SpringData JPA多表关联操作

简介: SpringData JPA多表关联操作

1 关联映射操作

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

1.1.1 @OneToMany:

作用:建立一对多的关系映射
属性:
  targetEntityClass:指定多的多方的类的字节码
  mappedBy:指定从表实体类中引用主表对象的名称。
  cascade:指定要使用的级联操作
  fetch:指定是否采用延迟加载
  orphanRemoval:是否使用孤儿删除

1.1.2 @ManyToOne

作用:建立多对一的关系
属性:
  targetEntityClass:指定一的一方实体类字节码
  cascade:指定要使用的级联操作
  fetch:指定是否采用延迟加载
  optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

1.1.3 @JoinColumn

 作用:用于定义主键字段和外键字段的对应关系。
 属性:
  name:指定外键字段的名称
  referencedColumnName:指定引用主表的主键字段名称
  unique:是否唯一。默认值不唯一
  nullable:是否允许为空。默认值允许。
  insertable:是否允许插入。默认值允许。
  updatable:是否允许更新。默认值允许。
  columnDefinition:列的定义信息。

56d44dfbc13948588f08597e01e5908e.png

表关系
  一对一:和一对多同理,就是看哪一方要维护外键.谁维护都行,主要是分清楚谁是主表,谁是从表
  一对多:
    一的一方:主表
    多的一方:从表
    外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
  多对多:
    中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键
讲师对学员:一对多关系
实体类中的关系
  包含关系:可以通过实体类中的包含关系描述表关系
  继承关系
分析步骤
  1.明确表关系
  2.确定表关系(描述 外键|中间表)
  3.编写实体类,再实体类中描述表关系(包含关系)
  4.配置映射关系

1.2 一对多的关联关系

i.一对多操作
  案例:客户和联系人的案例(一对多关系)
    客户:一家公司
    联系人:这家公司的员工
    一个客户可以具有多个联系人
    一个联系人从属于一家公司
  分析步骤
    1.明确表关系
      一对多关系
    2.确定表关系(描述 外键|中间表)
      主表:客户表
      从表:联系人表
        * 再从表上添加外键
    3.编写实体类,再实体类中描述表关系(包含关系)
      客户:再客户的实体类中包含一个联系人的集合
      联系人:在联系人的实体类中包含一个客户的对象
    4.配置映射关系
      * 使用jpa注解配置一对多映射关系
  级联:
    操作一个对象的同时操作他的关联对象
    级联操作:
      1.需要区分操作主体
      2.需要在操作主体的实体类上,添加级联属性(需要添加到多表映射关系的注解上)
      3.cascade(配置级联)
    级联添加,
      案例:当我保存一个客户的同时保存联系人
    级联删除
      案例:当我删除一个客户的同时删除此客户的所有联系人

2、实体类

package cn.oldlu.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:外键字段名称 一般有这个的时候mysql会自动创建字段,不需要重复定义
     *                  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 + '\'' +
                '}';
    }
}
package cn.oldlu.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 + '\'' +
                '}';
    }
}

3、dao层接口编写

package cn.oldlu.dao;
import cn.oldlu.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> {
}
package cn.oldlu.dao;
import cn.oldlu.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> {
}

4、测试查询

package cn.oldlu.test;
import cn.oldlu.dao.CustomerDao;
import cn.oldlu.dao.LinkManDao;
import cn.oldlu.domain.Customer;
import cn.oldlu.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);
    }
}

5.测试删改查

package cn.oldlu.test;
import cn.oldlu.dao.CustomerDao;
import cn.oldlu.dao.LinkManDao;
import cn.oldlu.domain.Customer;
import cn.oldlu.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.3 多对多的关联关系

注解说明:

1.3.1 @ManyToMany

作用:用于映射多对多关系
属性:
  cascade:配置级联操作。
  fetch:配置是否采用延迟加载。
  targetEntity:配置目标的实体类。映射多对多的时候不用写。

1.3.2 @JoinTable

作用:针对中间表的配置
属性:
  nam:配置中间表的名称
  joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段              
  inverseJoinColumn:中间表的外键字段关联对方表的主键字段

1.3.3 @JoinColumn

作用:用于定义主键字段和外键字段的对应关系。
属性:
  name:指定外键字段的名称
  referencedColumnName:指定引用主表的主键字段名称
  unique:是否唯一。默认值不唯一
  nullable:是否允许为空。默认值允许。
  insertable:是否允许插入。默认值允许。
  updatable:是否允许更新。默认值允许。
  columnDefinition:列的定义信息。
48ead7176a36407b8bfa6d6aa4de93cb.png
ii.多对多操作
  案例:用户和角色(多对多关系)
    用户:
    角色:
  分析步骤
    1.明确表关系
      多对多关系
    2.确定表关系(描述 外键|中间表)
      中间间表
    3.编写实体类,再实体类中描述表关系(包含关系)
      用户:包含角色的集合
      角色:包含用户的集合
    4.配置映射关系
iii.多表的查询
  1.对象导航查询
    查询一个对象的同时,通过此对象查询他的关联对象
    案例:客户和联系人
    从一方查询多方
      * 默认:使用延迟加载(****)
    从多方查询一方
      * 默认:使用立即加载

6、实体类

package cn.oldlu.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;
    }
}
package cn.oldlu.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;
    }
}

