zoukankan      html  css  js  c++  java
  • SpringDataJpa-多表操作

    多表之间的关系和操作多表的操作步骤

    表关系

    • 一对多
    • 一对多      >>  一:主表     多:从表
    • 多对多     >>  中间表中最少应该由两个字段组成,这两个字段作为外键指向两张表的主键,又组成了联合主键

     分析步骤

    1. 明确表关系
    2. 确定表关系( 描述: 外键  |  中间表 )
    3. 编写实体类,在实体类中描述表关系(包含关系)
    4. 配置映射关系

    完成多表操作

    一对多操作 案例:客户和联系人(一对多关系) 

        >> 客户:一家公司     联系人:这家公司的员工

     一个客户可以具有多个联系人,一个联系人从属于一家公司

    分析步骤

      1. 明确表关系  >> 一对多关系

      2. 确定表关系,再从表上添加外键(描述: 外键 I 中间表)

    • 主表:客户表   
    • 从表:联系人表

      3. 编写实体类,在实体类中描述表关系(包含关系)

    • 客户:在客户的实体类中包含一个联系人的集合
    • 联系人:在联系人的实体类中包含一个客户的对象

      4. 配置映射关系
      使用JPA注解配置一对多映射关系

    操作步骤

    1.引入依赖坐标,导入实体类和xml文件

    2.  Customer   >> 配置客户和联系人之间的一对多关系

    1   @OneToMany(targetEntity = LinkMan.class) //对方实体类的字节码对象
    2   @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") //name:外键的名称   referencedColumnName: 外键的取值来源
    3   private Set<LinkMan> linkMans = new HashSet<LinkMan>();

      LinkMan   >> 配置客户和联系人之间的一对多关系

    1   @ManyToOne(targetEntity = Customer.class)
    2   @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    3   private Customer customer;

    配置外键的过程中,配置到多的一方,就会在多的一方维护外键

    3. 保存一个客户,保存一个联系人

     1 @RunWith(SpringJUnit4ClassRunner.class)
     2 @ContextConfiguration(locations = "classpath:applicationContext.xml")
     3 public class OneToManyTest {
     4     @Autowired
     5     private CustomerDao customerDao;
     6     @Autowired
     7     private LinkManDao linkManDao;
     8     /**
     9      * 保存一个客户,保存一个联系人
    10      *  效果:客户和联系人作为独立的数据保存到数据库中
    11      *      联系人的外键为空
    12      *  原因? >> 实体类中没有配置关系!!
    14      */
    15     @Test
    16     @Transactional //配置事务
    17     @Rollback(false) //不自动回滚
    18     public void testAdd() {
    19         //创建一个客户,创建一个联系人
    20         Customer customer = new Customer();
    21         customer.setCustname("百度");
    22 
    23         LinkMan linkMan = new LinkMan();
    24         linkMan.setLkmName("小李");
    25         /**
    26          * 配置了客户到联系人的关系
    27          *      从客户的角度上:发送两条insert语句,发送一条更新语句更新数据库(更新外键)
    28          * 由于我们配置了客户到联系人的关系:客户可以对外键进行维护
    29          */
    30 customer.getLinkMans().add(linkMan);
    31 
    32         customerDao.save(customer);
    33         linkManDao.save(linkMan);
    34     }

    进行改进

    1 /**
    2          * 配置联系人到客户的关系(多对一)
    3          *    只发送了两条insert语句
    4          * 由于配置了联系人到客户的映射关系(多对一),联系人就能在保存的时候维护外键5          */
    6         linkMan.setCustomer(customer);

    再次进行改进

    1 linkMan.setCustomer(customer);//由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值)
    2         customer.getLinkMans().add(linkMan);//由于配置了一的一方到多的一方的关联关系(发送一条update语句)

    结果:会有一条多余的update语句 ,是由于一的一方会维护外键,发送update语句

    解决的办法:只需要在一的一方放弃外键维护权即可

    1 @OneToMany(mappedBy = "customer")//  mappedBy:对方配置关系的属性名称,如下橙色条
    2 private Set<LinkMan> linkMans = new HashSet<LinkMan>();对方配置关系的属性名称
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;
     

     删除说明:当在主表中设置了放弃外键维护权,导致从表中的外键无法修改,删除时就会提示主键正在被占用不能删除。    >> 真的想删除,则使用级联删除 (实际开发中,慎用!!  >> 一对多情况下)

    级联操作    >> 操作一个对象的同时操作他的关联对象

    1.  需要区分操作主体
    2.  需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
    3.  cascade(配置级联)

    级联添加

     案例:当我保存一个客户的同时保存联系人

     Customer

    1 @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)  //
    • CascadeType.ALL:所有
    • MERGE:更新
    • PERSIST:保存
    • REMOVE:删除
    2     private Set<LinkMan> linkMans = new HashSet<LinkMan>();
     1 @Test
     2 @Transactional //配置事务
     3 @Rollback(false) //不自动回滚
     4     public void testCascadeAdd() {
     5         //创建一个客户,创建一个联系人
     6         Customer customer = new Customer();
     7         customer.setCustname("百度级联添加");
     8 
     9         LinkMan linkMan = new LinkMan();
    10         linkMan.setLkmName("小李级联添加");
    11         
    12         linkMan.setCustomer(customer);//由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值)
    13         customer.getLinkMans().add(linkMan);//由于配置了一的一方到多的一方的关联关系(发送一条update语句)
    14 
    15         customerDao.save(customer);
    16     }

    级联删除

     案例:当我删除一个客户的同时删除此客户的所有联系人

    1 @Test
    2 @Transactional //配置事务
    3 @Rollback(false) //不自动回滚
    4     public void testRemove() {
    5         customerDao.delete(33l);
    6     }

       改进:根据客户名查询出id,再进行删除操作

     1 @Test
     2     @Transactional //配置事务
     3     @Rollback(false) //不自动回滚
     4     public void testRemove1() {
     5         Specification<Customer> spec=new Specification<Customer>() {
     6             public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
     7                 Path<Object> custname = root.get("custname");
     8                 Predicate predicate = criteriaBuilder.equal(custname, "百度级联添加");
     9                 return predicate;
    10             }};
    11         Customer customerDaoOne = customerDao.findOne(spec);
    12         Long custid = customerDaoOne.getCustid();
    13         customerDao.delete(custid);
    14     }

    多对多操作  案例:用户和角色

    1.  引入依赖坐标,导入实体类和xml文件,创建dao接口

    2.  User >> 配置用户和联系人之间的一对多关系

     1 package cn.itcast.domain;
     2 
     3 import lombok.Data;
     4 
     5 import javax.persistence.*;
     6 import java.util.HashSet;
     7 import java.util.Set;
     8 @Data
     9 @Entity
    10 @Table(name = "sys_user")
    11 public class User {
    12     @Id
    13     @GeneratedValue(strategy = GenerationType.IDENTITY)
    14     @Column(name="user_id")
    15     private Long userId;
    16     @Column(name="user_name")
    17     private String userName;
    18     @Column(name="age")
    19     private Integer age;
    20     /**
    21      * 配置用户到角色的多对多关系
    22      *      配置多对多的映射关系
    23      *          1.声明表关系的配置
    24      *              @ManyToMany(targetEntity = Role.class)  //多对多
    25      *                  targetEntity:代表对方的实体类字节码
    26      *          2.配置中间表(包含两个外键)
    27      *                @JoinTable
    28      *                  name : 中间表的名称
    29      *                  joinColumns:配置当前对象在中间表的外键
    30      *                      @JoinColumn的数组
    31      *                          name:外键名
    32      *                           referencedColumnName:参照的主表的主键名
    33      *                        inverseJoinColumns:配置对方对象在中间表的外键
    34      */
    35     @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    36     @JoinTable(name = "sys_user_role",
    37             //joinColumns,当前对象在中间表中的外键
    38             joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, name:外键名  referencedColumnName:参照的主表里的主键名
    39             //inverseJoinColumns,对方对象在中间表的外键
    40             inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}   name:外键名  referencedColumnName:参照的主表里的主键名
    41  ) 42 private Set<Role> roles = new HashSet<Role>(); 43 }

     3.  Role >> 配置用户和联系人之间的一对多关系

     1 package cn.itcast.domain;
     2 
     3 import lombok.Data;
     4 
     5 import javax.persistence.*;
     6 import java.util.HashSet;
     7 import java.util.Set;
     8 
     9 //@Data
    10 @Entity
    11 @Table(name = "sys_role")
    12 public class Role {
    13 
    14     @Id
    15     @GeneratedValue(strategy = GenerationType.IDENTITY)
    16     @Column(name = "role_id")
    17     private Long roleId;
    18     @Column(name = "role_name")
    19     private String roleName;
    20 
    21     /*@ManyToMany(targetEntity = User.class)
    22     @JoinTable(name = "sys_user_role",
    23             //joinColumns,当前对象在中间表中的外键
    24             joinColumns = {@JoinColumn(name = "sys_role_id", referencedColumnName = "role_id")},
    25             //inverseJoinColumns,对方对象在中间表的外键
    26             inverseJoinColumns = {@JoinColumn(name = "sys_user_id", referencedColumnName = "user_id")}
    27     )*/
    28 
    29     @ManyToMany(mappedBy = "roles")  //配置多表关系,被选择的一方放弃
    30     private Set<User> users = new HashSet<User>();
    31 
    32     public Long getRoleId() { return roleId; }
    33     public void setRoleId(Long roleId) { this.roleId = roleId; }
    34     public String getRoleName() { return roleName; }
    35     public void setRoleName(String roleName) { this.roleName = roleName; }
    36     public Set<User> getUsers() { return users; }
    37     public void setUsers(Set<User> users) { this.users = users; }
    38 }

    保存一个用户,保存一个角色

      Role

    1  @ManyToMany(mappedBy = "roles")  //配置多表关系,放弃维护权,被选择的一方放弃(因为角色被用户所选择)
    2  private Set<User> users = new HashSet<User>();
     1 @Test
     2 @Transactional
     3 @Rollback(false)
     4     
     5     public void testAdd() {
     6         User user = new User();
     7         user.setUserName("小马");
     8         user.setAge(21);
     9 
    10         Role role = new Role();
    11         role.setRoleName("tx程序员");
    12 
    13         //配置用户到角色关系,可以对中间表中的数据进行维护
    14         user.getRoles().add(role);
    15         //配置角色到用户关系
    16         role.getUsers().add(user);
    17 
    18         userDao.save(user);
    19         roleDao.save(role);
    20     }

     级联添加 / 级联删除(与一对多案例类似)

    1 这里只给出 级联删除案例
    2 @Transactional
    3     @Rollback(false)
    4     @Test
    5     public void testCaseCadeRemove() {
    6 User user = userDao.findOne(1l); 7 userDao.delete(user); 8 }

    多表的查询

    对象导航查询  :查询一个对象的同时,通过此对象查询他的关联对象

     使用 jpa-day03-onetomany 项目来演示

     1 @Test
     2     @Transactional    no Session错误! >>  因为所有操作并没有在一个事务中进行,所以需要添加事务注解
    3 public void testQuery1(){ 4 //查询id为6的客户 5 Customer customer = customerDao.getOne(6l); 6 //对象导航查询,此客户下的所有联系人 7 Set<LinkMan> linkMans = customer.getLinkMans(); 8 for (LinkMan linkMan : linkMans) { 9 System.out.println(linkMan); 10 } }

     默认使用的是延迟加载的形式查询的
      调用get方法并不会立即发送查询,而是在使用关联对象的时候才会差和讯延迟加载!
      修改配置,将延迟加载改为立即加载(GAGER)
        fetch (延迟加载),需要配置到多表映射关系的注解上

    LinkMan

    1 @ManyToOne(targetEntity = Customer.class,fetch = FetchType.LAZY)
    2     @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    3     private Customer customer;
    1 @Test
    2     @Transactional
    3     public void testQuery2(){
    4         LinkMan linkMan = linkManDao.findOne(1l);
    5         //对象导航查询所属的语句
    6         Customer customer = linkMan.getCustomer();
    7         System.out.println(customer);
    8     }
  • 相关阅读:
    学习中的坑
    友链
    CF1131E String Multiplication 题解
    CF438E The Child and Binary Tree 题解
    [WC2005]友好的生物题解
    [IOI2016]shortcut 题解
    CF911F [Tree Destruction] 题解
    状压dp技巧之轮廓线 hdu1400/poj2411acwing291 蒙德里安的梦想
    TG-WC2021 笔记
    拯救世界2题解
  • 原文地址:https://www.cnblogs.com/xiaozhongfeixiang/p/11483513.html
Copyright © 2011-2022 走看看