JPA第三章

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。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值