jpa中的表关系以及多表CRUD操作

文章目录

    • 一对多的关系映射
      • 第一步 明确两张表的关系
      • 第二步 在数据库中建立两张表的关系
      • 第三步 在实体类中建立两个实体间的关系
      • 第四步 关联关系维护的放弃
      • 实体类
      • 一对多的增删
      • 级联删除
    • 多对多
      • 保存操作
      • 删除操作
    • 对象导航查询的使用
      • 什么是对象导航查询:

一对多的关系映射

第一步 明确两张表的关系

一个客户可以对应多个联系人
多个联系人可以属于同一个客户

第二步 在数据库中建立两张表的关系

使用外键。
在联系人表中添加一列,这一列的值来源于 客户表的主键,添加的这列
是外键。

第三步 在实体类中建立两个实体间的关系

从表:
多对一的关系映射:多个联系人对应一个客户
所以,从表实体包含主表的对象引用

private Customer customer;

主表:
1.一对多关系映射:一个客户对应多个联系人
主表中包含从表实体的引用集合

private Set<LinkMan> linKmans = new HashSet(0);

第四步 关联关系维护的放弃

主表:

@OneToMany(targetEntity=LinkMan.class,mappedBy=”customer”)
Private Set<LinkMan> linkmans = new HashSet(0);

关联关系的维护:
在多表关系中,有一个很重要的概念叫做关联关系维护,也就是你中有我,我中有你,当一方数据有变动时会想着让对方也做相关的更新,但是这个事如果二者都去考虑操作比较不合理,那么我干脆让一方去维护这个事情,让谁呢? 让多的一方,也就是从表,因为他只需要维护一个对象 客户。
mappedBy: 关联关系维护,这个属性指向谁,就让谁放弃维护关联关系,如果不加,则会出现第三张表
从表:

@ManyToOne(targetEntity=Customer.class)
@JoinColumn(name=”lkm_cust_id”,referencedColumnName=”cust_id”)//增加一列外键列

实体类

主表类

package domain;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
* 

Title: Customer

*

Description: 客户的实体类

