1、一对多实体类的配置
我们采用的示例为客户和联系人。
客户:指的是一家公司,我们记为 A。
联系人:指的是 A 公司中的员工。
在不考虑兼职的情况下,公司和员工的关系即为一对多。
/**
* 客户的实体类
* 明确使用的注解都是 JPA 规范的
* 所以导包都要导入 javax.persistence 包下的
*/
@Entity // 声明实体类
@Table(name = "cst_customer") // 建立实体类和表的映射关系
public class Customer {
@Id // 声明当前私有属性为主键
@GeneratedValue(strategy = GenerationType.IDENTITY) // 配置主键的生成策略
@Column(name = "cust_id") // 指定和表中 cust_id 字段的映射关系
private Long custId;
@Column(name = "cust_name") // 指定和表中 cust_name 字段的映射关系
private String custName;
@Column(name = "cust_source") // 指定和表中 cust_source 字段的映射关系
private String custSource;
@Column(name = "cust_industry") // 指定和表中 cust_industry 字段的映射关系
private String custIndustry;
@Column(name = "cust_level") // 指定和表中 cust_level 字段的映射关系
private String custLevel;
@Column(name = "cust_address") // 指定和表中 cust_address 字段的映射关系
private String custAddress;
@Column(name = "cust_phone") // 指定和表中 cust_phone 字段的映射关系
private String custPhone;
//配置客户和联系人的一对多关系
@OneToMany(targetEntity = LinkMan.class, mappedBy = "customer", cascade = CascadeType.PERSIST)
private Set<LinkMan> linkMans = new HashSet<>(0);
}
由于联系人是多的一方,在实体类中要体现出,每个联系人只能对应一个客户,代码如下:
/**
* 联系人的实体类(数据模型)
*/
@Entity
@Table(name="cst_linkman")
public class LinkMan {
@Id
@Column(name="lkm_id")
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long 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,cascade=CascadeType.PERSIST)
@JoinColumn(name="cust_id",referencedColumnName="cust_id")
private Customer customer;
}
2、映射的注解说明
- @OneToMany:
作用:
建立一对多的关系映射
属性:
targetEntityClass:指定多的多方的类的字节码
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
mappedBy:指定从表实体类中引用主表对象的名称。
orphanRemoval:是否使用孤儿删除
- @ManyToOne
作用:
建立多对一的关系
属性:
targetEntityClass:指定一的一方实体类字节码
cascade:指定要使用的级联操作
fetch:指定是否采用延迟加载
- @JoinColumn
作用:
用于定义主键字段和外键字段的对应关系。
属性:
name:指定外键字段的名称
referencedColumnName:指定引用主表的主键字段名称
unique:是否唯一。默认值不唯一
nullable:是否允许为空。默认值允许。
insertable:是否允许插入。默认值允许。
updatable:是否允许更新。默认值允许。
columnDefinition:列的定义信息。
3、 一对多的操作
/**
* 1、保存操作
* 需求:
* 保存一个客户和一个联系人
* 要求:
* 创建一个客户对象和一个联系人对象
* 建立客户和联系人之间关联关系(双向一对多的关联关系)
* 先保存客户,再保存联系人
*
*/
@Test
public void testSave() {
// 创建客户和联系人对象
Customer c = new Customer();// 瞬时态
c.setCustName("百度云");
c.setCustLevel("VIP 客户");
c.setCustSource("网络");
c.setCustIndustry("商业办公");
c.setCustAddress("北京");
c.setCustPhone("010-84389340");
LinkMan l = new LinkMan();// 瞬时态
l.setLkmName("百度云 联系人");
l.setLkmGender("male");
l.setLkmMobile("13811111111");
l.setLkmPhone("010-34785348");
l.setLkmEmail("98354834@qq.com");
l.setLkmPosition("老师");
l.setLkmMemo("还行吧");
// 建立他们的双向一对多关联关系
l.setCustomer(c);
c.getLinkMans().add(l);
EntityManager em = JpaUtil.getEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// 按照要求:先保存客户,再保存联系人(此时符合保存原则:先保存主表,再保存从表)
em.persist(c);// 如果在把客户对象转成持久态时,不考虑联系人的信息。就不会有联系人的快照产生
em.persist(l);
tx.commit();// 默认此时会执行快照机制,当发现一级缓存和快照不一致了,使用一级缓存更新数据库。
}
通过保存的案例,我们可以发现在设置了双向关系之后,会发送两条 insert 语句,一条多余的 update 语句,那我们的解决是思路很简单,就是一的一方放弃维护权
/**
* 放弃外键维护权的配置将如下配置改为 :mappedBy="customer"
*/
@OneToMany(mappedBy="customer",targetEntity=LinkMan.class
/**
* 3、删除操作
*
* 删除从表实体:直接删
* 删除主表实体:
* 先看看从表实体是否有引用
* 有:
* 第一:解除引用关系,把从表实体的外键字段置为null。要求外键字段允许为null。
* 如果数据之间有父子关系,那么如果子数据没有引用的父数据,子数据就应该删除,所以在解除关系时,子数据就该被删除
* 例如:用户和订单
* 解除关系调用的是update方法,把外键字段给置为null,实现的是删除功能。
* 孤儿删除:是用update方法更新外键字段为null,干的是delete的事。
*
* 如果数据没有父子关系,那么就可以把子数据的外键字段置为null。
* 例如:部门和员工
* 第二:使用级联删除
* 删除客户的同时,把所有引用的联系人一起删除。
* 调用的删除方法,实现删除客户同时删除联系人
* 没有: 单表操作,直接删
* 在实际开发中,级联删除请慎用!(在一对多的情况下)
*/
@Test
public void testRemove() {
// 定义对象
EntityManager em = null;
EntityTransaction tx = null;
try {
// 获取实体管理对象
em = JpaUtil.getEntityManager();
// 获取事务对象
tx = em.getTransaction();
// 开启事务
tx.begin();
// 执行操作
Customer c1 = em.find(Customer.class, 26L);
em.remove(c1);
// 提交事务
tx.commit();
} catch (Exception e) {
// 回滚事务
tx.rollback();
e.printStackTrace();
} finally {
// 释放资源
em.close();
}
}
级联操作:
指操作一个对象同时操作它的关联对象
使用方法:只需要在操作主体的注解上配置 cascade
/**
* cascade:配置级联操作
* CascadeType.MERGE 级联更新
* CascadeType.PERSIST 级联保存:
* CascadeType.REFRESH 级联刷新:
* CascadeType.REMOVE 级联删除:
* CascadeType.ALL 包含所有
*/
@OneToMany(mappedBy="customer",cascade=CascadeType.ALL,targetEntity=LinkMan.clas
s)
@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
private Set<LinkMan> linkmans = new HashSet<LinkMan>(0)