• 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);
        }
  • 相关阅读:
    内网穿透之frp X
    python图形界面开发工具之PySimpleGUI X
    API 接口开发调试工具之ApiPost X
    分布式对象存储只FDFS X
    开发者工具 X
    Tornado框架之应用安全(四) X
    MySQL数据库设计规范(新) X
    Tornado框架之项目部署(六) X
    PHP之soap X
    python命令行工具之Click X
  • 原文地址:https://www.cnblogs.com/roadlandscape/p/12378696.html
Copyright © 2020-2023  润新知