* @author zhaocq * @date 2018年9月19日 */
@Entity @Table(name="cst_customer") public class Customer implements Serializable { @Id @Column(name="cust_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer custId; @Column(name="cust_name") private String custName; @Column(name="cust_source") private String custSource; @Column(name="cust_industry") private String custIndustry; @Column(name="cust_level") private String custLevel; @Column(name="cust_address") private String custAddress; @Column(name="cust_phone") private String custPhone; // 一对多(客户表中要包含一个联系集合) @OneToMany(targetEntity=LinkMan.class,mappedBy="customer",cascade=CascadeType.REMOVE,fetch=FetchType.EAGER) private Set<LinkMan> linkMams = new HashSet(0); public Set<LinkMan> getLinkMams() { return linkMams; } public void setLinkMams(Set<LinkMan> linkMams) { this.linkMams = linkMams; } public Integer getCustId() { return custId; } public void setCustId(Integer custId) { this.custId = custId; } public String getCustName() { return custName; } public void setCustName(String custName) { this.custName = custName; } public String getCustSource() { return custSource; } public void setCustSource(String custSource) { this.custSource = custSource; } public String getCustIndustry() { return custIndustry; } public void setCustIndustry(String custIndustry) { this.custIndustry = custIndustry; } public String getCustLevel() { return custLevel; } public void setCustLevel(String custLevel) { this.custLevel = custLevel; } public String getCustAddress() { return custAddress; } public void setCustAddress(String custAddress) { this.custAddress = custAddress; } public String getCustPhone() { return custPhone; } public void setCustPhone(String custPhone) { this.custPhone = custPhone; } @Override public String toString() { return "Customer [custId=" + custId + ", custName=" + custName + ", custSource=" + custSource + ", custIndustry=" + custIndustry + ", custLevel=" + custLevel + ", custAddress=" + custAddress + ", custPhone=" + custPhone + "]"; } }

从表类

package domain;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

/**
* 

Title: LinkMan

*

Description: 联系人实体类

* @author zhaocq * @date 2018年9月19日 */
@Entity @Table(name="cst_linkman") public class LinkMan implements Serializable { @Id @Column(name="lkm_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer lkmId; @Column(name="lkm_name") private String lkmName; @Column(name="lkm_gender") private String lkmGender; @Column(name="lkm_phone") private String lkmPhone; @Column(name="lkm_mobile") private String lkmMobile; @Column(name="lkm_email") private String lkmEmail; @Column(name="lkm_position") private String lkmPosition; @Column(name="lkm_memo") private String lkmMemo; // 多对一(联系人中包含客户对象的引用) @ManyToOne(targetEntity=Customer.class,fetch=FetchType.LAZY) @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id") private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Integer getLkmId() { return lkmId; } public void setLkmId(Integer lkmId) { this.lkmId = lkmId; } public String getLkmName() { return lkmName; } public void setLkmName(String lkmName) { this.lkmName = lkmName; } public String getLkmGender() { return lkmGender; } public void setLkmGender(String lkmGender) { this.lkmGender = lkmGender; } public String getLkmPhone() { return lkmPhone; } public void setLkmPhone(String lkmPhone) { this.lkmPhone = lkmPhone; } public String getLkmMobile() { return lkmMobile; } public void setLkmMobile(String lkmMobile) { this.lkmMobile = lkmMobile; } public String getLkmEmail() { return lkmEmail; } public void setLkmEmail(String lkmEmail) { this.lkmEmail = lkmEmail; } public String getLkmPosition() { return lkmPosition; } public void setLkmPosition(String lkmPosition) { this.lkmPosition = lkmPosition; } public String getLkmMemo() { return lkmMemo; } public void setLkmMemo(String lkmMemo) { this.lkmMemo = lkmMemo; } @Override public String toString() { return "LinkMan [lkmId=" + lkmId + ", lkmName=" + lkmName + ", lkmGender=" + lkmGender + ", lkmPhone=" + lkmPhone + ", lkmMobile=" + lkmMobile + ", lkmEmail=" + lkmEmail + ", lkmPosition=" + lkmPosition + ", lkmMemo=" + lkmMemo + "]"; } }

一对多的增删

package com.itheima.test;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.junit.Test;

import domain.Customer;
import domain.LinkMan;
import utils.JpaUtil;

public class OTMTest {
	
	//增
	@Test
	public void test() {
		EntityManager em = JpaUtil.createEntityManager();
		Customer customer = new Customer();
		customer.setCustName("客户1");
		LinkMan linkMan = new LinkMan();
		linkMan.setLkmName("联系人1");
		customer.getLinkMams().add(linkMan);
		linkMan.setCustomer(customer);
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		em.persist(customer);
		em.persist(linkMan);
		tx.commit();
	}
	
	/**
	 * 级联删除
	 * 
	 *	从表:随便删
	 *    主表:
	 *       是否有引用:
	 *          有不让删
	 *          没有可以删
	 */
	@Test
	public void test1(){
		EntityManager em = JpaUtil.createEntityManager();
		EntityTransaction tx = em.getTransaction();
		tx.begin();
		
		Customer c = em.find(Customer.class, 1);
		em.remove(c);
		tx.commit();
		em.close();
	}
}

在增加的时候,要有一定的顺序,应该先添加主表对象,再增加从表对象
但是当我们先添加从表对象,再添加主表对象的时候,执行也能成功,但是会出现一句update语句,这是为什么呢?

这是由于mappedBy="customer"的作用,当我们为主表放弃维护关系,从表为维护关系的一方。此时如果先添加从表对象,通过debug可以看到,保存从表对象的时候,customer属性为null。而当我们在保存主表对象的时候,customer属性又出现了,这也就导致了缓存区与快照区的差异,缓存区有customer属性但是快照区没有,根据快照的特性,在我们提交的时候就会产生update语句来同步缓存区与快照区

而当我们先添加主表对象的时候,由于主表为放弃维护关系的一方,所以是忽略LinkMan集合为null。所以不会产生缓存区与快照区之间的差异,因此不会产生update语句

级联删除

上述删除操作就是级联删除,如果不配置级联删除,对于存在外间关系的主表对象,不能直接删除
要在@OneToMany中配置cascade=CascadeType.REMOVE进行级联删除

多对多

保存操作

按照需求实现功能,没有问题!
这是时候 将 mappedBy 属性去掉,就会发现,多出一张中间表:
分析原因:因为没有配置 关联关系维护 放弃的对象,所以两个对象都会进行关联关系维护所以各自产生一张表。

删除操作

结论:不能双向级联删除,由于多对多表之间有引用,一旦级联删除,会把所有有关联的数据全部干掉。

对象导航查询的使用

什么是对象导航查询:

当我们有一个实体对象时,根据该对象中的方法获取关联对象的数据。
要求:两个对象两个实体之间必须要有关联关系(四种关联关系)
出现的问题
1.为什么查询客户后再利用导航查询获取联系人的时候会出现两条SQL语句?
2.相反查询联系人后再用导航查询获取客户信息的时候会出现一条SQL语句?
分析原因:
考虑一个问题,当我们获取客户后要不要同时获取它下面的所有联系人?同样当获取联系人时要不要获取它所属的客户?这里就谈到一个加载策略的问题,当我们从一对多的角度考虑,没必要立刻获取多的一方的所有信息,我们需要什么就去加载什么就可以了,因为如果多的一方有可能会有很多数据,如果全部加载,必然会消耗巨大的内存。而从多对一的较多来考虑,当联系人信息查询之后,随之它所引用的客户信息也会立即加载,因为客户一方只有一条数据,不会造成太大的内存消耗,另外一般从表中的信息的完整性肯定要要主表信息的支持。

小结: 通过以上的分析,可以确定这是由jpa底层的加载策略所造成,它默认情况就这以上的效果,但是我们可以通过配置来控制加载策略。配置属性都是在四种关系的注解中加 fetch 属性,属性值EAGER:立即加载 LAZY:按需加载。

package com.itheima.test;

import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.junit.Test;

import domain.Customer;
import domain.LinkMan;
import utils.JpaUtil;



/**
* 

Title: TestQuery

*

Description: 对象导航查询

* @author zhaocq * @date 2018年9月19日 * 对象导航查询: * 当获取一个对象,然后根据这个对象中提供的方法对关联信息进行查询。 * * 分析: * 以下输出结果是 当通过主表获取从表信息的时候用的是懒加载,当通过从表获取主表信息的时候是立即加载,jpa中默认情况就是这样的。 * 但是我们可以通过配置改变加载时机,在关系注解当中配置 fecth LAZY EAGER */
public class TestQuery { /** *

Title: test

*

Description: 对象导航查询1

* 现获取客户信息,然后再通过对象导航查询获取联系人信息 */
@Test public void test(){ EntityManager em = JpaUtil.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); Customer c = em.find(Customer.class, 1); Set<LinkMan> linkMams = c.getLinkMams(); for(LinkMan l:linkMams){ System.out.println(l); } } /** *

Title: test1

*

Description:对象导航查询2

* 现获取联系人信息,然后通过导航查询获取客户信息 */
@Test public void test1(){ EntityManager em = JpaUtil.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); LinkMan l = em.find(LinkMan.class, 1); Customer c = l.getCustomer(); System.out.println(c); } }

你可能感兴趣的:(jpa中的表关系以及多表CRUD操作)