映射单向一对多的关联关系
新建项目项目请参考《》,基于上一章讲解的《》中的例子进行修改(需要清空数据中的表,因为本例子还是使用customer,order表来测试,但是关联关系发生了变化):
Customer.java
package com.dx.jpa.singlemanytoone;import java.util.Date;import java.util.HashSet;import java.util.Set;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.JoinColumn;import javax.persistence.OneToMany;import javax.persistence.Table;import javax.persistence.Temporal;import javax.persistence.TemporalType;import javax.persistence.Transient;@Entity@Table(name = "jpa_customer")public class Customer { private Integer id; private String fullName; private Integer age; private Date birth; private Date createDate; private Setorders = new HashSet<>(); @Id @GeneratedValue(strategy = GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name = "FULL_NAME", length = 64, nullable = false) public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Temporal(TemporalType.DATE) public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } @Temporal(TemporalType.TIMESTAMP) public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } // 映射一对多的关联关系 // @JoinColumn 用来映射一对多的关联关系 // @OneToMany 用来映射外键列 @JoinColumn(name = "CUSTOMER_ID") @OneToMany() public Set getOrders() { return orders; } public void setOrders(Set orders) { this.orders = orders; } // 帮助方法,不希望保存到数据库,但是需要动态获取Customer对象的属性。 @Transient public String getCustomerInfo() { return "username:" + fullName + ",age:" + age; }}
注意:这里在Customer.java中添加了一对多的注解@OneToMany:
// 映射一对多的关联关系 // @JoinColumn 用来映射一对多的关联关系 // @OneToMany 用来映射外键列 @JoinColumn(name = "CUSTOMER_ID") @OneToMany() public SetgetOrders() { return orders; }
Order.java
package com.dx.jpa.singlemanytoone;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;@Entity@Table(name = "jpa_order")public class Order { private Integer id; private String name; @Id @GeneratedValue(strategy = GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}
初始化JPA项目时,创建表语句如下:
Hibernate: create table hibernate_sequence ( next_val bigint ) engine=InnoDBHibernate: insert into hibernate_sequence values ( 1 )Hibernate: insert into hibernate_sequence values ( 1 )Hibernate: create table jpa_customer ( id integer not null, age integer, birth date, createDate datetime, FULL_NAME varchar(64) not null, primary key (id) ) engine=InnoDBHibernate: create table jpa_order ( id integer not null, name varchar(255), CUSTOMER_ID integer, primary key (id) ) engine=InnoDBHibernate: alter table jpa_order add constraint FK7glkngwj74nr8h2amofkp1fjd foreign key (CUSTOMER_ID) references jpa_customer (id)
persistence.xml
org.hibernate.jpa.HibernatePersistenceProvider com.dx.jpa.singlemanytoone.Customer com.dx.jpa.singlemanytoone.Order
此时在order表中创建了外键关联关系:
添加测试
添加测试函数:
@Test public void testPersist() { Customer customer = new Customer(); customer.setFullName("AA"); customer.setAge(26); customer.setBirth(new Date()); customer.setCreateDate(new Date()); Order order1 = new Order(); order1.setName("O-AA-01"); Order order2 = new Order(); order2.setName("O-AA-02"); customer.getOrders().add(order1); customer.getOrders().add(order2); entityManager.persist(customer); entityManager.persist(order1); entityManager.persist(order2); }
此时执行sql如下:
Hibernate: select next_val as id_val from hibernate_sequence for update Hibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: select next_val as id_val from hibernate_sequence for update Hibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: select next_val as id_val from hibernate_sequence for update Hibernate: update hibernate_sequence set next_val= ? where next_val=?Hibernate: insert into jpa_customer (age, birth, createDate, FULL_NAME, id) values (?, ?, ?, ?, ?)Hibernate: insert into jpa_order (name, id) values (?, ?)Hibernate: insert into jpa_order (name, id) values (?, ?)Hibernate: update jpa_order set CUSTOMER_ID=? where id=?Hibernate: update jpa_order set CUSTOMER_ID=? where id=?
此时并不是因为customer,order保存顺序导致的(即使调换添加先后顺序也会多处update语句),因为多的一端在插入时不会插入外键列,因此一定会多处update语句。
查询测试
添加测试函数:
@Test public void testFind() { Customer customer = entityManager.find(Customer.class, 1); System.out.println(customer.getFullName()); System.out.println(customer.getOrders().size()); }
执行打印结果:
Hibernate: select customer0_.id as id1_0_0_, customer0_.age as age2_0_0_, customer0_.birth as birth3_0_0_, customer0_.createDate as createDa4_0_0_, customer0_.FULL_NAME as FULL_NAM5_0_0_ from jpa_customer customer0_ where customer0_.id=?AAHibernate: select orders0_.CUSTOMER_ID as CUSTOMER3_1_0_, orders0_.id as id1_1_0_, orders0_.id as id1_1_1_, orders0_.name as name2_1_1_ from jpa_order orders0_ where orders0_.CUSTOMER_ID=?2
从打印结果上可以看出默认采用懒加载的方式,修改Customer.java中的@OneToMany()中的fetch属性为:@OneToMany(fetch=FetchType.EAGER),此时才会出现非懒加载:
此时,再次执行插叙测试函数,执行打印结果如下:
Hibernate: select customer0_.id as id1_0_0_, customer0_.age as age2_0_0_, customer0_.birth as birth3_0_0_, customer0_.createDate as createDa4_0_0_, customer0_.FULL_NAME as FULL_NAM5_0_0_, orders1_.CUSTOMER_ID as CUSTOMER3_1_1_, orders1_.id as id1_1_1_, orders1_.id as id1_1_2_, orders1_.name as name2_1_2_ from jpa_customer customer0_ left outer join jpa_order orders1_ on customer0_.id=orders1_.CUSTOMER_ID where customer0_.id=?AA2
修改测试
修改测试函数:
@Test public void testUpdate() { Customer customer = entityManager.find(Customer.class, 1); customer.getOrders().iterator().next().setName("O-XX-01"); }
此时执行打印结果为:
Hibernate: select customer0_.id as id1_0_0_, customer0_.age as age2_0_0_, customer0_.birth as birth3_0_0_, customer0_.createDate as createDa4_0_0_, customer0_.FULL_NAME as FULL_NAM5_0_0_, orders1_.CUSTOMER_ID as CUSTOMER3_1_1_, orders1_.id as id1_1_1_, orders1_.id as id1_1_2_, orders1_.name as name2_1_2_ from jpa_customer customer0_ left outer join jpa_order orders1_ on customer0_.id=orders1_.CUSTOMER_ID where customer0_.id=?Hibernate: update jpa_order set name=? where
注意:这时@OneToMany(fetch=FetchType.EAGER)
删除测试
删除测试函数:
@Test public void testRemove() { Customer customer = entityManager.find(Customer.class, 4); entityManager.remove(customer); }
此时customer表内容记录如下:
order表记录如下:
此时执行删除,可以删除成功,删除后customer.id=4的记录被删除了,而order表中id=5,6记录的customer_id值被置为null。
删除后结果为:
customer表记录:
order表记录:
从执行打印语句可以看出:
Hibernate: select customer0_.id as id1_0_0_, customer0_.age as age2_0_0_, customer0_.birth as birth3_0_0_, customer0_.createDate as createDa4_0_0_, customer0_.FULL_NAME as FULL_NAM5_0_0_, orders1_.CUSTOMER_ID as CUSTOMER3_1_1_, orders1_.id as id1_1_1_, orders1_.id as id1_1_2_, orders1_.name as name2_1_2_ from jpa_customer customer0_ left outer join jpa_order orders1_ on customer0_.id=orders1_.CUSTOMER_ID where customer0_.id=?Hibernate: update jpa_order set CUSTOMER_ID=null where CUSTOMER_ID=?Hibernate: delete from jpa_customer where id=?
实际上,我们可以通过配置@OneToMany的级联删除属性,可以通过删除customer来实现级联删除的。
修改Customer.java中的@OneToMany注解信息:
这里修改配置后Customer的getOrders()方法的注解为:
// 映射一对多的关联关系 // @JoinColumn 用来映射一对多的关联关系 // @OneToMany 用来映射外键列 @JoinColumn(name = "CUSTOMER_ID") @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.REMOVE) public SetgetOrders() { return orders; }
此时,测试通过删除customer.id=1的记录,测试结果可以成功级联删除,执行打印结果为:
Hibernate: select customer0_.id as id1_0_0_, customer0_.age as age2_0_0_, customer0_.birth as birth3_0_0_, customer0_.createDate as createDa4_0_0_, customer0_.FULL_NAME as FULL_NAM5_0_0_, orders1_.CUSTOMER_ID as CUSTOMER3_1_1_, orders1_.id as id1_1_1_, orders1_.id as id1_1_2_, orders1_.name as name2_1_2_ from jpa_customer customer0_ left outer join jpa_order orders1_ on customer0_.id=orders1_.CUSTOMER_ID where customer0_.id=?Hibernate: update jpa_order set CUSTOMER_ID=null where CUSTOMER_ID=?Hibernate: delete from jpa_order where id=?Hibernate: delete from jpa_order where id=?Hibernate: delete from jpa_customer where id=?