7、dao层接口

package cn.oldlu.dao;
import cn.oldlu.domain.Role;
import cn.oldlu.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface RoleDao extends JpaRepository<Role,Long> ,JpaSpecificationExecutor<Role> {
}
}
package cn.oldlu.dao;
import cn.oldlu.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
public interface UserDao extends JpaRepository<User,Long> ,JpaSpecificationExecutor<User> {
}

8、测试

package cn.oldlu.test;
import cn.oldlu.dao.RoleDao;
import cn.oldlu.dao.UserDao;
import cn.oldlu.domain.Role;
import cn.oldlu.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);
    }
}

2 对象导航查询

对象图导航检索方式是根据已经加载的对象,导航到他的关联对象。它利用类与类之间的关系来检索对象。例如:我们通过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);
    }
  }

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

@Autowired
  private LinkManDao linkManDao;
  @Test
  public void testFind() {
    LinkMan linkMan = linkManDao.findOne(4l);
    Customer customer = linkMan.getCustomer(); //对象导航查询
    System.out.println(customer);
  }

对象导航查询的问题分析


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


分析:如果我们不查的话,在用的时候还要自己写代码,调用方法去查询。如果我们查出来的,不使用时又会白白的浪费了服务器内存。


解决:采用延迟加载的思想。通过配置的方式来设定当我们在需要使用时,发起真正的查询。


配置方式:

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

问题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;

3 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);
    }
  }

4 SQL方式查询

    @Query(value = "SELECT\n" +
            "\tt_internet_scheduling.*\n" +
            "FROM\n" +
            "\tt_internet_member\n" +
            "\tRIGHT JOIN t_internet_scheduling ON t_internet_member.schedule_id = t_internet_scheduling.id \n" +
            "WHERE\n" +
            "\t( CASE WHEN :phone IS NOT NULL THEN phone= :phone ELSE 1 <> 1 END " +
            "OR CASE WHEN :idNum IS NOT NULL THEN id_num= :idNum ELSE 1 <> 1 END)AND CASE WHEN :caseNo IS NOT NULL THEN case_no like :caseNo ELSE 1 = 1 END\n" +
            "AND CASE WHEN :isOpen IS NOT NULL THEN is_open= :isOpen ELSE 1 = 1 END  ORDER BY start_time DESC\n ",
            countQuery = "SELECT COUNT(*) FROM t_internet_member\n" +
                    "            RIGHT JOIN t_internet_scheduling ON t_internet_member.schedule_id = t_internet_scheduling.id \n" +
                    "            WHERE\n" +
                    "            ( CASE WHEN :phone IS NOT NULL THEN phone= :phone ELSE 1 <> 1 END \n" +
                    "            OR CASE WHEN :idNum IS NOT NULL THEN id_num= :idNum ELSE 1 <> 1 END)AND CASE WHEN :caseNo IS NOT NULL THEN case_no like :caseNo ELSE 1 = 1 END\n" +
                    "          AND CASE WHEN :isOpen IS NOT NULL THEN is_open= :isOpen ELSE 1 = 1 END  ORDER BY start_time DESC"
            , nativeQuery = true)
    Page<InternetScheduling> findByIdCardAndPhone(@Param("idNum") String idNum, @Param("phone") String phone, @Param("caseNo") String caseNo,@Param("isOpen") String isOpen,Pageable pageable);



目录
相关文章
|
SQL Java 数据库
Springboot整合JPA 多表关联操作 @Query
Springboot整合JPA 多表关联操作 @Query
865 0
Springboot整合JPA 多表关联操作 @Query
|
6月前
|
SQL
MyBatis-Plus-Join关联查询
MyBatis-Plus-Join关联查询
304 2
|
5月前
|
SQL 开发框架 .NET
【Entity Framework】聊聊单个查询与拆分查询
【Entity Framework】聊聊单个查询与拆分查询
37 0
|
6月前
|
NoSQL MongoDB
使用MongoTemplate 对 mongodb数据进行分组、排序、分页、连表查询
使用MongoTemplate 对 mongodb数据进行分组、排序、分页、连表查询
|
6月前
|
SQL Java Spring
MybatisPlus-条件查询方式及多条件构建查询
MybatisPlus-条件查询方式及多条件构建查询
872 0
|
11月前
|
SQL Java
Springboot 实现 INNER JOIN数据表内连接
本文主要讲解如何使用springboot实现内连接两张表的操作.
76 0
|
SQL 数据库 C++
Jimmer VS MyBatisPlus查询自关联表
对象抓取器是 jimmer-sql 一个非常强大的特征,具备可媲美 GraphQL 的能力。 即使用户不采用任何 GraphQL 相关的技术栈,也能在 SQL 查询层面得到和 GraphQL 相似的对象图查询能力。
100 0
|
SQL Java 数据库连接
MyBatis实现关联表查询
MyBatis实现关联表查询
122 0
|
SQL XML Java
15min快速掌握Mybatis 多表操作 & 查询过程 & 查询结果
15分钟快速掌握Mybatis中的多表操作(一对一,一对多,多对多), 查询过程, 查询结果.以及学习和关键属性的应用👍👍👍
353 0
|
SQL Java 数据库连接
hibernate 根据某一列数据去重
hibernate 根据某一列数据去重