SpringDataJpa(五) 多表操作-一对多
1.多表设计
1)表之间关系划分
数据库中多表之间存在着三种关系:
一对多关系可以看作两种: 一对多,多对一
2)JPA 表关系分析步骤
1.确定两张表之间的关系
2.在数据库中实现两张表的关系
3.在实体类中描述两张表的关系(包含|继承)
4.配置实体类与数据库表的关系映射
2.一对多操作
1) 实例分析:
在CRM 客户关系管理系统中: 客户与联系人 是一对多关系
客户:客户是指顾客方,可以理解为公司,如公司A
联系人:公司中负责对接(洽谈合同…)的联系人,如公司A中的员工
一个客户可能有多个联系人(存在一对多关系)
eg:商务联系人、财务联系人、技术联系人
2) 表关系建立
如图:
3)实体类与数据库表关系映射
Customer.java
客户实体 一的一方
/**
* 客户实体类
*/
@Entity
@Table(name = "cst_customer")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cust_id")
private Long custId;
@Column(name = "cust_name")
private String custName;
@Column(name = "cust_industry")
private String custIndustry;
@Column(name = "cust_source")
private String custSource;
@Column(name = "cust_level")
private String custLevel;
@Column(name = "cust_address")
private String custAddress;
@Column(name = "cust_phone")
private String custPhone;
/**
* 外键关联 一对多
* 客户:联系人:1:n
* @OneToMany(targetEntity = 关联实体.class)
* @JoinColumn(name = "从表外键名称(在多的一方表中)",
* referencedColumnName = "主表主键id")
* 最维护外键
*/
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
/**
* 放弃外键维护权,外键维护参照 mappedBy= "多的一方维护外键的属性"
* cascade: 级联设置
* CascadeType.ALL: 增删改查全部级联操作
*
*/
//@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
//getter setter 方法
}
LinkMan.java
联系人实体类,多的一方
/**
* 联系人实体类
*/
@Entity
@Table(name = "cst_linkman")
public class LinkMan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "lkm_id")
private Long lkmId; //联系人id
@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;//联系人备注
/**
* 外键关联
* 多对一
* @JoinColumn: 维护外键
*/
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Customer customer;
//getter setter 方法
}
4) 映射注解说明
@OneToMany:
- 作用: 建立一对多关系映射
- 属性:
-
targetEntityCLass: 指定多的一方类的字节码
- eg:@OneToMany(targetEntity = LinkMan.class)
-
mapperBy: 放弃外键维护权力,由从表维护外键,指定多的一方引用一的一方对象名称
- eg: LinkMan: private Customer customer;
Customer: @OneToMany(mappedBy = “customer”)
- eg: LinkMan: private Customer customer;
-
cascade: 指定要使用的级联操作
-
fetch:指定是否使用延迟加载
-
orphanRemoval:是否使用孤儿删除
-
@ManyToOne
- 作用:建立多对一的关系
- 属性:
- targetEntityClass:指定一的一方实体类字节码
- cascade:指定要使用的级联操作
- fetch:指定是否采用延迟加载
- fetch = FetchType.EAGER 立即加载
- fetch = FetchType.LAZY 延迟加载
- optional:关联是否可选。如果设置为false,则必须始终存在非空关系。
@JoinColumn
- 作用:用于定义主键和外键的对应关系
- 属性:
- name:指定外键字段的名称(以数据库表为准)
- referencedColumnName:指定引用主表的主键字段名称
- unique:是否唯一,默认值不唯一
- nullable:是否允许空值,默认允许
- insertable:是否允许插入,默认允许
- updatable:是否允许更新,默认允许
- columnDefinition:列定义信息
5) 双向关系添加操作
Customer.java
@OneToMany(targetEntity = LinkMan.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
LinkMan.java
@ManyToOne(targetEntity = Customer.class)
@JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
private Customer customer;
双方都有外键维护权,都能找到对方,故为双向关系
测试
@Autowired
private CustomerDao customerDao;
@Autowired
private LinkManDao linkManDao;
/**
* 双方同时维护外键测试
* 多出一条update 语句
* 原因: 多的一方在插入数据时自动维护外键
* 而后一的一方再次更新外键(多余操作)
*/
@Test
@Transactional
@Rollback(false)
public void test_save03(){
Customer customer = new Customer();
customer.setCustName("李四");
LinkMan linkMan = new LinkMan();
linkMan.setLkmName("王二");
/**
* 双方同时维护外键
*/
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);
customerDao.save(customer);
linkManDao.save(linkMan);
}
问题:多的一方在插入时就已经设置了外键,一的一方又updte重新修改了外键(虽然外键没变化,该操作是多余的)
- 两条inser语句,一条update语句(多余的)
解决:一的一方放弃外键维护权
Customer.java
@OneToMany(mappedBy = "customer")
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
6)级联删除
删除从表数据:可任意删除
删除主表数据:
- 无从表关联:随便删除,无影响
- 有从表关联:
默认情况下
,会将从表关联的外键字段设为null,然后删除主表数据(如果数据库表结构上设置外键不能为控,则会报错)- 如果主表配置
放弃外键维护权
,则不能删除(有外键与之关联) 设置级联删除
,删除主表记录的同时,从表与之有关联的记录也会被删除,从表删除不影响主表
cascade:配置级联操作
- CascadeType.MERGE 级联更新
- CascadeType.PERSIST 级联保存
- CascadeType.REFRESH 级联刷新
- CascadeType.REMOVE 级联删除
- CascadeType.ALL 级联所有操作
Customer.java
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
测试:
/**
* 级联删除
* 删除客户的同时,删除与客户关联的所有联系人
*/
@Test
@Transactional
@Rollback(false)
public void test_cascadeDelete(){
Customer customer = customerDao.findOne(1l);
customerDao.delete(customer);
}