JPA第三章
一、表和表之间的关系
1、一对一的关系
A、B两张表,A表中的每条记录都对应B表的一条记录,反之亦然。
2、一对多关系
A、B两张表,A表中的每条记录都对应B表中的多条记录。B表中的多条对应A表中的一条记录。
3、多对多的关系
A、B两张表。A表中的每条记录对应B表中的多条,B表中的每条记录对应A表中的多条记录。
需要有一个中间表C记录多对多的关系。
二、使用jpa框架处理表关系步骤
1、确定表和表之间的关系。
2、对应每个表创建实体类。
3、在实体类中使用注解配置关联关系。
4、测试。
三、一对一的关系
1、工程搭建
1)创建一个maven工程
2)添加jar包
3)配置框架整合。
4)创建实体类、dao
2、一对一关系实现方案
1)使用外键关联
2)使用主键关联,包装两个表的主键相同。
3、外键方案
使用客户表和客户扩展表。
配置关联关系:
1)在Customer实体类中添加一个属性CustomerExt类型
2)在属性上添加一个@OneToOne代表是一个一对一的关系
3)在属性上添加一个@JoinColumn
name:存储外键的字段的名称
referencedColumnName:对方表的主键字段的名称(可以不用配置)
-
//Customer实体类 //表示是一对一的关联关系 @OneToOne //连接的字段:name:当前表存储外键字段的名称,referencedColumnName:对方表的主键字段的名称(可以不用配置,只要对面配置了主键) @JoinColumn(name = "extId",referencedColumnName = "ext_id") private CustomerExt ext; //Customer接口 public interface CustomerDao extends JpaRepository<Customer,Long> {} //CustomerExt实体类 @OneToOne @JoinColumn(name = "custId",referencedColumnName = "cust_id") private Customer customer; //CustomerExt接口 public interface CustomerExtDao extends JpaRepository<CustomerExt,Long> {}
测试:
1)创建一个Customer对象
2)创建一个CustomerExt对象
3)配置对象的关联关系。
4)把对象写入数据库。
插入数据应该开启事务。可以使用@Transactional注解开启事务。
无需在两个表中都添加外键。只需要在一个表中记录外键即可。
-
//测试类 @Test @Transactional @Commit public void addCustomer(){ //1)创建一个Customer对象 Customer customer = new Customer(); customer.setCustName("小花"); customer.setCustSource("学生"); customer.setCustIndustry("有钱人"); customer.setCustLevel("董事长"); customer.setCustAddress("上海"); customer.setCustPhone("16677889900"); //2)创建一个CustomerExt对象 CustomerExt ext = new CustomerExt(); ext.setMemo("这是花花信息"); ext.setInfo("world"); //3)配置对象的关联关系。 customer.setExt(ext); ext.setCustomer(customer); //4)把对象写入数据库。 customerDao.save(customer); customerExtDao.save(ext); } --sql结果 Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, extId) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_customer_ext (custId, info, memo) values (?, ?, ?) Hibernate: update cst_customer set cust_address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_source=?, extId=? where cust_id=?
可以在一方放弃维护权:例如Customer为主表,CustomerExt表就为从表,从表放弃维护权
删除@JoinColumn注解
在@OneToOne注解中添加mappedBy属性,值应该是对方实体类维护关联关系属性的名称。
@OneToOne(mappedBy = “ext”)
-
//CustomerExt实体类 //mappedBy属性,值应该是对方实体类维护关联关系属性的名称。 @OneToOne(mappedBy = "ext") //@JoinColumn(name = "custId") private Customer customer; //测试类 @Test @Transactional @Commit public void addCustomer(){ //1)创建一个Customer对象 Customer customer = new Customer(); customer.setCustName("二胎"); customer.setCustSource("热热"); customer.setCustIndustry("有钱人"); customer.setCustLevel("董事长"); customer.setCustAddress("上海"); customer.setCustPhone("16677889900"); //2)创建一个CustomerExt对象 CustomerExt ext = new CustomerExt(); ext.setMemo("这是二胎信息"); ext.setInfo("kids"); //3)配置对象的关联关系。 customer.setExt(ext); ext.setCustomer(customer); //4)把对象写入数据库。 customerDao.save(customer); customerExtDao.save(ext); } --sql结果 Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, extId) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_customer_ext (info, memo) values (?, ?) Hibernate: update cst_customer set cust_address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_source=?, extId=? where cust_id=?
4、级联操作
需要在@OneToOne注解中添加一个属性cascade:
CascadeType.PERSIST:级联添加
CascadeType.MERGE:级联更新
CascadeType.REMOVE:级联删除
CascadeType.ALL:增删改都级联处理。
-
//Customer实体类 @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "extId") private CustomerExt ext; //CustomerExt实体类 @OneToOne(mappedBy = "ext") private Customer customer; //测试类 @Test @Transactional @Commit public void addCustomer(){ //1)创建一个Customer对象 Customer customer = new Customer(); customer.setCustName("小明"); customer.setCustSource("辅导课"); customer.setCustIndustry("有钱人"); customer.setCustLevel("董事长"); customer.setCustAddress("上海"); customer.setCustPhone("16677889900"); //2)创建一个CustomerExt对象 CustomerExt ext = new CustomerExt(); ext.setMemo("这是学生信息"); ext.setInfo("student"); //3)配置对象的关联关系。 customer.setExt(ext); ext.setCustomer(customer); //4)把对象写入数据库。 customerDao.save(customer); //customerExtDao.save(ext); 省略,会级联添加 } --sql结果 Hibernate: insert into cst_customer_ext (info, memo) values (?, ?) Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source, extId) values (?, ?, ?, ?, ?, ?, ?)
-
修改数据
-
//Customer实体类 @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "extId") private CustomerExt ext; //CustomerExt实体类 @OneToOne(mappedBy = "ext") private Customer customer; //测试 @Test @Transactional @Commit public void updateCustomer() { //查询客户信息 Customer customer = customerDao.findOne(23L); //根据客户信息取扩展信息 CustomerExt ext = customer.getExt(); //修改扩展信息 ext.setMemo("这是超人信息"); ext.setInfo("woman"); //更新到数据库 customerDao.save(customer); } --sql结果 Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_, customer0_.extId as extId8_0_0_, customerex1_.ext_id as ext_id1_1_1_, customerex1_.info as info2_1_1_, customerex1_.memo as memo3_1_1_ from cst_customer customer0_ left outer join cst_customer_ext customerex1_ on customer0_.extId=customerex1_.ext_id where customer0_.cust_id=? Hibernate: select customer0_.cust_id as cust_id1_0_1_, customer0_.cust_address as cust_add2_0_1_, customer0_.cust_industry as cust_ind3_0_1_, customer0_.cust_level as cust_lev4_0_1_, customer0_.cust_name as cust_nam5_0_1_, customer0_.cust_phone as cust_pho6_0_1_, customer0_.cust_source as cust_sou7_0_1_, customer0_.extId as extId8_0_1_, customerex1_.ext_id as ext_id1_1_0_, customerex1_.info as info2_1_0_, customerex1_.memo as memo3_1_0_ from cst_customer customer0_ left outer join cst_customer_ext customerex1_ on customer0_.extId=customerex1_.ext_id where customer0_.extId=? Hibernate: update cst_customer_ext set info=?, memo=? where ext_id=?
-
-
删除数据
-
//测试类 @Test @Transactional @Commit public void deleteCustomer() { customerDao.delete(26L); } --sql结果 Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_address as cust_add2_0_0_, customer0_.cust_industry as cust_ind3_0_0_, customer0_.cust_level as cust_lev4_0_0_, customer0_.cust_name as cust_nam5_0_0_, customer0_.cust_phone as cust_pho6_0_0_, customer0_.cust_source as cust_sou7_0_0_, customer0_.extId as extId8_0_0_, customerex1_.ext_id as ext_id1_1_1_, customerex1_.custId as custId4_1_1_, customerex1_.info as info2_1_1_, customerex1_.memo as memo3_1_1_, customer2_.cust_id as cust_id1_0_2_, customer2_.cust_address as cust_add2_0_2_, customer2_.cust_industry as cust_ind3_0_2_, customer2_.cust_level as cust_lev4_0_2_, customer2_.cust_name as cust_nam5_0_2_, customer2_.cust_phone as cust_pho6_0_2_, customer2_.cust_source as cust_sou7_0_2_, customer2_.extId as extId8_0_2_ from cst_customer customer0_ left outer join cst_customer_ext customerex1_ on customer0_.extId=customerex1_.ext_id left outer join cst_customer customer2_ on customerex1_.custId=customer2_.cust_id where customer0_.cust_id=? Hibernate: delete from cst_customer where cust_id=? Hibernate: delete from cst_customer_ext where ext_id=?
-
5、主键方案
使用方法:
不使用@JoinColumn注解
使用@PrimaryKeyJoinColumn注解,不需要配置属性。
双方都需要添加
-
//Customer实体类 //表示是一对一的关联关系 @OneToOne/*(cascade = CascadeType.ALL)*/ //连接的字段:name:当前表存储外键字段的名称,referencedColumnName:对方表的主键字段的名称(可以不用配置) //@JoinColumn(name = "extId") @PrimaryKeyJoinColumn private CustomerExt ext; //CustomerExt实体类 //mappedBy属性,值应该是对方实体类维护关联关系属性的名称。 @OneToOne/*(mappedBy = "ext")*/ //@JoinColumn(name = "custId") @PrimaryKeyJoinColumn private Customer customer; --sql结果 Hibernate: insert into cst_customer (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?) Hibernate: insert into cst_customer_ext (info, memo) values (?, ?)
四、一对多关联
1、场景
我们采用客户和联系人之间的关系来说明一对多关系。
一个客户对应多个联系人。
2、关联关系的配置
一的一方:
1)添加一个属性记录多的一方的信息。应该是一个集合属性。
2)在属性上添加一个注解@OneToMany
3)使用@JoinColumn注解配置外键关系。
一定是多的一方记录外键,参照一的一方的主键。
多的一方:
1)在联系人实体类中添加一个Customer属性。
2)在属性上添加注解@ManyToOne
3)在属性上添加@JoinColumn注解
-
//Customer2实体类 //@OneToMany默认的fetch是LAZY,懒加载 @OneToMany @JoinColumn(name = "custId") private Set<LinkMan> linkManSet = new HashSet<>(); //Customer2接口 public interface Customer2Dao extends JpaRepository<Customer2,Long> {} //LinkMan实体类 //@ManyToOne默认的fetch是EAGER,即时加载 @ManyToOne @JoinColumn(name = "custId") private Customer2 customer2; //LinkMan接口 public interface LinkManDao extends JpaRepository<LinkMan,Long> {}
3、测试
1)每个实体类对应创建dao
2)在测试方法中,创建一个Customer2对象
3)创建LinkMan对象,可以创建多个。
4)配置客户和联系人之间的关系。
5)使用dao把数据写入数据库。
需要开启事务
-
//Customer2实体类 @OneToMany //name:外键字段的名称 @JoinColumn(name = "custid") private Set<LinkMan> linkManSet = new HashSet<>(); //LinkMan实体类 @ManyToOne @JoinColumn(name = "custId") private Customer2 customer2; //测试类 @Test @Transactional @Commit public void addCustomer2() { //1)每个实体类对应创建dao //2)在测试方法中,创建一个Customer2对象 Customer2 customer2 = new Customer2(); customer2.setCustName("马化腾"); customer2.setCustSource("腾讯"); customer2.setCustIndustry("有钱人"); customer2.setCustLevel("董事长"); customer2.setCustAddress("上海"); customer2.setCustPhone("16677889900"); //3)创建LinkMan对象,可以创建多个。 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkmName("张三"); linkMan1.setLkmPhone("13344556677"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkmName("李四"); linkMan2.setLkmPhone("13944588677"); //4)配置客户和联系人之间的关系。 customer2.getLinkManSet().add(linkMan1); customer2.getLinkManSet().add(linkMan2); linkMan1.setCustomer2(customer2); linkMan2.setCustomer2(customer2); //5)使用dao把数据写入数据库。 customer2Dao.save(customer2); linkManDao.save(linkMan1); linkManDao.save(linkMan2); } --sql结果 Hibernate: insert into cst_customer2 (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (custId, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (custId, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: update cst_linkman set custId=? where lkm_id=? Hibernate: update cst_linkman set custId=? where lkm_id=?
只需要在一方维护关联关系即可,一方放弃维护。
一对多关系应该在一的一方访问维护交给多的一方来维护。
删除@JoinColumn注解
在@OneToMany注解中配置mappedBy属性
-
//Customer2实体类 //@OneToMany默认的fetch是LAZY,懒加载 @OneToMany(mappedBy = "customer2") //@JoinColumn(name = "custId") private Set<LinkMan> linkManSet = new HashSet<>(); //LinkMan实体类 @ManyToOne @JoinColumn(name = "custId") private Customer2 customer2;
4、级联操作
应该只对主表进行操作即可,从表自动更新。
要求应该在@OneToMany注解中配置cascade属性
-
添加数据
-
//Customer2实体类 //@OneToMany默认的fetch是LAZY,懒加载 @OneToMany(mappedBy = "customer2",cascade = CascadeType.ALL) //@JoinColumn(name = "custId") private Set<LinkMan> linkManSet = new HashSet<>(); //LinkMan实体类 //@ManyToOne默认的fetch是EAGER,即时加载 @ManyToOne @JoinColumn(name = "custId") private Customer2 customer2; //测试类 @Test @Transactional @Commit public void addCustomer2() { //1)每个实体类对应创建dao //2)在测试方法中,创建一个Customer2对象 Customer2 customer2 = new Customer2(); customer2.setCustName("马化腾2"); customer2.setCustSource("腾讯"); customer2.setCustIndustry("有钱人"); customer2.setCustLevel("董事长"); customer2.setCustAddress("上海"); customer2.setCustPhone("16677889900"); //3)创建LinkMan对象,可以创建多个。 LinkMan linkMan1 = new LinkMan(); linkMan1.setLkmName("王五"); linkMan1.setLkmPhone("13344556677"); LinkMan linkMan2 = new LinkMan(); linkMan2.setLkmName("麻子"); linkMan2.setLkmPhone("13944588677"); //4)配置客户和联系人之间的关系。 customer2.getLinkManSet().add(linkMan1); customer2.getLinkManSet().add(linkMan2); linkMan1.setCustomer2(customer2); linkMan2.setCustomer2(customer2); //5)使用dao把数据写入数据库。 customer2Dao.save(customer2); /* linkManDao.save(linkMan1); linkManDao.save(linkMan2);*/ } --sql结果 Hibernate: insert into cst_customer2 (cust_address, cust_industry, cust_level, cust_name, cust_phone, cust_source) values (?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (custId, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (custId, lkm_email, lkm_gender, lkm_memo, lkm_mobile, lkm_name, lkm_phone, lkm_position) values (?, ?, ?, ?, ?, ?, ?, ?)
-
-
修改数据
-
//测试类 @Test @Transactional @Commit public void updateCustomer() { //根据id查询一个客户信息 Customer2 customer2 = customer2Dao.findOne(2L); customer2.setCustIndustry("游戏"); customer2.setCustSource("北京"); //根据客户信息取联系人信息 Set<LinkMan> linkManSet = customer2.getLinkManSet(); //更新数据 for (LinkMan linkMan : linkManSet) { linkMan.setLkmGender("男"); } //保存到数据库 customer2Dao.save(customer2); } --sql结果 Hibernate: select customer2x0_.cust_id as cust_id1_1_0_, customer2x0_.cust_address as cust_add2_1_0_, customer2x0_.cust_industry as cust_ind3_1_0_, customer2x0_.cust_level as cust_lev4_1_0_, customer2x0_.cust_name as cust_nam5_1_0_, customer2x0_.cust_phone as cust_pho6_1_0_, customer2x0_.cust_source as cust_sou7_1_0_ from cst_customer2 customer2x0_ where customer2x0_.cust_id=? Hibernate: select linkmanset0_.custId as custId9_3_0_, linkmanset0_.lkm_id as lkm_id1_3_0_, linkmanset0_.lkm_id as lkm_id1_3_1_, linkmanset0_.custId as custId9_3_1_, linkmanset0_.lkm_email as lkm_emai2_3_1_, linkmanset0_.lkm_gender as lkm_gend3_3_1_, linkmanset0_.lkm_memo as lkm_memo4_3_1_, linkmanset0_.lkm_mobile as lkm_mobi5_3_1_, linkmanset0_.lkm_name as lkm_name6_3_1_, linkmanset0_.lkm_phone as lkm_phon7_3_1_, linkmanset0_.lkm_position as lkm_posi8_3_1_ from cst_linkman linkmanset0_ where linkmanset0_.custId=? Hibernate: update cst_customer2 set cust_address=?, cust_industry=?, cust_level=?, cust_name=?, cust_phone=?, cust_source=? where cust_id=? Hibernate: update cst_linkman set custId=?, lkm_email=?, lkm_gender=?, lkm_memo=?, lkm_mobile=?, lkm_name=?, lkm_phone=?, lkm_position=? where lkm_id=? Hibernate: update cst_linkman set custId=?, lkm_email=?, lkm_gender=?, lkm_memo=?, lkm_mobile=?, lkm_name=?, lkm_phone=?, lkm_position=? where lkm_id=?
-
-
删除数据
-
//测试类 @Test @Transactional @Commit public void deleteCustomer2() { customer2Dao.delete(1L); } --sql结果 Hibernate: select customer2x0_.cust_id as cust_id1_1_0_, customer2x0_.cust_address as cust_add2_1_0_, customer2x0_.cust_industry as cust_ind3_1_0_, customer2x0_.cust_level as cust_lev4_1_0_, customer2x0_.cust_name as cust_nam5_1_0_, customer2x0_.cust_phone as cust_pho6_1_0_, customer2x0_.cust_source as cust_sou7_1_0_ from cst_customer2 customer2x0_ where customer2x0_.cust_id=? Hibernate: select linkmanset0_.custId as custId9_3_0_, linkmanset0_.lkm_id as lkm_id1_3_0_, linkmanset0_.lkm_id as lkm_id1_3_1_, linkmanset0_.custId as custId9_3_1_, linkmanset0_.lkm_email as lkm_emai2_3_1_, linkmanset0_.lkm_gender as lkm_gend3_3_1_, linkmanset0_.lkm_memo as lkm_memo4_3_1_, linkmanset0_.lkm_mobile as lkm_mobi5_3_1_, linkmanset0_.lkm_name as lkm_name6_3_1_, linkmanset0_.lkm_phone as lkm_phon7_3_1_, linkmanset0_.lkm_position as lkm_posi8_3_1_ from cst_linkman linkmanset0_ where linkmanset0_.custId=? Hibernate: delete from cst_linkman where lkm_id=? Hibernate: delete from cst_linkman where lkm_id=? Hibernate: delete from cst_customer2 where cust_id=?
-
五、多对多关联关系
1、场景
我们采用用户、角色的关系为例
需要第三张表记录关联关系。
2、配置关联关系
多对多的关联关系使用注解@ManyToMany配置。
1)在实体机中添加一个集合属性。
2)在属性上添加@ManyToMany注解。
3)使用@JoinTable注解配置关联关系。
@JoinTable注解就是中间表的定义。
name:中间表的名称
当前表和中间表的映射关系。
对方表和中间表的映射关系
-
//SysUser实体类 //多对多 @ManyToMany //@JoinTable相当于中间表的定义 //name:中间表的表名 @JoinTable(name = "sys_user_role", joinColumns = @JoinColumn( //中间表和当前表映射的字段的属性名称 name = "userId", //参照的当前表的主键字段 referencedColumnName = "user_id"), inverseJoinColumns = @JoinColumn(name = "roleId", referencedColumnName = "role_id")) private Set<SysRole> roles = new HashSet<>(); //SysUser接口 public interface SysUserDao extends JpaRepository<SysUser,Long> {} //SysRole实体类 //多对多 @ManyToMany //@JoinTable相当于中间表的定义 //name:中间表的表名 @JoinTable(name = "sys_user_role", //中间表和当前表映射的字段的属性名称 joinColumns = @JoinColumn( name = "roleId", //参照的当前表的主键字段 referencedColumnName = "role_id"), inverseJoinColumns = @JoinColumn( name = "userId", referencedColumnName = "user_id")) private Set<SysUser> users = new HashSet<>(); //SysRole接口 public interface SysRoleDao extends JpaRepository<SysRole,Long> {}
在多对多关联关系中,只能一方维护关联关系,另外一方放弃维护权。
************************************************************
//相当于中间表的定义
//name:中间表的表名
-
@JoinTable(name = "sys_user_role", joinColumns = @JoinColumn( //中间表和当前表映射的字段的名称 name = "userid", //参照的当前表的主键字段 referencedColumnName = "user_id"), inverseJoinColumns = @JoinColumn( name = "roleid", referencedColumnName = "role_id"))
3、测试
对应每个实体类创建一个dao
创一个测试类
1)创建两个用户
2)创建两个角色
3)配置用户和角色之间的关联关系
4)把用户和角色写入数据库
需要开启事务
-
//测试类 @Test @Transactional @Commit public void addUserAndRole() { //1)创建两个用户 SysUser user = new SysUser(); user.setUsername("admin"); user.setPassword("123456"); SysRole role = new SysRole(); role.setRoleName("管理员"); //3)配置用户和角色之间的关联关系 user.getRoles().add(role); role.getUsers().add(user); //4)把用户和角色写入数据库 userDao.save(user); roleDao.save(role); } --sql结果报错,因为重复添加2次主键,主键相同,会冲突,必须只能一方有维护权 Hibernate: insert into sys_user (password, username) values (?, ?) Hibernate: insert into sys_role (memo, role_name) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?) Hibernate: insert into sys_user_role (roleId, userId) values (?, ?) org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
4、放弃维护权
删除@JoinTable注解
在@ManyToMany注解中添加mappedBy属性
-
//SysRole实体类修改放弃维护权 //多对多 @ManyToMany(mappedBy = "roles") //@JoinTable相当于中间表的定义 //name:中间表的表名 /* @JoinTable(name = "sys_user_role", //中间表和当前表映射的字段的属性名称 joinColumns = @JoinColumn( name = "roleId", //参照的当前表的主键字段 referencedColumnName = "role_id"), inverseJoinColumns = @JoinColumn( name = "userId", referencedColumnName = "user_id"))*/ private Set<SysUser> users = new HashSet<>(); //sql结果 Hibernate: insert into sys_user (password, username) values (?, ?) Hibernate: insert into sys_role (memo, role_name) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?)
5、配置级联操作
在@ManyToMany注解中配置cascade属性。
-
//SysUser实体类 //多对多 @ManyToMany(cascade = CascadeType.ALL) //@JoinTable相当于中间表的定义 //name:中间表的表名 @JoinTable(name = "sys_user_role", joinColumns = @JoinColumn( //中间表和当前表映射的字段的属性名称 name = "userId", //参照的当前表的主键字段 referencedColumnName = "user_id"), inverseJoinColumns = @JoinColumn(name = "roleId", referencedColumnName = "role_id")) private Set<SysRole> roles = new HashSet<>(); //测试类 @Test @Transactional @Commit public void addUserAndRole2() { SysUser user1 = new SysUser(); user1.setUsername("周星驰"); user1.setPassword("123"); SysUser user2 = new SysUser(); user2.setUsername("徐峥"); user2.setPassword("456"); SysRole role1 = new SysRole(); role1.setRoleName("演员"); SysRole role2 = new SysRole(); role2.setRoleName("导演"); //配置关联关系 user1.getRoles().add(role1); user1.getRoles().add(role2); user2.getRoles().add(role1); user2.getRoles().add(role2); role1.getUsers().add(user1); role1.getUsers().add(user2); role2.getUsers().add(user1); role2.getUsers().add(user2); //写入数据库 userDao.save(user1); userDao.save(user2); //roleDao.save(role1); //roleDao.save(role2); } --sql结果 Hibernate: insert into sys_user (password, username) values (?, ?) Hibernate: insert into sys_role (memo, role_name) values (?, ?) Hibernate: insert into sys_role (memo, role_name) values (?, ?) Hibernate: insert into sys_user (password, username) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?) Hibernate: insert into sys_user_role (userId, roleId) values (?, ?)
-
修改数据
-
//测试类 @Test @Transactional @Commit public void deleteUser() { userDao.delete(1L); } --sql结果 Hibernate: select sysuser0_.user_id as user_id1_5_0_, sysuser0_.password as password2_5_0_, sysuser0_.username as username3_5_0_ from sys_user sysuser0_ where sysuser0_.user_id=? Hibernate: select roles0_.userId as userId1_6_0_, roles0_.roleId as roleId2_6_0_, sysrole1_.role_id as role_id1_4_1_, sysrole1_.memo as memo2_4_1_, sysrole1_.role_name as role_nam3_4_1_ from sys_user_role roles0_ inner join sys_role sysrole1_ on roles0_.roleId=sysrole1_.role_id where roles0_.userId=? Hibernate: delete from sys_user_role where userId=? Hibernate: delete from sys_role where role_id=? Hibernate: delete from sys_user where user_id=?
-
六、关联查询
1、使用导航的方式查询
使用“对象.属性”方式进行查询。
对多的查询默认是延迟加载。
如果想修改默认行为:
在@OneToMany注解中添加一个属性fetch
FetchType.LAZY:延迟加载
FetchType.EAGER:即时加载
通过联系人查询客户:
对一的查询默认是即时加载。
也可以配置成延迟加载。
对多的查询默认都是延迟加载,对一的查询默认都是即时加载,可以修改fetch属性修改默认行为。
一对一的是即时加载,多对多默认是延迟加载。
-
//测试类头部 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class QueryTest { @Autowired private Customer2Dao customer2Dao; @Autowired private LinkManDao linkManDao; }
-
查询一对多数据根据Customer2获取
//测试方法 @Test @Transactional public void queryCustomer() { Customer2 customer2 = customer2Dao.findOne(2L); System.out.println(customer2); //根据客户查询联系人信息 Set<LinkMan> linkManSet = customer2.getLinkManSet(); for (LinkMan linkMan : linkManSet) { System.out.println(linkMan); } } --sql结果 Hibernate: select customer2x0_.cust_id as cust_id1_1_0_, customer2x0_.cust_address as cust_add2_1_0_, customer2x0_.cust_industry as cust_ind3_1_0_, customer2x0_.cust_level as cust_lev4_1_0_, customer2x0_.cust_name as cust_nam5_1_0_, customer2x0_.cust_phone as cust_pho6_1_0_, customer2x0_.cust_source as cust_sou7_1_0_ from cst_customer2 customer2x0_ where customer2x0_.cust_id=? Customer2{custId=2, custName='马化腾', custSource='腾讯', custIndustry='游戏', custLevel='董事长', custAddress='上海', custPhone='16677889900'} Hibernate: select linkmanset0_.custId as custId9_3_0_, linkmanset0_.lkm_id as lkm_id1_3_0_, linkmanset0_.lkm_id as lkm_id1_3_1_, linkmanset0_.custId as custId9_3_1_, linkmanset0_.lkm_email as lkm_emai2_3_1_, linkmanset0_.lkm_gender as lkm_gend3_3_1_, linkmanset0_.lkm_memo as lkm_memo4_3_1_, linkmanset0_.lkm_mobile as lkm_mobi5_3_1_, linkmanset0_.lkm_name as lkm_name6_3_1_, linkmanset0_.lkm_phone as lkm_phon7_3_1_, linkmanset0_.lkm_position as lkm_posi8_3_1_ from cst_linkman linkmanset0_ where linkmanset0_.custId=? LinkMan{lkmId=4, lkmName='麻子', lkmGender='男', lkmPhone='13944588677', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'} LinkMan{lkmId=3, lkmName='王五', lkmGender='男', lkmPhone='13344556677', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'}
-
查询一对多数据根据LinkMan获取
//测试方法 @Test @Transactional public void getLinkMan() { LinkMan linkMan = linkManDao.findOne(2L); System.out.println(linkMan); //通过联系人查询客户 Customer2 customer2 = linkMan.getCustomer2(); System.out.println(customer2); } --sql结果 Hibernate: select linkman0_.lkm_id as lkm_id1_3_0_, linkman0_.custId as custId9_3_0_, linkman0_.lkm_email as lkm_emai2_3_0_, linkman0_.lkm_gender as lkm_gend3_3_0_, linkman0_.lkm_memo as lkm_memo4_3_0_, linkman0_.lkm_mobile as lkm_mobi5_3_0_, linkman0_.lkm_name as lkm_name6_3_0_, linkman0_.lkm_phone as lkm_phon7_3_0_, linkman0_.lkm_position as lkm_posi8_3_0_, customer2x1_.cust_id as cust_id1_1_1_, customer2x1_.cust_address as cust_add2_1_1_, customer2x1_.cust_industry as cust_ind3_1_1_, customer2x1_.cust_level as cust_lev4_1_1_, customer2x1_.cust_name as cust_nam5_1_1_, customer2x1_.cust_phone as cust_pho6_1_1_, customer2x1_.cust_source as cust_sou7_1_1_ from cst_linkman linkman0_ left outer join cst_customer2 customer2x1_ on linkman0_.custId=customer2x1_.cust_id where linkman0_.lkm_id=? LinkMan{lkmId=3, lkmName='王五', lkmGender='男', lkmPhone='13344556677', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'} Customer2{custId=2, custName='马化腾', custSource='腾讯', custIndustry='游戏', custLevel='董事长', custAddress='上海', custPhone='16677889900'}
2、使用Specification方式查询
1)需求
查询客户信息,根据联系人的手机号。
2) sql
SELECT * FROM cst_customer2 a LEFT JOIN cst_linkman b ON a.cust_id = b.custid WHERE
b.lkm_phone = '16677889900'
-
需要使用Customer2Dao执行查询。需要继承JpaSpecificationExecutor接口。
-
public interface Customer2Dao extends JpaRepository<Customer2,Long>, JpaSpecificationExecutor<Customer2> {}
//使用root关联联系人表
//参数1:Customer2实体类中关联的属性名称
//参数2:连接方式
Join<Object, Object> join = root.join(“linkMans”, JoinType.LEFT); -
-
//测试方法 @Test @Transactional public void findCustomerByPhone() { Customer2 customer2 = customer2Dao.findOne(new Specification<Customer2>() { @Override public Predicate toPredicate(Root<Customer2> root, CriteriaQuery<?> query, CriteriaBuilder cb) { //使用root关联联系人表 //参数1:Customer2实体类中关联的属性名称 //参数2:连接方式 // 类似cst_customer2 a LEFT JOIN cst_linkman b ON a.cust_id = b.custid Join<Object, Object> join = root.join("linkManSet", JoinType.LEFT); //创建查询条件: 类似WHERE b.lkm_phone = '16677889900' Predicate predicate = cb.equal(join.get("lkmPhone"), "12345678901"); return predicate; } }); System.out.println(customer2); Set<LinkMan> linkManSet = customer2.getLinkManSet(); for (LinkMan linkMan : linkManSet) { System.out.println(linkMan); } } //测试简化 @Test @Transactional public void findCustomerByPhoneEasy() { customer2Dao.findOne(((root, query, cb) -> cb.equal( (root.join("linkManSet",JoinType.LEFT)).get("lkmPhone"), "12345678901"))) .getLinkManSet().forEach(System.out::println); } --sql结果 Hibernate: select customer2x0_.cust_id as cust_id1_1_, customer2x0_.cust_address as cust_add2_1_, customer2x0_.cust_industry as cust_ind3_1_, customer2x0_.cust_level as cust_lev4_1_, customer2x0_.cust_name as cust_nam5_1_, customer2x0_.cust_phone as cust_pho6_1_, customer2x0_.cust_source as cust_sou7_1_ from cst_customer2 customer2x0_ left outer join cst_linkman linkmanset1_ on customer2x0_.cust_id=linkmanset1_.custId where linkmanset1_.lkm_phone=? Customer2{custId=2, custName='马化腾', custSource='腾讯', custIndustry='游戏', custLevel='董事长', custAddress='上海', custPhone='16677889900'} Hibernate: select linkmanset0_.custId as custId9_3_0_, linkmanset0_.lkm_id as lkm_id1_3_0_, linkmanset0_.lkm_id as lkm_id1_3_1_, linkmanset0_.custId as custId9_3_1_, linkmanset0_.lkm_email as lkm_emai2_3_1_, linkmanset0_.lkm_gender as lkm_gend3_3_1_, linkmanset0_.lkm_memo as lkm_memo4_3_1_, linkmanset0_.lkm_mobile as lkm_mobi5_3_1_, linkmanset0_.lkm_name as lkm_name6_3_1_, linkmanset0_.lkm_phone as lkm_phon7_3_1_, linkmanset0_.lkm_position as lkm_posi8_3_1_ from cst_linkman linkmanset0_ where linkmanset0_.custId=? LinkMan{lkmId=3, lkmName='王五', lkmGender='男', lkmPhone='12345678901', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'} LinkMan{lkmId=4, lkmName='麻子', lkmGender='男', lkmPhone='13944588677', lkmMobile='null', lkmEmail='null', lkmPosition='null', lkmMemo='null'}
七、总结
1、表和表之间的关系
一对一
一对多
多对多
2、处理关联关系的步骤
1)确定表和表之间的关系
2)对应每个表创建实体类
3)在实体类中使用注解配置关联关系
4)测试
3、一对一
注解:
@OneToOne
配置关联关系:
使用外键:
@JoinColumn
使用主键关联:
@PrimaryKeyJoinColumn
4、一对多
一的一方:
@OneToMany
使用@JoinColumn注解配置关联关系
多的一方:
@ManyToOne
也可以配置@JoinColumn注解配置关联关系
推荐一方维护即可。
5、多对多
注解:
@ManyToMany
配置关联关系:
@JoinTable
中间表的定义。
只能是一方维护关联关系,另外一方放弃。
6、关联查询
1)使用导航方式查询
对多查询默认是懒加载
对一的查询默认即时加载。
可以设置fetch属性,修改默认行为。
2)使用Specification方式查询
使用root对象join关联其他的实体类。作为真正的root。