双向多对一关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端(owner side), one 的一方作为被维护端(inverse side)。
可以在 one 方指定 @OneToMany 注释并设置 mappedBy 属性,以指定它是这一关联中的被维护端,many 为维护端。
在 many 方指定 @ManyToOne 注释,并使用 @JoinColumn 指定外键名称
【1】修改Order和Customer
Order:Customer = N:1。
Order如下:
@Table(name="JPA_ORDERS") @Entity public class Order { private Integer id; private String orderName; private Customer customer; @GeneratedValue @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="ORDER_NAME") public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } //映射单向 n-1的关联关系 //使用@ManyToOne来映射多对一的关联关系 //使用@JoinColumn 来映射外键 //可使用 @ManyToOne 的fetch 属性来修改默认的关联属性的加载策略 @JoinColumn(name="CUSTOMER_ID") // @ManyToOne(fetch=FetchType.LAZY) @ManyToOne(fetch=FetchType.EAGER) public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } }
Customer如下:
@NamedQuery(name="testNamedQuery", query="FROM Customer c WHERE c.id = ?") @Cacheable(true) @Table(name="JPA_CUTOMERS") @Entity public class Customer { private Integer id; private String lastName; private String email; private int age; private Date createdTime; private Date birth; public Customer() { } public Customer(String lastName, int age) { super(); this.lastName = lastName; this.age = age; } private Set<Order> orders = new HashSet<>(); // @TableGenerator(name="ID_GENERATOR", // table="jpa_id_generators", // pkColumnName="PK_NAME", // pkColumnValue="CUSTOMER_ID", // valueColumnName="PK_VALUE", // allocationSize=100) // @GeneratedValue(strategy=GenerationType.TABLE,generator="ID_GENERATOR") @GeneratedValue(strategy=GenerationType.AUTO) @Id public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name="LAST_NAME",length=50,nullable=false) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Temporal(TemporalType.TIMESTAMP) public Date getCreatedTime() { return createdTime; } public void setCreatedTime(Date createdTime) { this.createdTime = createdTime; } @Temporal(TemporalType.DATE) public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } //映射单向1-n的关联关系 //使用@OneToMany 映射单向1-n的关联关系 //使用@JoinColumn 来映射外键的名称 //可以使用@OneToMany 的 fetch 属性来修改加载策略 //可以使用@OneToMany 的 cascade 属性来修改默认的删除策略 @JoinColumn(name="CUSTOMER_ID") @OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE}) public Set<Order> getOrders() { return orders; } public void setOrders(Set<Order> orders) { this.orders = orders; } @Transient public String getInfo(){ return "lastName: " + lastName + ", email: " + email; } @Override public String toString() { return "Customer [id=" + id + ", lastName=" + lastName + ", email=" + email + ", age=" + age + ", createdTime=" + createdTime + ", birth=" + birth + "]"; } }
【2】双向多对一持久化操作
示例代码如下:
@Test public void testOneToManyPersist(){ Customer customer = new Customer(); customer.setAge(18); customer.setBirth(new Date()); customer.setCreatedTime(new Date()); customer.setEmail("mm@163.com"); customer.setLastName("MM"); Order order1 = new Order(); order1.setOrderName("O-MM-1"); Order order2 = new Order(); order2.setOrderName("O-MM-2"); //建立关联关系 customer.getOrders().add(order1); customer.getOrders().add(order2); order1.setCustomer(customer); order2.setCustomer(customer); //执行保存操作 entityManager.persist(customer); entityManager.persist(order1); entityManager.persist(order2); }
这里先保存了1的一端。
控制台输出如下:
Hibernate: insert into JPA_CUTOMERS (age, birth, createdTime, email, LAST_NAME) values (?, ?, ?, ?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: update JPA_ORDERS set CUSTOMER_ID=? where id=? Hibernate: update JPA_ORDERS set CUSTOMER_ID=? where id=?
如果先保存 n的一端呢,即先保存Order。
代码修改如下:
entityManager.persist(order1); entityManager.persist(order2); entityManager.persist(customer);
控制台输出如下:
Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_CUTOMERS (age, birth, createdTime, email, LAST_NAME) values (?, ?, ?, ?, ?) Hibernate: update JPA_ORDERS set CUSTOMER_ID=?, ORDER_NAME=? where id=? Hibernate: update JPA_ORDERS set CUSTOMER_ID=?, ORDER_NAME=? where id=? Hibernate: update JPA_ORDERS set CUSTOMER_ID=? where id=? Hibernate: update JPA_ORDERS set CUSTOMER_ID=? where id=?
综上可知:
若是双向 1-n 的关联关系, 执行保存时:
- 若先保存 n 的一端, 再保存 1 的一端, 默认情况下, 会多出2n 条 UPDATE 语句;
- 若先保存 1 的一端, 则会多出 n 条 UPDATE 语句。
故在进行双向 1-n 关联关系时, 建议使用 n 的一方来维护关联关系, 而 1 的一方不维护关联系, 这样会有效的减少 SQL 语句。
即,N的一端对应的表中使用外键关联1的一端,外键对应1的一端的表主键
【3】一的一端放弃维持关联关系
修改Customer实体类如下(放弃维持关联关系):
// @JoinColumn(name="CUSTOMER_ID") @OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE},mappedBy="customer") public Set<Order> getOrders() { return orders; }
注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了。
先保存N的一端时控制台输出如下:
Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_CUTOMERS (age, birth, createdTime, email, LAST_NAME) values (?, ?, ?, ?, ?) Hibernate: update JPA_ORDERS set CUSTOMER_ID=?, ORDER_NAME=? where id=? Hibernate: update JPA_ORDERS set CUSTOMER_ID=?, ORDER_NAME=? where id=?
先保存1 的一端时控制台输出如下:
Hibernate: insert into JPA_CUTOMERS (age, birth, createdTime, email, LAST_NAME) values (?, ?, ?, ?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?) Hibernate: insert into JPA_ORDERS (CUSTOMER_ID, ORDER_NAME) values (?, ?)
在一的一端放弃维持关联关系时,先插入n的一端,会多出n条update语句;先保存一的一端时只有n+1条insert语句!
综上,多对一持久化操作时,建议由一的一端放弃维持关联关系,多的一端维持关联关系。保存时先保存1的一端,这样减少SQL执行,提高效率。