SpringData JPA多表操作(增,删)
Posted 路上的风景
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringData JPA多表操作(增,删)相关的知识,希望对你有一定的参考价值。
一对多:
示例:客户和联系人关系
在实体类中,由于客户是少的一方,它应该包含多个联系人,所以实体类要体现出客户中有多个联系人的信息
/** * 客户的实体类 */ @Entity @Table(name = "cst_customer") public class Customer implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "cust_id") private Long 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, fetch = FetchType.LAZY) // @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id") /** * 放弃外键维护权 * mappedBy: 对方配置关系的属性名称 * cascade: 配置级联(可以配置到设置多表的映射关系的注解上) * CascadeType.all : 所有 * MERGE :更新 * PERSIST :保存 * REMOVE :删除 * fetch : 配置关联对象的加载方式 * EAGER :立即加载 * LAZY :延迟加载 */ @OneToMany(mappedBy = "customer") private Set<LinkMan> linkMans = new HashSet<>(0); /************************ get/set方法 ************************/ }
由于联系人是多的一方,在实体类中要体现出,每个联系人只能对应一个客户
/** * 联系人实体类 */ @Entity @Table(name = "cst_linkman") public class LinkMan { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "lkm_id") 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; /** * 配置联系人到客户的多对一关系 * 使用注解的形式配置多对一关系 * 1.配置表关系 * @ManyToOne : 配置多对一关系 * targetEntity:对方的实体类字节码 * 2.配置外键(多对多配置中间表) * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键 */ @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") private Customer customer; /************************ get/set方法 ************************/ }
持久层接口
/** * 客户持久层接口 * JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作 * JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作) */ public interface CustomerDao extends JpaRepository<Customer, Long>, JpaSpecificationExecutor<Customer> { }
/** * 联系人持久层接口 */ public interface LinkManDao extends JpaRepository<LinkMan, Long>, JpaSpecificationExecutor<LinkMan> { }
测试新增记录
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class OneToManyTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; /** * 保存一个客户,保存一个联系人 */ @Test @Transactional @Rollback(false) public void testAdd() { Customer customer = new Customer(); customer.setCustName("夫子"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("宁缺"); /** * 配置了客户到联系人的关系 * 从客户的角度上:发送两条insert语句,发送一条更新语句更新数据库(更新外键) * 由于我们配置了客户到联系人的关系:客户可以对外键进行维护 */ customer.getLinkMans().add(linkMan); /** * 配置联系人到客户的关系(多对一) * 只发送了两条insert语句 * 由于配置了联系人到客户的映射关系(多对一):联系人也可以对外键进行维护 */ // linkMan.setCustomer(customer); customerDao.save(customer); linkManDao.save(linkMan); } /** *** 最终建议使用方式 *** * 会有一条多余的update语句 * * 由于一的一方可以维护外键:会发送update语句 * * 解决此问题:只需要在一的一方放弃维护权即可 * */ @Test @Transactional @Rollback(false) public void testAdd2() { //创建一个客户,创建一个联系人 Customer customer = new Customer(); customer.setCustName("陈某"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("隆庆"); linkMan.setCustomer(customer);//由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值) customer.getLinkMans().add(linkMan);//由于配置了一的一方到多的一方的关联关系(发送一条update语句) customerDao.save(customer); linkManDao.save(linkMan); } }
测试删除记录
@Test @Transactional @Rollback(false) public void testDelete() { customerDao.delete(1L); }
删除操作的说明如下:
删除从表数据:可以随时任意删除。
删除主表数据:
有从表数据
1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表结构上,外键字段有非空约束,默认情况就会报错。
2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null, 没有关系)因为在删除时,它根本不会去更新从表的外键字段了。
3、如果还想删除,使用级联删除引用(慎用)
没有从表数据引用:随便删
级联操作:指操作一个对象同时操作它的关联对象
使用方法:只需要在操作主体的注解上配置cascade
@OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private Set<LinkMan> linkMans = new HashSet<>(0);
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class CascadeTest { @Autowired private CustomerDao customerDao; @Autowired private LinkManDao linkManDao; /** * 级联添加:保存一个客户的同时,保存客户的所有联系人 * 需要在操作主体的实体类上,配置casacde属性 */ @Test @Transactional @Rollback(false) public void testCascadeAdd() { Customer customer = new Customer(); customer.setCustName("百度"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("小李"); linkMan.setCustomer(customer); customer.getLinkMans().add(linkMan); customerDao.save(customer); } /** * 级联删除: * 删除1号客户的同时,删除1号客户的所有联系人 */ @Test @Transactional @Rollback(false) public void testCascadeRemove() { //1.查询1号客户 Customer customer = customerDao.findOne(1L); //2.删除1号客户 customerDao.delete(customer); } }
多对多:
示例:用户和角色关系
多对多的表关系建立靠的是中间表,其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多
实体类关系建立以及映射配置:
一个用户可以具有多个角色,所以在用户实体类中应该包含多个角色的信息
/** * 用户实体类 */ @Entity @Table(name = "sys_user") public class SysUser implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long userId; @Column(name = "user_code") private String userCode; @Column(name = "user_name") private String userName; @Column(name = "user_password") private String userPassword; @Column(name = "user_state") private String userState;
/** * 配置用户到角色的多对多关系 * 配置多对多的映射关系 * 1.声明表关系的配置 * @ManyToMany(targetEntity = SysRole.class) //多对多 * targetEntity:代表对方的实体类字节码 * 2.配置中间表(包含两个外键) * @JoinTable * name : 中间表的名称 * joinColumns:配置当前对象在中间表的外键 * @JoinColumn的数组 * name:外键名 * referencedColumnName:参照的主表的主键名 * inverseJoinColumns:配置对方对象在中间表的外键 */ @ManyToMany(targetEntity = SysRole.class) @JoinTable(name = "sys_user_role", //joinColumns,当前对象在中间表中的外键 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, //inverseJoinColumns,对方对象在中间表的外键 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} ) private Set<SysRole> roles = new HashSet<SysRole>(0);
/************************ get/set方法 ************************/
}
一个角色可以赋予多个用户,所以在角色实体类中应该包含多个用户的信息
/** * 角色实体类 */ @Entity @Table(name = "sys_role") public class SysRole implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "role_id") private Long roleId; @Column(name = "role_name") private String roleName; @Column(name = "role_memo") private String roleMemo;
//多对多关系映射 @ManyToMany(mappedBy = "roles") // 对方配置关系的属性名称,表示由对方来维护中间表关系 private Set<SysUser> users = new HashSet<SysUser>(0);
/************************ get/set方法 ************************/ }
持久层接口:
/** * 用户持久层接口 */ public interface SysUserDao extends JpaRepository<SysUser, Long>, JpaSpecificationExecutor<SysUser> { }
/** * 角色持久层接口 */ public interface SysRoleDao extends JpaRepository<SysRole, Long>, JpaSpecificationExecutor<SysRole> { }
测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class ManyToManyTest { @Autowired private SysRoleDao sysRoleDao; @Autowired private SysUserDao sysUserDao; /** * 保存一个用户,保存一个角色 * 多对多放弃维护权:被动的一方放弃 */ @Test @Transactional @Rollback(false) public void testAdd() { SysUser sysUser = new SysUser(); sysUser.setUserName("小强"); SysRole sysRole = new SysRole(); sysRole.setRoleName("java程序员"); // 配置用户到角色关系,可以对中间表中的数据进行维护 sysUser.getRoles().add(sysRole); // 配置角色到用户的关系,可以对中间表的数据进行维护(放弃了维护) sysRole.getUsers().add(sysUser); sysUserDao.save(sysUser); sysRoleDao.save(sysRole); } }
在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,主键重复,所以报错
解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:
// 放弃对中间表的维护权,解决保存中主键冲突的问题
@ManyToMany(mappedBy = "roles")
private Set<SysUser> users = new HashSet<SysUser>(0);
多对多级联操作:
和一对多一样,只需要在操作主体的注解上配置cascade
@ManyToMany(targetEntity = SysRole.class, cascade = CascadeType.ALL) @JoinTable(name = "sys_user_role", //joinColumns,当前对象在中间表中的外键 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, //inverseJoinColumns,对方对象在中间表的外键 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} ) private Set<SysRole> roles = new HashSet<SysRole>(0);
测试:
/** * 测试级联添加(保存一个用户的同时保存用户的关联角色) */ @Test @Transactional @Rollback(false) public void testCasCadeAdd() { SysUser sysUser = new SysUser(); sysUser.setUserName("小李"); SysRole sysRole = new SysRole(); sysRole.setRoleName("java程序员"); sysUser.getRoles().add(sysRole); sysRole.getUsers().add(sysUser); sysUserDao.save(sysUser); } /** * 删除操作 * 在多对多的删除时,双向级联删除根本不能配置 * 禁用 * 如果配了的话,如果数据之间有相互引用关系,可能会清空所有数据 * * 案例:删除id为1的用户,同时删除他的关联对象 */ @Test @Transactional @Rollback(false) public void testCasCadeRemove() { //查询1号用户 SysUser user = sysUserDao.findOne(1L); //删除1号用户 sysUserDao.delete(user); }
以上是关于SpringData JPA多表操作(增,删)的主要内容,如果未能解决你的问题,请参考以下文章