zoukankan      html  css  js  c++  java
  • 【Spring Data JPA】08 多表关系 Part1 一对多关系操作

    表关系概述:

    1、一 对应 一

    一对夫妻,一个男人只能有一个老婆,一个女人只能有一个老公,即这种对应关系

    2、一 对应 多 【多对一】

    一个年级具有多个班级,一个班级具有对应的所属年级,即这种上下层级关系或者其他类似的

    3、多 对应 多

    授课老师和所授课程 一个授课老师可以教授多门课程,一个课程也可以被多个老师教授。即多重关系

    数据库表设计的实现:

    一对一只需要一个关联键即可

    一对多,需要建立主表(一)从表(多)关系,从表设置外键来检索主表信息

    多对多,对应的关系无法用外键实现,所以需要建立关联表,用来存储关联关系映射

    实体类设计的实现:

    一对一,对象附属,作为属性存在【包含】

    一对多,对象仅表示一个记录,所以更改为一个对象的集合容器作为属性存在,或者使用继承实现

    多对多

    测试环境搭建:

    customer.sql

    /*创建客户表*/
    CREATE TABLE cst_customer (
      cust_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
      cust_name varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
      cust_source varchar(32) DEFAULT NULL COMMENT '客户信息来源',
      cust_industry varchar(32) DEFAULT NULL COMMENT '客户所属行业',
      cust_level varchar(32) DEFAULT NULL COMMENT '客户级别',
      cust_address varchar(128) DEFAULT NULL COMMENT '客户联系地址',
      cust_phone varchar(64) DEFAULT NULL COMMENT '客户联系电话',
      PRIMARY KEY (`cust_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8;
    
    /*创建联系人表*/
    CREATE TABLE cst_linkman (
      lkm_id bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
      lkm_name varchar(16) DEFAULT NULL COMMENT '联系人姓名',
      lkm_gender char(1) DEFAULT NULL COMMENT '联系人性别',
      lkm_phone varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
      lkm_mobile varchar(16) DEFAULT NULL COMMENT '联系人手机',
      lkm_email varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
      lkm_position varchar(16) DEFAULT NULL COMMENT '联系人职位',
      lkm_memo varchar(512) DEFAULT NULL COMMENT '联系人备注',
      lkm_cust_id bigint(32) NOT NULL COMMENT '客户id(外键)',
      PRIMARY KEY (`lkm_id`),
      KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
      CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

    客户:

    package cn.echo42.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import javax.persistence.*;
    import java.io.Serializable;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * @author DaiZhiZhou
     * @file Spring-Data-JPA
     * @create 2020-08-01 9:48
     */
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity//表示当前类是一个实体类
    @Table(name="cst_customer")//建立当前实体类和表之间的对应关系
    public class Customer implements Serializable {
    
        @Id//表明当前私有属性是主键
        @GeneratedValue(strategy = GenerationType.IDENTITY)//指定主键的生成策略
        @Column(name = "cust_id")//指定和数据库表中的cust_id列对应
        private Long custId;
        @Column(name = "cust_name")//指定和数据库表中的cust_name列对应
        private String custName;
        @Column(name = "cust_source")//指定和数据库表中的cust_source列对应
        private String custSource;
        @Column(name = "cust_industry")//指定和数据库表中的cust_industry列对应
        private String custIndustry;
        @Column(name = "cust_level")//指定和数据库表中的cust_level列对应
        private String custLevel;
        @Column(name = "cust_address")//指定和数据库表中的cust_address列对应
        private String custAddress;
        @Column(name = "cust_phone")//指定和数据库表中的cust_phone列对应
        private String custPhone;
    
        //配置客户和联系人的一对多关系
        @OneToMany(targetEntity = LinkMan.class)
        @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
        private Set<LinkMan> linkmans = new HashSet<LinkMan>(0);
    }

    联系人:

    package cn.echo42.domain;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import javax.persistence.*;
    import java.io.Serializable;
    
    /**
     * @author DaiZhiZhou
     * @file Spring-Data-JPA
     * @create 2020-08-01 9:49
     */
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Entity
    @Table(name="cst_linkman")
    public class LinkMan implements Serializable {
        @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;
    
        //多对一关系映射:多个联系人对应客户
        @ManyToOne(targetEntity=Customer.class)
        @JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")
        private Customer customer;//用它的主键,对应联系人表中的外键
    }

    编写对应的Dao接口:

    package cn.echo42.repository;
    
    import cn.echo42.domain.Customer;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    
    /**
     * @author DaiZhiZhou
     * @file Spring-Data-JPA
     * @create 2020-08-01 9:54
     */
    public interface CustomerRepository extends JpaRepository<Customer, Integer> , JpaSpecificationExecutor<Customer> {
    }
    ---------------------------------------------------------------------------------------------------------------------------
    package cn.echo42.repository;
    
    import cn.echo42.domain.LinkMan;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    
    /**
     * @author DaiZhiZhou
     * @file Spring-Data-JPA
     * @create 2020-08-01 9:54
     */
    public interface LinkManRepository extends JpaRepository<LinkMan, Integer>, JpaSpecificationExecutor<LinkMan> {
    }

    注解配置Jpa的配置信息

    自动创建数据库表设置:create每次执行创建数据库表,update没有表才会创建

    多表关系插入:

    创建测试类:

    import cn.echo42.config.ApplicationConfiguration;
    import cn.echo42.domain.Customer;
    import cn.echo42.domain.LinkMan;
    import cn.echo42.repository.CustomerRepository;
    import cn.echo42.repository.LinkManRepository;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.annotation.Rollback;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.transaction.annotation.Transactional;
    
    /**
     * @author DaiZhiZhou
     * @file Spring-Data-JPA
     * @create 2020-08-01 10:06
     */
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = ApplicationConfiguration.class)
    public class MTRQ_Test {
    
        @Autowired
        CustomerRepository customerRepository;
        @Autowired
        LinkManRepository linkManRepository;
    
        @Test
        @Transactional
        @Rollback(false) // 设置不自动回滚
        public void addExecute(){
            // 创建一个客户 & 联系人
            Customer customer = new Customer();
            customer.setCustName("百度");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李");
    
            customerRepository.save(customer);
            linkManRepository.save(linkMan);
        }
    
    }

    显示结果:

    [org.hibernate.SQL]-alter table cst_linkman drop foreign key FKh9yp1nql5227xxcopuxqx2e7q
    [org.hibernate.SQL]-drop table if exists cst_customer
    [org.hibernate.SQL]-drop table if exists cst_linkman
    [org.hibernate.SQL]-drop table if exists sys_user
    [org.hibernate.SQL]-create table cst_customer (cust_id bigint not null auto_increment, cust_address varchar(255), cust_industry varchar(255), cust_level varchar(255), cust_name varchar(255), cust_phone varchar(255), cust_source varchar(255), primary key (cust_id))
    [org.hibernate.SQL]-create table cst_linkman (lkm_id bigint not null auto_increment, lkm_email varchar(255), lkm_gender varchar(255), lkm_memo varchar(255), lkm_mobile varchar(255), lkm_name varchar(255), lkm_phone varchar(255), lkm_position varchar(255), lkm_cust_id bigint, primary key (lkm_id))
    [org.hibernate.SQL]-create table sys_user (user_id integer not null auto_increment, user_is_del integer, user_name varchar(255), user_password varchar(255), user_status integer, primary key (user_id))
    [org.hibernate.SQL]-alter table cst_linkman add constraint FKh9yp1nql5227xxcopuxqx2e7q foreign key (lkm_cust_id) references cst_customer (cust_id)

    数据库查看:

    可以看到联系人的外键这里没有百度,两个记录不具备任何联系,是相互独立的存在

    要建立关系,我们则需要在联系人的集合中增加对象,表现出我们的关联关系:

        @Test
        @Transactional
        @Rollback(false) // 设置不自动回滚
        public void addExecute(){
            // 创建一个客户 & 联系人
            Customer customer = new Customer();
            customer.setCustName("百度");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李");
            
            // 配置多表关系
            customer.getLinkmans().add(linkMan);
    
            customerRepository.save(customer);
            linkManRepository.save(linkMan);
        }

    再次执行的结果:

    可以看到已经成功绑定到了

    从客户表的角度上观察:

    发送了两条插入语句,

    一条插入记录

    一条更新外键

    我们配置了客户到联系人的关系,客户可以对外键进行维护

    我们也可以换过来,从联系人的角度上关联客户:

        @Test
        @Transactional
        @Rollback(false) // 设置不自动回滚
        public void addExecute2(){
            // 创建一个客户 & 联系人
            Customer customer = new Customer();
            customer.setCustName("百度");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李");
    
            // 在联系人的属性中设置客户,以关联
            linkMan.setCustomer(customer);
    
            customerRepository.save(customer);
            linkManRepository.save(linkMan);
        }

    结果一样

    但是如果同时绑定设置就会出错了:

        @Test
        @Transactional
        @Rollback(false) // 设置不自动回滚
        public void addExecute3(){
            // 创建一个客户 & 联系人
            Customer customer = new Customer();
            customer.setCustName("百度");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李");
    
            linkMan.setCustomer(customer);
            customer.getLinkmans().add(linkMan);
    
            customerRepository.save(customer);
            linkManRepository.save(linkMan);
        }

    双方彼此的对象在不断相互引用:

    解决方案是任意的一方放弃主键的维护即可:

    原先联系人类的维护关系:

    如果放弃,就需要这样设置:

    But it not work .... 不晓得为什么,难道又有新的策略了?

    【发现是使用了Lombok原因,这里也只要记住一点,放弃主键维护注意下Lombok辅助的问题】

    级联操作:

    ALL 所有操作
    
    MERGE 更新
    PERSIST 持久化 -> 保存
    REMOVE  移除

    取消之前设置的创建数据库表:

    测试级联删除:

        @Test
        @Transactional
        @Rollback(false) // 设置不自动回滚
        public void addExecute4(){
            Specification<Customer> customerSpecification = (Specification<Customer>) (root, criteriaQuery, criteriaBuilder) -> {
                // 获取比较的属性
                Path<Object> cust_id = root.get("custId");
                // 模糊要求指定参数类型
                return criteriaBuilder.equal(cust_id, 1);
            };
            Optional<Customer> customerOptional = customerRepository.findOne(customerSpecification);
    
            Customer customer = customerOptional.get();
    
            customerRepository.delete(customer);
        }
  • 相关阅读:
    JQuery源码解读 JQ框架简化( 妙味讲堂
    Mia Fringe官网会员须知
    require.js
    :before与::before的区别
    css----苹果移动端以及小程序滚动模块卡顿的处理
    Vue使用国密SM4加密
    vue + echarts + echarts-gl 实现3D 环状图
    React Hook 初学
    常用阻止默认行为的两种方式
    理解事件触发,事件捕获,事件冒泡
  • 原文地址:https://www.cnblogs.com/mindzone/p/13413451.html
Copyright © 2011-2022 走看看