• SpringDataJpa(三)


    七、Specifications动态查询

    有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象

    import java.util.List;
    
    import org.springframework.data.domain.Page;
    import org.springframework.data.domain.Pageable;
    import org.springframework.data.domain.Sort;
    import org.springframework.data.jpa.domain.Specification;
    
    /**
     *	JpaSpecificationExecutor中定义的方法
     **/
     public interface JpaSpecificationExecutor<T> {
       	//根据条件查询一个对象
     	T findOne(Specification<T> spec);	
       	//根据条件查询集合
     	List<T> findAll(Specification<T> spec);
       	//根据条件分页查询
     	Page<T> findAll(Specification<T> spec, Pageable pageable);
       	//排序查询查询
     	List<T> findAll(Specification<T> spec, Sort sort);
       	//统计查询
     	long count(Specification<T> spec);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件。

    Specification接口中只定义了如下一个方法:

        //构造查询条件
        /**
        *	root	:Root接口,代表查询的根对象,可以通过root获取实体中的属性
        *	query	:代表一个顶层查询对象,用来自定义查询
        *	cb		:用来构建查询,此对象里有很多条件方法
        **/
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7.1 基于Specifications实现查询

    代码结构

    在这里插入图片描述

    dao、domain、applicationContext.xml、pom.xml不变,延用上面部分。

    对test文件夹编写代码

    7.1.1 条件查询

    test/SpecTest.java

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class SpecTest {
    
        @Autowired
        private CustomerDao customerDao;
    
        /**
         * 根据条件,查询单个对象
         *
         */
        @Test
        public void testSpec() {
            //匿名内部类
            /**
             * 自定义查询条件
             *      1.实现Specification接口(提供泛型:查询的对象类型)
             *      2.实现toPredicate方法(构造查询条件)
             *      3.需要借助方法参数中的两个参数(
             *          root:获取需要查询的对象属性
             *          CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
             *       )
             *  案例:根据客户名称查询,查询客户名为传智播客的客户
             *          查询条件
             *              1.查询方式
             *                  cb对象
             *              2.比较的属性名称
             *                  root对象
             *
             */
            Specification<Customer> spec = new Specification<Customer>() {
                @Override
                public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    //1.获取比较的属性
                    Path<Object> custName = root.get("custId");
                    //2.构造查询条件  :    select * from cst_customer where cust_name = '传智播客'
                    /**
                     * 第一个参数:需要比较的属性(path对象)
                     * 第二个参数:当前需要比较的取值
                     */
                    Predicate predicate = cb.equal(custName, "传智播客");//进行精准的匹配  (比较的属性,比较的属性的取值)
                    return predicate;
                }
            };
            Customer customer = customerDao.findOne(spec);
            System.out.println(customer);
        }
    
        /**
         * 多条件查询
         *      案例:根据客户名(传智播客)和客户所属行业查询(it教育)
         *
         */
        @Test
        public void testSpec1() {
            /**
             *  root:获取属性
             *      客户名
             *      所属行业
             *  cb:构造查询
             *      1.构造客户名的精准匹配查询
             *      2.构造所属行业的精准匹配查询
             *      3.将以上两个查询联系起来
             */
            Specification<Customer> spec = new Specification<Customer>() {
                @Override
                public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    Path<Object> custName = root.get("custName");//客户名
                    Path<Object> custIndustry = root.get("custIndustry");//所属行业
    
                    //构造查询
                    //1.构造客户名的精准匹配查询
                    Predicate p1 = cb.equal(custName, "传智播客");//第一个参数,path(属性),第二个参数,属性的取值
                    //2..构造所属行业的精准匹配查询
                    Predicate p2 = cb.equal(custIndustry, "it教育");
                    //3.将多个查询条件组合到一起:组合(满足条件一并且满足条件二:与关系,满足条件一或满足条件二即可:或关系)
                    Predicate and = cb.and(p1, p2);//以与的形式拼接多个查询条件
                    // cb.or();//以或的形式拼接多个查询条件
                    return and;
                }
            };
            Customer customer = customerDao.findOne(spec);
            System.out.println(customer);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    7.1.2 模糊查询

        /**
         * 案例:完成根据客户名称的模糊匹配,返回客户列表
         *      客户名称以 ’传智播客‘ 开头
         *
         * equal :直接的到path对象(属性),然后进行比较即可
         * gt,lt,ge,le,like : 得到path对象,根据path指定比较的参数类型,再去进行比较
         *      指定参数类型:path.as(类型的字节码对象)
         */
        @Test
        public void testSpec3() {
            //构造查询条件
            Specification<Customer> spec = new Specification<Customer>() {
                @Override
                public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                    //查询属性:客户名
                    Path<Object> custName = root.get("custName");
                    //查询方式:模糊匹配
                    Predicate like = cb.like(custName.as(String.class), "传智播客%");
                    return like;
                }
            };
    //        List list = customerDao.findAll(spec);
    //        for (Customer customer : list) {
    //            System.out.println(customer);
    //        }
            //添加排序
            //创建排序对象,需要调用构造方法实例化sort对象
            //第一个参数:排序的顺序(倒序,正序)
            //   Sort.Direction.DESC:倒序
            //   Sort.Direction.ASC : 升序
            //第二个参数:排序的属性名称
            Sort sort = new Sort(Sort.Direction.DESC,"custId");
            List<Customer> list = customerDao.findAll(spec, sort);
            for (Customer customer : list) {
                System.out.println(customer);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    7.1.3 分页查询

    对于Spring Data JPA中的分页查询,是其内部自动实现的封装过程,返回的是一个Spring Data JPA提供的pageBean对象。其中的方法说明如下:

        /**
         * 分页查询
         *      Specification: 查询条件
         *      Pageable:分页参数
         *          分页参数:查询的页码,每页查询的条数
         *          findAll(Specification,Pageable):带有条件的分页
         *          findAll(Pageable):没有条件的分页
         *  返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,共条数)
         */
        @Test
        public void testSpec4() {
    
            Specification spec = null;
            //PageRequest对象是Pageable接口的实现类
            /**
             * 创建PageRequest的过程中,需要调用他的构造方法传入两个参数
             *      第一个参数:当前查询的页数(从0开始)
             *      第二个参数:每页查询的数量
             */
            Pageable pageable = new PageRequest(0,2);
            //分页查询
            Page<Customer> page = customerDao.findAll(null, pageable);
            System.out.println(page.getContent());         //得到数据集合列表
            System.out.println(page.getTotalElements());   //得到总条数
            System.out.println(page.getTotalPages());      //得到总页数
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    7.2 方法对应关系

    方法名称Sql对应关系
    equlefiled = value
    gt(greaterThan )filed > value
    lt(lessThan )filed < value
    ge(greaterThanOrEqualTo )filed >= value
    le( lessThanOrEqualTo)filed <= value
    notEqulefiled != value
    likefiled like value
    notLikefiled not like value

    八、多表设计

    8.1 表之间关系的划分

    数据库中多表之间存在着三种关系,如图所示。

    在这里插入图片描述

    明确:我们今天只涉及实际开发中常用的关联关系,一对多和多对多。而一对一的情况,在实际开发中几乎不用

    8.2 在JPA框架中表关系的分析步骤

    在实际开发中,我们数据库的表难免会有相互的关联关系,在操作表的时候就有可能会涉及到多张表的操作。而在这种实现了ORM思想的框架中(如JPA),可以让我们通过操作实体类就实现对数据库表的操作。所以今天我们的学习重点是:掌握配置实体之间的关联关系。

    第一步:首先确定两张表之间的关系。

    ​ 如果关系确定错了,后面做的所有操作就都不可能正确。

    第二步:在数据库中实现两张表的关系

    第三步:在实体类中描述出两个实体的关系

    第四步:配置出实体类和数据库表的关系映射(重点)

    九、JPA中的一对多

    9.1 示例分析

    我们采用的示例为客户和联系人。

    客户:指的是一家公司,我们记为A。

    联系人:指的是A公司中的员工。

    在不考虑兼职的情况下,公司和员工的关系即为一对多。

    9.2 表关系建立

    在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束。

    外键:指的是从表中有一列,取值参照主表的主键,这一列就是外键。

    一对多数据库关系的建立,如下图所示

    在这里插入图片描述

    9.3 实体类关系建立以及映射配置

    代码结构如下

    在这里插入图片描述

    在实体类中,由于客户是少的一方,它应该包含多个联系人,所以实体类要体现出客户中有多个联系人的信息,代码如下:

    domain/Customer.java

    package cn.itcast.domain;
    
    import javax.persistence.*;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * 1.实体类和表的映射关系
     *      @Eitity
     *      @Table
     * 2.类中属性和表中字段的映射关系
     *      @Id
     *      @GeneratedValue
     *      @Column
     */
    @Entity
    @Table(name="cst_customer")
    public class Customer {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name="cust_id")
        private Long custId;
        @Column(name="cust_address")
        private String custAddress;
        @Column(name="cust_industry")
        private String custIndustry;
        @Column(name="cust_level")
        private String custLevel;
        @Column(name="cust_name")
        private String custName;
        @Column(name="cust_phone")
        private String custPhone;
        @Column(name="cust_source")
        private String custSource;
    
        //配置客户和联系人之间的关系(一对多关系)
        /**
         * 使用注解的形式配置多表关系
         *      1.声明关系
         *          @OneToMany : 配置一对多关系
         *              targetEntity :对方对象的字节码对象
         *      2.配置外键(中间表)
         *              @JoinColumn : 配置外键
         *                  name:外键字段名称
         *                  referencedColumnName:参照的主表的主键字段名称
         *
         *  * 在客户实体类上(一的一方)添加了外键了配置,所以对于客户而言,也具备了维护外键的作用
         *
         */
    
    //    @OneToMany(targetEntity = LinkMan.class)
    //    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
        /**
         * 放弃外键维护权
         *      mappedBy:对方配置关系的属性名称\
         * cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
         *      CascadeType.all         : 所有
         *                  MERGE       :更新
         *                  PERSIST     :保存
         *                  REMOVE      :删除
         *
         * fetch : 配置关联对象的加载方式
         *          EAGER   :立即加载
         *          LAZY    :延迟加载
    
          */
        @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
        private Set<LinkMan> linkMans = new HashSet<>();
    
        public Long getCustId() {
            return custId;
        }
    
        public void setCustId(Long custId) {
            this.custId = custId;
        }
    
        public String getCustAddress() {
            return custAddress;
        }
    
        public void setCustAddress(String custAddress) {
            this.custAddress = custAddress;
        }
    
        public String getCustIndustry() {
            return custIndustry;
        }
    
        public void setCustIndustry(String custIndustry) {
            this.custIndustry = custIndustry;
        }
    
        public String getCustLevel() {
            return custLevel;
        }
    
        public void setCustLevel(String custLevel) {
            this.custLevel = custLevel;
        }
    
        public String getCustName() {
            return custName;
        }
    
        public void setCustName(String custName) {
            this.custName = custName;
        }
    
        public String getCustPhone() {
            return custPhone;
        }
    
        public void setCustPhone(String custPhone) {
            this.custPhone = custPhone;
        }
    
        public String getCustSource() {
            return custSource;
        }
    
        public void setCustSource(String custSource) {
            this.custSource = custSource;
        }
    
        public Set<LinkMan> getLinkMans() {
            return linkMans;
        }
    
        public void setLinkMans(Set<LinkMan> linkMans) {
            this.linkMans = linkMans;
        }
    
        @Override
        public String toString() {
            return "Customer{" +
                    "custId=" + custId +
                    ", custAddress='" + custAddress + '\'' +
                    ", custIndustry='" + custIndustry + '\'' +
                    ", custLevel='" + custLevel + '\'' +
                    ", custName='" + custName + '\'' +
                    ", custPhone='" + custPhone + '\'' +
                    ", custSource='" + custSource + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147

    由于联系人是多的一方,在实体类中要体现出,每个联系人只能对应一个客户,代码如下:

    daomain/LinkMan.java

    package cn.itcast.domain;
    
    import javax.persistence.*;
    
    @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,fetch = FetchType.LAZY)
        @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
        private Customer customer;
    
        public Long getLkmId() {
            return lkmId;
        }
    
        public void setLkmId(Long lkmId) {
            this.lkmId = lkmId;
        }
    
        public String getLkmName() {
            return lkmName;
        }
    
        public void setLkmName(String lkmName) {
            this.lkmName = lkmName;
        }
    
        public String getLkmGender() {
            return lkmGender;
        }
    
        public void setLkmGender(String lkmGender) {
            this.lkmGender = lkmGender;
        }
    
        public String getLkmPhone() {
            return lkmPhone;
        }
    
        public void setLkmPhone(String lkmPhone) {
            this.lkmPhone = lkmPhone;
        }
    
        public String getLkmMobile() {
            return lkmMobile;
        }
    
        public void setLkmMobile(String lkmMobile) {
            this.lkmMobile = lkmMobile;
        }
    
        public String getLkmEmail() {
            return lkmEmail;
        }
    
        public void setLkmEmail(String lkmEmail) {
            this.lkmEmail = lkmEmail;
        }
    
        public String getLkmPosition() {
            return lkmPosition;
        }
    
        public void setLkmPosition(String lkmPosition) {
            this.lkmPosition = lkmPosition;
        }
    
        public String getLkmMemo() {
            return lkmMemo;
        }
    
        public void setLkmMemo(String lkmMemo) {
            this.lkmMemo = lkmMemo;
        }
    
        public Customer getCustomer() {
            return customer;
        }
    
        public void setCustomer(Customer customer) {
            this.customer = customer;
        }
    
        @Override
        public String toString() {
            return "LinkMan{" +
                    "lkmId=" + lkmId +
                    ", lkmName='" + lkmName + '\'' +
                    ", lkmGender='" + lkmGender + '\'' +
                    ", lkmPhone='" + lkmPhone + '\'' +
                    ", lkmMobile='" + lkmMobile + '\'' +
                    ", lkmEmail='" + lkmEmail + '\'' +
                    ", lkmPosition='" + lkmPosition + '\'' +
                    ", lkmMemo='" + lkmMemo + '\'' +
                    '}';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128

    do/CustomerDao.java

    package cn.itcast.dao;
    
    import cn.itcast.domain.Customer;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    
    import java.util.List;
    
    /**
     * 符合SpringDataJpa的dao层接口规范
     *      JpaRepository<操作的实体类类型,实体类中主键属性的类型>
     *          * 封装了基本CRUD操作
     *      JpaSpecificationExecutor<操作的实体类类型>
     *          * 封装了复杂查询(分页)
     */
    public interface CustomerDao extends JpaRepository<Customer,Long> ,JpaSpecificationExecutor<Customer> {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    do/LinkeManDao.java

    package cn.itcast.dao;
    
    import cn.itcast.domain.LinkMan;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    
    /**
     * 联系人的dao接口
     */
    public interface LinkManDao extends JpaRepository<LinkMan,Long>, JpaSpecificationExecutor<LinkMan> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    9.4 映射的注解说明

    @OneToMany:建立一对多的关系映射

    ​ 属性:

    ​ targetEntityClass:指定多的多方的类的字节码

    mappedBy:指定从表实体类中引用主表对象的名称

    cascade:指定要使用的级联操作

    ​ fetch:指定是否采用延迟加载

    ​ orphanRemoval:是否使用孤儿删除

    @ManyToOne:建立多对一的关系

    ​ 属性:

    targetEntityClass:指定一的一方实体类字节码

    ​ cascade:指定要使用的级联操作

    fetch:指定是否采用延迟加载

    ​ optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

    @JoinColumn:用于定义主键字段和外键字段的对应关系。

    ​ 属性:

    name:指定外键字段的名称

    referencedColumnName:指定引用主表的主键字段名称

    ​ unique:是否唯一。默认值不唯一

    ​ nullable:是否允许为空。默认值允许。

    ​ insertable:是否允许插入。默认值允许。

    ​ updatable:是否允许更新。默认值允许。

    ​ columnDefinition:列的定义信息。

    9.5 一对多的操作

    9.5.1 添加

    test/OneToManyTest.java

    package cn.itcast.test;
    
    import cn.itcast.dao.CustomerDao;
    import cn.itcast.dao.LinkManDao;
    import cn.itcast.domain.Customer;
    import cn.itcast.domain.LinkMan;
    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;
    
    @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);
    
    
            customerDao.save(customer);
            linkManDao.save(linkMan);
        }
    
    
        @Test
        @Transactional //配置事务
        @Rollback(false) //不自动回滚
        public void testAdd1() {
            //创建一个客户,创建一个联系人
            Customer customer = new Customer();
            customer.setCustName("百度");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李");
    
            /**
             * 配置联系人到客户的关系(多对一)
             *    只发送了两条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);
        }
    
        /**
         * 级联添加:保存一个客户的同时,保存客户的所有联系人
         *      需要在操作主体的实体类上,配置casacde属性
         */
        @Test
        @Transactional //配置事务
        @Rollback(false) //不自动回滚
        public void testCascadeAdd() {
            Customer customer = new Customer();
            customer.setCustName("百度1");
    
            LinkMan linkMan = new LinkMan();
            linkMan.setLkmName("小李1");
    
            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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139

    通过保存的案例,我们可以发现在设置了双向关系之后,会发送两条insert语句,一条多余的update语句,那我们的解决是思路很简单,就是一的一方放弃维护权

    Customer.java

    	/**
    	 *放弃外键维护权的配置将如下配置改为
    	 */
        //@OneToMany(targetEntity=LinkMan.class)
        //@JoinColumn(name="lkm_cust_id",referencedColumnName="cust_id")	
        //设置为
    	@OneToMany(mappedBy="customer")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    9.5.2 删除

    	@Autowired
    	private CustomerDao customerDao;
    	
    	@Test
    	@Transactional
    	@Rollback(false)//设置为不回滚
    	public void testDelete() {
    		customerDao.delete(1l);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    删除操作的说明如下:

    删除从表数据:可以随时任意删除。

    删除主表数据:

    • 有从表数据

      1、在默认情况下,它会把外键字段置为null,然后删除主表数据。如果在数据库的表 结构上,外键字段有非空约束,默认情况就会报错了。

      2、如果配置了放弃维护关联关系的权利,则不能删除(与外键字段是否允许为null, 没有关系)因为在删除时,它根本不会去更新从表的外键字段了。

      3、如果还想删除,使用级联删除引用

    • 没有从表数据引用:随便删

    在实际开发中,级联删除请慎用!(在一对多的情况下)

    9.5.3 级联操作

    级联操作:指操作一个对象同时操作它的关联对象

    使用方法:只需要在操作主体的注解上配置cascade

    	/**
    	 * cascade:配置级联操作
    	 * 		CascadeType.MERGE	级联更新
    	 * 		CascadeType.PERSIST	级联保存:
    	 * 		CascadeType.REFRESH 级联刷新:
    	 * 		CascadeType.REMOVE	级联删除:
    	 * 		CascadeType.ALL		包含所有
    	 */
    	@OneToMany(mappedBy="customer",cascade=CascadeType.ALL)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    test/ObjectQueryTest.java

    package cn.itcast.test;
    
    import cn.itcast.dao.CustomerDao;
    import cn.itcast.dao.LinkManDao;
    import cn.itcast.domain.Customer;
    import cn.itcast.domain.LinkMan;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.util.Set;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class ObjectQueryTest {
        @Autowired
        private CustomerDao customerDao;
    
        @Autowired
        private LinkManDao linkManDao;
    
        //could not initialize proxy - no Session
        //测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
        @Test
        @Transactional // 解决在java代码中的no session问题
        public void  testQuery1() {
            //查询id为1的客户
            Customer customer = customerDao.getOne(1l);
            //对象导航查询,此客户下的所有联系人
            Set<LinkMan> linkMans = customer.getLinkMans();
    
            for (LinkMan linkMan : linkMans) {
                System.out.println(linkMan);
            }
        }
    
        /**
         * 对象导航查询:
         *      默认使用的是延迟加载的形式查询的
         *          调用get方法并不会立即发送查询,而是在使用关联对象的时候才会差和讯
         *      延迟加载!
         * 修改配置,将延迟加载改为立即加载
         *      fetch,需要配置到多表映射关系的注解上
         *
         */
    
        @Test
        @Transactional // 解决在java代码中的no session问题
        public void  testQuery2() {
            //查询id为1的客户
            Customer customer = customerDao.findOne(1l);
            //对象导航查询,此客户下的所有联系人
            Set<LinkMan> linkMans = customer.getLinkMans();
    
            System.out.println(linkMans.size());
        }
    
        /**
         * 从联系人对象导航查询他的所属客户
         *      * 默认 : 立即加载
         *  延迟加载:
         *
         */
        @Test
        @Transactional // 解决在java代码中的no session问题
        public void  testQuery3() {
            LinkMan linkMan = linkManDao.findOne(2l);
            //对象导航查询所属的客户
            Customer customer = linkMan.getCustomer();
            System.out.println(customer);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    十、JPA中的多对多

    10.1 示例分析

    我们采用的示例为用户和角色。
    用户:指的是咱们班的每一个同学。
    角色:指的是咱们班同学的身份信息。
    比如A同学,它是我的学生,其中有个身份就是学生,还是家里的孩子,那么他还有个身份是子女。
    同时B同学,它也具有学生和子女的身份。
    那么任何一个同学都可能具有多个身份。同时学生这个身份可以被多个同学所具有。
    所以我们说,用户和角色之间的关系是多对多。

    10.2 表关系建立

    多对多的表关系建立靠的是中间表,其中用户表和中间表的关系是一对多,角色表和中间表的关系也是一对多,如下图所示:
    在这里插入图片描述

    10.3 实体类关系建立以及映射配置

    代码结构如下:

    在这里插入图片描述

    一个用户可以具有多个角色,所以在用户实体类中应该包含多个角色的信息,代码如下:

    daomain/User.java

    package cn.itcast.domain;
    
    import javax.persistence.*;
    import java.util.HashSet;
    import java.util.Set;
    
    @Entity
    @Table(name = "sys_user")
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name="user_id")
        private Long userId;
        @Column(name="user_name")
        private String userName;
        @Column(name="age")
        private Integer age;
    
        /**
         * 配置用户到角色的多对多关系
         *      配置多对多的映射关系
         *          1.声明表关系的配置
         *              @ManyToMany(targetEntity = Role.class)  //多对多
         *                  targetEntity:代表对方的实体类字节码
         *          2.配置中间表(包含两个外键)
         *                @JoinTable
         *                  name : 中间表的名称
         *                  joinColumns:配置当前对象在中间表的外键
         *                      @JoinColumn的数组
         *                          name:外键名
         *                          referencedColumnName:参照的主表的主键名
         *                  inverseJoinColumns:配置对方对象在中间表的外键
         */
        @ManyToMany(targetEntity = Role.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<Role> roles = new HashSet<>();
    
        public Long getUserId() {
            return userId;
        }
    
        public void setUserId(Long userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Set<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(Set<Role> roles) {
            this.roles = roles;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    一个角色可以赋予多个用户,所以在角色实体类中应该包含多个用户的信息,代码如下:

    daomain/Role.java

    package cn.itcast.domain;
    
    import javax.persistence.*;
    import java.util.HashSet;
    import java.util.Set;
    
    @Entity
    @Table(name = "sys_role")
    public class Role {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "role_id")
        private Long roleId;
        @Column(name = "role_name")
        private String roleName;
    
        //配置多对多
        @ManyToMany(mappedBy = "roles")  //配置多表关系
        private Set<User> users = new HashSet<>();
    
        public Long getRoleId() {
            return roleId;
        }
    
        public void setRoleId(Long roleId) {
            this.roleId = roleId;
        }
    
        public String getRoleName() {
            return roleName;
        }
    
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
    
        public Set<User> getUsers() {
            return users;
        }
    
        public void setUsers(Set<User> users) {
            this.users = users;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    10.4 映射的注解说明

    @ManyToMany

    ​ 作用:用于映射多对多关系

    ​ 属性:

    cascade:配置级联操作。

    ​ fetch:配置是否采用延迟加载。

    targetEntity:配置目标的实体类。映射多对多的时候不用写。

    @JoinTable

    ​ 作用:针对中间表的配置

    ​ 属性:

    ​ nam:配置中间表的名称

    ​ joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段

    ​ inverseJoinColumn:中间表的外键字段关联对方表的主键字段

    @JoinColumn

    ​ 作用:用于定义主键字段和外键字段的对应关系。

    ​ 属性:

    ​ name:指定外键字段的名称

    ​ referencedColumnName:指定引用主表的主键字段名称

    ​ unique:是否唯一。默认值不唯一

    ​ nullable:是否允许为空。默认值允许。

    ​ insertable:是否允许插入。默认值允许。

    ​ updatable:是否允许更新。默认值允许。

    ​ columnDefinition:列的定义信息。

    10.5 多对多的操作

    10.5.1 保存

    test/ManyToManyTest.java

    package cn.itcast.test;
    
    import cn.itcast.dao.RoleDao;
    import cn.itcast.dao.UserDao;
    import cn.itcast.domain.Role;
    import cn.itcast.domain.User;
    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;
    
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:applicationContext.xml")
    public class ManyToManyTest {
    
        @Autowired
        private UserDao userDao;
        @Autowired
        private RoleDao roleDao;
    
        /**
         * 保存一个用户,保存一个角色
         *
         *  多对多放弃维护权:被动的一方放弃
         */
        @Test
        @Transactional
        @Rollback(false)
        public void  testAdd() {
            User user = new User();
            user.setUserName("小李");
    
            Role role = new Role();
            role.setRoleName("java程序员");
    
            //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
            user.getRoles().add(role);
    
            //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
            role.getUsers().add(user);
    
            userDao.save(user);
            roleDao.save(role);
        }
    
    
        //测试级联添加(保存一个用户的同时保存用户的关联角色)
        @Test
        @Transactional
        @Rollback(false)
        public void  testCasCadeAdd() {
            User user = new User();
            user.setUserName("小李");
    
            Role role = new Role();
            role.setRoleName("java程序员");
    
            //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
            user.getRoles().add(role);
    
            //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
            role.getUsers().add(user);
    
            userDao.save(user);
        }
    
        /**
         * 案例:删除id为1的用户,同时删除他的关联对象
         */
        @Test
        @Transactional
        @Rollback(false)
        public void  testCasCadeRemove() {
            //查询1号用户
            User user = userDao.findOne(1l);
            //删除1号用户
            userDao.delete(user);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    在多对多(保存)中,如果双向都设置关系,意味着双方都维护中间表,都会往中间表插入数据,中间表的2个字段又作为联合主键,所以报错,主键重复,解决保存失败的问题:只需要在任意一方放弃对中间表的维护权即可,推荐在被动的一方放弃,配置如下:

    	//放弃对中间表的维护权,解决保存中主键冲突的问题
    	@ManyToMany(mappedBy="roles")
    	private Set<SysUser> users = new HashSet<SysUser>(0);
    
    • 1
    • 2
    • 3

    十一 Spring Data JPA中的多表查询

    11.1 对象导航查询

    对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过ID查询方式查出一个客户,可以调用Customer类中的getLinkMans()方法来获取该客户的所有联系人。对象导航查询的使用要求是:两个对象之间必须存在关联关系。

    查询一个客户,获取该客户下的所有联系人

    	@Autowired
    	private CustomerDao customerDao;
    	
    	@Test
    	//由于是在java代码中测试,为了解决no session问题,将操作配置到同一个事务中
    	@Transactional 
    	public void testFind() {
    		Customer customer = customerDao.findOne(5l);
    		Set<LinkMan> linkMans = customer.getLinkMans();//对象导航查询
    		for(LinkMan linkMan : linkMans) {
      			System.out.println(linkMan);
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    查询一个联系人,获取该联系人的所有客户

    	@Autowired
    	private LinkManDao linkManDao;
    	
    	
    	@Test
    	public void testFind() {
    		LinkMan linkMan = linkManDao.findOne(4l);
    		Customer customer = linkMan.getCustomer(); //对象导航查询
    		System.out.println(customer);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    对象导航查询的问题分析

    问题1:我们查询客户时,要不要把联系人查询出来?

    分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。
    解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。
    配置方式:

    	/**
    	 * 在客户对象的@OneToMany注解中添加fetch属性
    	 * 		FetchType.EAGER	:立即加载
    	 * 		FetchType.LAZY	:延迟加载
    	 */
    	@OneToMany(mappedBy="customer",fetch=FetchType.EAGER)
    	private Set<LinkMan> linkMans = new HashSet<>(0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    问题2:我们查询联系人时,要不要把客户查询出来?
    分析:例如:查询联系人详情时,肯定会看看该联系人的所属客户。如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的话,一个对象不会消耗太多的内存。而且多数情况下我们都是要使用的。
    解决: 采用立即加载的思想。通过配置的方式来设定,只要查询从表实体,就把主表实体对象同时查出来
    配置方式

    	/**
    	 * 在联系人对象的@ManyToOne注解中添加fetch属性
    	 * 		FetchType.EAGER	:立即加载
    	 * 		FetchType.LAZY	:延迟加载
    	 */
    	@ManyToOne(targetEntity=Customer.class,fetch=FetchType.EAGER)
    	@JoinColumn(name="cst_lkm_id",referencedColumnName="cust_id")
    	private Customer customer;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    11.2 使用Specification查询

    	/**
    	 * Specification的多表查询
    	 */
    	@Test
    	public void testFind() {
    		Specification<LinkMan> spec = new Specification<LinkMan>() {
    			public Predicate toPredicate(Root<LinkMan> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    				//Join代表链接查询,通过root对象获取
    				//创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
    				//JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接
    				Join<LinkMan, Customer> join = root.join("customer",JoinType.INNER);
    				return cb.like(join.get("custName").as(String.class),"传智播客1");
    			}
    		};
    		List<LinkMan> list = linkManDao.findAll(spec);
    		for (LinkMan linkMan : list) {
    			System.out.println(linkMan);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    二维矩阵乘法的意义
    【Python自然语言处理】文本向量化的六种常见模型讲解(独热编码、词袋模型、词频-逆文档频率模型、N元模型、单词-向量模型、文档-向量模型)
    模拟退火算法
    PySpark之Python版本如何选择(详细版)
    Kruskal算法求最小生成树
    OpenAPI 3.0 规范-食用指南
    Java八股文(急速版)
    内存优化总结: ptmalloc、tcmalloc 和 jemalloc
    Kafka实时数据即席查询应用与实践
    【开发心得】微信网页应用授权登录
  • 原文地址:https://blog.csdn.net/qq_39236499/article/details/134288892