1 双向一多
1.1 概念
双向一对多关系中,必须存在一个关系维护端,在 JPA 规范中,要求 many 的一方作为关系的维护端(ownerside), one 的一方作为被维护端(inverse side)。
可以在 one 方指定@OneToMany 注释并设置mappedBy 属性,以指定它是这一关联中的被维护端,many 为维护端。
1.2 配置
一个订单只有一个用户,一个用户多个订单
使用 @ManyToOne 来映射多对一的关联关系, fetch 属性来修改默认的关联属性的加载策略
使用 @JoinColumn 来映射外键.
@JoinColumn(name="CUSTOMER_ID")
@OneToMany
@JoinColumn(name="CUSTOMER_ID")
@ManyToOne(fetch=FetchType.LAZY)
1.3 数据生成信息
Order表会产生外键
1.4 测试准备
1.4.1 Customer
@Table(name="JPA_CUTOMERS")
@Entity
public classCustomer {
private Integer id;
private String lastName;
private String email;
private int age;
private Date createdTime;
private Date birth;
private Set<Order> orders = new HashSet<>();
@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 的关联关系
// 1.使用 @OneToMany 来映射 1-n 的关联关系;使用 @JoinColumn 来映射外键列的名称
// 2.可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略
// 3.可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略.
// @JoinColumn(name="CUSTOMER_ID")
// @OneToMany
// @OneToMany(fetch=FetchType.EAGER)
// @OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE})
@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE},mappedBy="customer")
public Set<Order>getOrders() {
return orders;
}
public voidsetOrders(Set<Order> orders) {
this.orders = orders;
}
}
1.4.2 Order
@Table(name="JPA_ORDERS")
@Entity
public classOrder {
private Integer id;
private String orderName;
@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;
}
}
1.4.3 准备JPATest
public classJPATest {
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
private EntityTransaction transaction;
@Before
public void init(){
entityManagerFactory= Persistence.createEntityManagerFactory("jpa_test1");
entityManager = entityManagerFactory.createEntityManager();
transaction = entityManager.getTransaction();
transaction.begin();
}
@After
public void destroy(){
transaction.commit();
entityManager.close();
entityManagerFactory.close();
}
}
2 操作
2.1 保存(双向维护)
//若是双向 1-n 的关联关系, 执行保存时
//1.若先保存 n 的一端, 再保存 1 的一端, 默认情况下, 会多出 n 条 UPDATE 语句.
//2.若先保存 1 的一端,再保存 n 的一端, 则会多出 n*2 条 UPDATE 语句
//3.在进行双向 1-n 关联关系时, 建议使用 n 的一方来维护关联关系, 而 1 的一方不维护关联系, 这样会有效的减少 SQL 语句.
//注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了.
2.1.1 先一再n
//若是双向 1-n 的关联关系, 执行保存时
//1.若先保存 n 的一端, 再保存 1 的一端, 默认情况下, 会多出 n 条 UPDATE 语句.
// 1.1 先插入customer,得到id,此时customer_id有值,插入order
// 1.2 只需要维护多方外键关联关系,
@Test
public voidtestOneToManyPersist1(){//先n再一
Customercustomer= newCustomer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("mm@163.com");
customer.setLastName("MM");
Orderorder1= newOrder();
order1.setOrderName("O-MM-1");
Orderorder2= newOrder();
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);
}
2.1.2 先n再一
//2.若先保存 1 的一端,再保存 n 的一端, 则会多出 n方*2 条 UPDATE 语句
// 2.1 先插入order,此时customer_id为空,再插入customer,得到id
// 2.2先维护多方外键关联关系,
// 2.3由于双向维护,再维护一方外键关系,实际是不需要的
@Test
public voidtestOneToManyPersist2(){//先n再一
Customercustomer= newCustomer();
customer.setAge(18);
customer.setBirth(new Date());
customer.setCreatedTime(new Date());
customer.setEmail("mm@163.com");
customer.setLastName("MM");
Orderorder1= newOrder();
order1.setOrderName("O-MM-1");
Orderorder2= newOrder();
order2.setOrderName("O-MM-2");
//建立关联关系
customer.getOrders().add(order1);
customer.getOrders().add(order2);
order1.setCustomer(customer);
order2.setCustomer(customer);
//执行保存操作
entityManager.persist(order1);
entityManager.persist(order2);
entityManager.persist(customer);
}
2.1.3 一方放弃维护,只维护多方(推荐)
//3.在进行双向 1-n 关联关系时, 建议使用 n 的一方来维护关联关系, 而 1 的一方不维护关联系, 这样会有效的减少 SQL 语句.
// 注意: 若在 1 的一端的 @OneToMany 中使用 mappedBy 属性, 则 @OneToMany 端就不能再使用 @JoinColumn 属性了.
//@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE},mappedBy="customer")
2.2 查询(oid)
2.2.1 默认懒加载
//默认对关联的多的一方使用懒加载的加载策略.
//可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略
@Test
public void testOneToManyFind(){
Customercustomer= entityManager.find(Customer.class, 3);
System.out.println(customer.getLastName());
System.out.println(customer.getOrders().size());
}
2.2.2 正常加载(EAGER)
修改默认的加载策略
2.3 删除(一方)
2.3.1 外键置空,只删一方
//默认情况下, 若删除 1 的一端, 则会先把关联的 n 的一端的外键置空, 然后进行删除.
//可以通过 @OneToMany 的 cascade 属性来修改默认的删除策略.
@Test
public void testOneToManyRemove(){
Customercustomer= entityManager.find(Customer.class, 8);
entityManager.remove(customer);
}
2.3.2 外键置空,都删
//可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略
@OneToMany(fetch=FetchType.EAGER,cascade={CascadeType.REMOVE})
2.4 更新
只要是针对一方,多方就直接单表了
@Test
public void testUpdate(){
Customercustomer= entityManager.find(Customer.class, 3);
customer.getOrders().iterator().next().setOrderName("O-XXX-10");
}
源码:https://download.csdn.net/download/qq_26553781/10320460
//默认对关联的多的一方使用懒加载的加载策略.
//可以使用 @OneToMany 的 fetch 属性来修改默认的加载策略
@Test
public void testOneToManyFind(){
Customercustomer= entityManager.find(Customer.class, 3);
System.out.println(customer.getLastName());
System.out.println(customer.getOrders().size());
}