Jpa:关系(二)

    技术2025-07-18  10

    文章目录

    映射关系1.单向多对一 @ManyToOne2 单向一对多 @OneToMany3 双向多对一4 双向一对一5 双向多对多 @ManyToMany

    映射关系

    1.单向多对一 @ManyToOne

    @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_person") public class Person { @Id @GeneratedValue private Integer pid; private String pname; } @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_book") public class Book { @Id @GeneratedValue private Integer bid; private String bname; //映射单向 n-1 的关联关系 //使用 @ManyToOne 来映射多对一的关联关系 //使用 @JoinColumn 来映射外键. //可使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略 @JoinColumn(name="p_id") @ManyToOne(fetch=FetchType.LAZY) private Person person; } /** * 保存多对一时, 建议先保存 1 的一端, 后保存 n 的一端, 这样不会多出额外的 UPDATE 语句. */ @Test public void testManyToOnePersist(){ Person person = new Person(); person.setPname("mmm"); Book book = new Book(); book.setBname("book"); Book book1 = new Book(); book.setBname("book1"); //设置关联关系 book.setPerson(person); book1.setPerson(person); //执行保存操作 entityManager.persist(person); entityManager.persist(book); entityManager.persist(book1); }

    //不能直接删除 1 的一端, 因为有外键约束. @Test public void testManyToOneRemove(){ Person person = entityManager.find(Person.class, 8); //删除主表,子表还有记录,则会报错 entityManager.remove(person); }

    2 单向一对多 @OneToMany

    @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_person") public class Person { @Id @GeneratedValue private Integer pid; private String pname; //映射单向 1-n 的关联关系 //使用 @OneToMany 来映射 1-n 的关联关系 //使用 @JoinColumn 来映射外键列的名称 //可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略 //可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略. //注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了. @JoinColumn(name="p_id") @OneToMany(fetch=FetchType.LAZY) private Set<Book> books = new HashSet<>(); } @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_book") public class Book { @Id @GeneratedValue private Integer bid; private String bname; } //单向 1-n 关联关系执行保存时, 一定会多出 UPDATE 语句. //因为 n 的一端在插入时不会同时插入外键列. @Test public void testOneToManyPersist(){ Person person = new Person(); person.setPname("onetomany"); Book book = new Book(); book.setBname("book"); Book book1 = new Book(); book1.setBname("book1"); //设置关联关系 person.getBooks().add(book); person.getBooks().add(book1); //执行保存操作 entityManager.persist(person); entityManager.persist(book); entityManager.persist(book1); }

    3 双向多对一

    @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_person") public class Person { @Id @GeneratedValue private Integer pid; private String pname; //映射单向 1-n 的关联关系 //使用 @OneToMany 来映射 1-n 的关联关系 //使用 @JoinColumn 来映射外键列的名称 //可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略 //可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略. //注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了. @JoinColumn(name="p_id") @OneToMany(fetch=FetchType.LAZY) private Set<Book> books = new HashSet<>(); } @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_book") public class Book { @Id @GeneratedValue private Integer bid; private String bname; //映射单向 n-1 的关联关系 //使用 @ManyToOne 来映射多对一的关联关系 //使用 @JoinColumn 来映射外键. //可使用 @ManyToOne 的 fetch 属性来修改默认的关联属性的加载策略 @JoinColumn(name="p_id") @ManyToOne(fetch=FetchType.LAZY) private Person person; } //若是双向 1-n 的关联关系, 执行保存时 //若先保存 n 的一端, 再保存 1 的一端, 默认情况下, 会多出 n 条 UPDATE 语句. //若先保存 1 的一端, 则会多出 n 条 UPDATE 语句 //在进行双向 1-n 关联关系时, 建议使用 n 的一方来维护关联关系, 而 1 的一方不维护关联系, 这样会有效的减少 SQL 语句. //注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了. @Test public void testOneToManyPersist(){ Person person = new Person(); person.setPname("onetomany"); Book book = new Book(); book.setBname("book"); Book book1 = new Book(); book1.setBname("book1"); //设置关联关系 person.getBooks().add(book); person.getBooks().add(book1); book.setPerson(person); book1.setPerson(person); //执行保存操作(两种顺序,sql执行的数量就不一样) //sql执行13条 entityManager.persist(book); entityManager.persist(book1); entityManager.persist(person); //sql执行11条 entityManager.persist(person); entityManager.persist(book); entityManager.persist(book1); }

    4 双向一对一

    @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_person") public class Person { @Id @GeneratedValue private Integer pid; private String pname; //对于不维护关联关系, 没有外键的一方, 使用 @OneToOne 来进行映射, 建议设置 mappedBy=true @OneToOne(mappedBy="person") private Book book; } @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_book") public class Book { @Id @GeneratedValue private Integer bid; private String bname; //使用 @OneToOne 来映射 1-1 关联关系。 //若需要在当前数据表中添加主键则需要使用 @JoinColumn 来进行映射. 注意, 1-1 关联关系, 所以需要添加 unique=true @JoinColumn(name="p_id", unique=true) @OneToOne(fetch=FetchType.LAZY) private Person person; } //双向 1-1 的关联关系, 建议先保存不维护关联关系的一方, 即没有外键的一方, 这样不会多出 UPDATE 语句. @Test public void testOneToOnePersistence(){ Person person = new Person(); person.setPname("one"); Book book = new Book(); book.setBname("toone"); //设置关联关系 person.setBook(book); book.setPerson(person); //执行保存操作 entityManager.persist(person); entityManager.persist(book); }

    外键在book表里维护

    5 双向多对多 @ManyToMany

    @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_person") public class Person { @Id @GeneratedValue private Integer pid; private String pname; @ManyToMany(mappedBy="persons") private Set<Book> books = new HashSet<>(); } @AllArgsConstructor @NoArgsConstructor @Getter @Setter @ToString @Entity @Table(name="tb_book") public class Book { @Id @GeneratedValue private Integer bid; private String bname; //使用 @ManyToMany 注解来映射多对多关联关系 //使用 @JoinTable 来映射中间表 //1. name 指向中间表的名字 //2. joinColumns 映射当前类所在的表在中间表中的外键 //2.1 name 指定外键列的列名 //2.2 referencedColumnName 指定外键列关联当前表的哪一列 //3. inverseJoinColumns 映射关联的类所在中间表的外键 @JoinTable(name="person_book", joinColumns={@JoinColumn(name="book_id", referencedColumnName="bid")}, inverseJoinColumns={@JoinColumn(name="person_id", referencedColumnName="pid")}) @ManyToMany private Set<Person> persons = new HashSet<>(); } //多对多的保存 @Test public void testManyToManyPersist(){ Person person = new Person(); person.setPname("P1"); Person person2 = new Person(); person2.setPname("P2"); Book book = new Book(); book.setBname("B1"); Book book2 = new Book(); book2.setBname("B2"); //设置关联关系 person.getBooks().add(book); person.getBooks().add(book2); person2.getBooks().add(book); person2.getBooks().add(book2); book.getPersons().add(person); book.getPersons().add(person2); book2.getPersons().add(person); book2.getPersons().add(person2); //执行保存 entityManager.persist(person); entityManager.persist(person2); entityManager.persist(book); entityManager.persist(book2); }

    多对多,会产生一个中间表

    Processed: 0.011, SQL: 9