Spring data Jpa 学习第三天

本文概述了Spring Data JPA与JPA规范的关系,介绍了如何编写符合规范的DAO接口,涉及动态查询、Specifications的使用,以及一对一、一对多和多对多关系的数据库操作,包括级联操作和对象导航查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

回顾
  

 i.springDatajpa,jpa规范,hibernate三者之间的关系
        code  -- > springDatajpa  --> jpa规范的API --> hibernate
    ii.符合springDataJpa规范的dao层接口的编写规则
        1.需要实现两个接口(JpaRepository,JapSpecificationExecutor)
        2.提供响应的泛型
    iii.运行过程
        * 动态代理的方式:动态代理对象
    iiii.查询

第一 Specifications动态查询

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

 JpaSpecificationExecutor 方法列表
    
        T findOne(Specification<T> spec);  //查询单个对象

        List<T> findAll(Specification<T> spec);  //查询列表

        //查询全部,分页
        //pageable:分页参数
        //返回值:分页pageBean(page:是springdatajpa提供的)
        Page<T> findAll(Specification<T> spec, Pageable pageable);

        //查询列表
        //Sort:排序参数
        List<T> findAll(Specification<T> spec, Sort sort);

        long count(Specification<T> spec);//统计查询
        
    * Specification :查询条件

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


        自定义我们自己的Specification实现类
            实现
                //root:查询的根对象(查询的任何属性都可以从根对象中获取)
                //CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
                //CriteriaBuilder:查询的构造器,封装了很多的查询条件
                Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb); //封装查询条件

package com.test;

import com.test.dao.CustomerDao;
import com.test.domain.Customer;
import javassist.runtime.Desc;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.persistence.criteria.*;
import java.util.List;

/**
 * @author 琴宝宝
 * @version V1.0
 * @Package com.test
 * @date 2022/3/28 16:26
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpecTest {

    @Autowired
    private CustomerDao customerDao;
    /**
     * 根据条件,查询单个对象
     */
    @Test
    public void testSpec(){
       Specification<Customer> spec=new Specification<Customer>() {
           //匿名内部类
           /**
            * 自定义查询条件
            *      1.实现Specification接口(提供泛型:查询的对象类型)
            *      2.实现toPredicate方法(构造查询条件)
            *      3.需要借助方法参数中的两个参数(
            *          root:获取需要查询的对象属性
            *          CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配)
            *       )
            *  案例:根据客户名称查询,查询客户名为啦啦啦1的客户
            *          查询条件
            *              1.查询方式
            *                  cb对象
            *              2.比较的属性名称
            *                  root对象
            */

           public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
               Path<Object> custName = root.get("custName");
               Predicate equal = criteriaBuilder.equal(custName, "啦啦啦1");
               return equal;
           }
       };

        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }
    /**
     * 多条件查询
     *      案例:根据客户名和客户所属行业查询
     */
    @Test
    public void testSpec1(){
        Specification<Customer> spec=new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                Path<Object> custName = root.get("custName");
                Path<Object> custIndustry = root.get("custIndustry");

                Predicate p1 = cb.equal(custName, "啦啦啦1");
                Predicate p2 = cb.equal(custIndustry, "IT");
                Predicate and = cb.and(p1, p2);
                return and;
            }
        };

        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }

    /**
     * 模糊查询
     */
    @Test
    public void testLike(){
        Specification<Customer> spec =new Specification<Customer>() {
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Path<Object> custName = root.get("custName");
                Predicate like = criteriaBuilder.like(custName.as(String.class), "啦啦啦%");
                return like;
            }
        };

        Sort sort=new Sort(Sort.Direction.DESC,"custId");
        List<Customer> l = customerDao.findAll(spec,sort);
        for (Customer customer : l) {
            System.out.println(customer);
        }
    }
    /**
     * 分页查询
     */
    @Test
    public void testPage(){
        Specification<Customer> spec=null;
        Pageable page=new PageRequest(0,3);

        Page<Customer> list = customerDao.findAll(spec, page);
        System.out.println(list.getTotalElements());
        System.out.println(list.getTotalPages());
        System.out.println(list.getContent());
    }
}
    • 方法对应关系

方法名称

Sql对应关系

equle

filed = value

gt(greaterThan )

filed > value

lt(lessThan )

filed < value

ge(greaterThanOrEqualTo )

filed >= value

le( lessThanOrEqualTo)

filed <= value

notEqule

filed != value

like

filed like value

notLike

filed not like value


        

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

  

 表关系
        一对一
        一对多:
            一的一方:主表
            多的一方:从表
            外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
        多对多:
            中间表:中间表中最少应该由两个字段组成,这两个字段做为外键指向两张表的主键,又组成了联合主键

    讲师对学员:一对多关系
            
    实体类中的关系
        包含关系:可以通过实体类中的包含关系描述表关系
        继承关系
    
    分析步骤
        1.明确表关系
        2.确定表关系(描述 外键|中间表)
        3.编写实体类,再实体类中描述表关系(包含关系)
        4.配置映射关系

映射的注解说明

@OneToMany:

     作用:建立一对多的关系映射

    属性:

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

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

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

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

             orphanRemoval:是否使用孤儿删除

@ManyToOne

    作用:建立多对一的关系

    属性:

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

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

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

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

@JoinColumn

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

     属性:

             name:指定外键字段的名称

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

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

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

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

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

             columnDefinition:列的定义信息。

package com.test;

import com.test.dao.CustomerDao;
import com.test.dao.LinkManDao;
import com.test.domain.Customer;
import com.test.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;

/**
 * @author 琴宝宝
 * @version V1.0
 * @Package com.test
 * @date 2022/4/18 22:04
 */
@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 save(){
        Customer customer=new Customer();
        customer.setCustName("百度");


        LinkMan linkMan=new LinkMan();
        linkMan.setLkmName("张三");
        customer.getLinkMans().add(linkMan);

        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
    @Test
    @Transactional
    @Rollback(false)
    public void save1(){
        Customer customer=new Customer();
        customer.setCustName("百度");


        LinkMan linkMan=new LinkMan();
        linkMan.setLkmName("张三");
        linkMan.setCustomer(customer);

        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
    @Test
    @Transactional
    @Rollback(false)
    public void save2(){
        Customer customer=new Customer();
        customer.setCustName("百度");


        LinkMan linkMan=new LinkMan();
        linkMan.setLkmName("张三");
        customer.getLinkMans().add(linkMan);
        linkMan.setCustomer(customer);

        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
    @Test
    @Transactional
    @Rollback(false)
    public void cascadeTest(){
        Customer customer=new Customer();
        customer.setCustName("百度1");


        LinkMan linkMan=new LinkMan();
        linkMan.setLkmName("张三1");


        customerDao.save(customer);
        linkManDao.save(linkMan);
    }


    @Test
    @Transactional
    @Rollback(false)
    public void cascadeRemoveTest(){
        Customer customer = customerDao.findOne(2l);
        customerDao.delete(2l);

    }
}

第三 完成多表操作

    

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

映射的注解说明

@ManyToMany

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

         属性:

                  cascade:配置级联操作。

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

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

@JoinTable

    作用:针对中间表的配置

    属性:

             nam:配置中间表的名称

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

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

            

@JoinColumn

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

    属性:

             name:指定外键字段的名称

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

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

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

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

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

             columnDefinition:列的定义信息。


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

package test;

import com.test.dao.RoleDao;
import com.test.dao.UserDao;
import com.test.domain.Role;
import com.test.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;

/**
 * @author 琴宝宝
 * @version V1.0
 * @Package test
 * @date 2022/4/18 23:33
 */
@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 SaveTest(){
        User user=new User();
        user.setUserName("admin");
        Role role=new Role();
        role.setRoleName("System");

        user.getRoles().add(role);
        role.getUsers().add(user);
        userDao.save(user);
        roleDao.save(role);
    }
    @Test
    @Transactional
    @Rollback(false)
    public void RemoveTest(){
     userDao.findOne(1l);
     userDao.delete(1l);
    }
}

    
    iii.多表的查询
        1.对象导航查询
            查询一个对象的同时,通过此对象查询他的关联对象
            
            案例:客户和联系人
            
            从一方查询多方
                * 默认:使用延迟加载(****)
                
            从多方查询一方
                * 默认:使用立即加载
        
        

package com.test;

import com.test.dao.CustomerDao;
import com.test.dao.LinkManDao;
import com.test.domain.Customer;
import com.test.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;

import java.util.Set;

/**
 * @author 琴宝宝
 * @version V1.0
 * @Package com.test
 * @date 2022/4/18 22:04
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ObjectQueryTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
    @Test
    @Transactional
   public void Query1(){
        Set<LinkMan> linkMans = customerDao.getOne(1l).getLinkMans();
        for (LinkMan linkMan : linkMans) {
            System.out.println(linkMan);
        }
    }
    /**
     * 对象导航查询:
     *      默认使用的是延迟加载的形式查询的
     *          调用get方法并不会立即发送查询,而是在使用关联对象的时候才会差和讯
     *      延迟加载!
     * 修改配置,将延迟加载改为立即加载
     *      fetch,需要配置到多表映射关系的注解上
     *
     */
    @Test
    @Transactional
    public void Query2(){
        Set<LinkMan> linkMans = customerDao.getOne(1l).getLinkMans();
        for (LinkMan linkMan : linkMans) {
            System.out.println(linkMan);
        }
    }
    @Test
    @Transactional
    public void Query3(){
        LinkMan one = linkManDao.findOne(2l);
        System.out.println(one);
    }
}


        
        
        
        
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值