SpringDataJpa中的复杂查询、动态查询和多表查询。(保姆级教程)

在前面的章节已经讲述了SpringDataJpa的CRUD操作以及其底层代理实现的分析,下面介绍SpringDataJpa中的复杂查询和动态查询,多表查询。(保姆级教程)

文章字数较多,请各位按需阅读。😂

不清楚JPA的小伙伴可以参考这篇文章:JPA简介

不清楚SpringDataJPA环境搭建的小伙伴可以参考这篇文章:SpringDataJPA入门案例

想了解SpringDataJPA代理类实现过程可以参考这篇文章:SpringDadaJPA底层实现原理

如需转载,请注明出处。

1.复杂查询

i.方法名称规则查询

方法名查询:只需要按照SpringDataJpa提供的方法名称规则去定义方法,在dao接口中定义方法即可。

其中对于方法的名称有一套约定。

KeyWord Sample JPQL
And findByLastnameAndFirstname where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname where x.lastname = ?1 or x.firstname = ?2
Between findByAgeBetween where x.Age between ?1 and ?2
LessThan findByAgeLessThan where x.age < ?1
GreaterThan findByAgeGreaterThan where x.age > ?1
Like findByFirstnameLike where x.firstname like ?1
NotLike findByFirstnameNotLike where x.firstname not like ?1
TRUE findByActiveTrue() where x.active = true
FALSE findByActiveFalse() where x.active = false
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
   
    /**
     * 方法名的约定:
     *         findBy:查询
     *              对象中的属性名(首字母大写):查询条件
     *              *默认情况:使用 =的方式查询
     *              特殊的查询方式,比如模糊查询
     *         findByCustName-----根据客户名称查询   findBy表示要查询  CustName属性名
     *     springDataJpa在运行阶段
     *              会根据方法名称进行解析 findBy  from XXX(实体类)
     *                                          属性名称  where custName
     *     1. findBy+属性名称(根据属性名称进行完成匹配任务)
     *     2. findBy+属性名称+查询方式(Like|isnull)
     *     3. 多条件查询
     *          findBy+属性名称+查询方式+多条件连接符(and|or)+属性名+查询方式
     */
    public List<Customer> findByCustName(String name);
    //查询id为3且name中含有大学的用户
    public Customer findByCustId(Long id);
    public Customer findByCustIdAndCustNameLike(Long id,String name);
}

ii.JPQL查询

使用 Spring Data JPA 提供的查询方法已经可以解决大部分的应用场景,但是对于某些业务来
说,我们还需要灵活的构造查询条件,这时就可以使用@Query 注解,结合 JPQL 的语句方式完成
查询 。

@Query 注解的使用非常简单,只需在方法上面标注该注解,同时提供一个 JPQL 查询语句即可

注意:

通过使用 @Query 来执行一个更新操作,为此,我们需要在使用 @Query 的同时,用 @Modifying 来将该操作标识为修改查询,这样框架最终会生成一个更新的操作,而非查询 。

public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
   
    /**
     *  1.根据客户名称查询客户
     *  jpql:from Customer where custName=?
     */
    @Query(value="from Customer where custName =?")
    public List<Customer> findCustomerJpql(String name);
    /**
     * 2.根据客户名称和客户id查询
     * 对多个占位符参数
     *      默认情况下,占位符的位置需要和方法参数中的位置保持一致
     *  也可以指定占位符参数的位置(注意:中间不要有空格)
     *         ? 索引的方式,指定此占位符的取值来源  eg ?2表示此占位符对应第二个参数
     */
    @Query(value="from Customer where custName=?2 and custId=?1")
    public Customer findByNameAndId(Long id,String name);
    /**
     * 3.根据id更新客户的name
     * sql:update cst_customer set cust_name=? where cust_id=?
     * jpql:update Customer set custName=? where custId=?
     *
     * @query:代表的是进行查询
     *      需要声明此方法是执行更新操作
     *    使用 @Modifying
     */
    @Query(value = "update Customer set custName=? where custId=?")
    @Modifying
    public void updateCustomerName(String name,Long id);
}

注意:在执行springDataJpa中使用jpql完成更新,删除操作时,需要手动添加事务的支持 必须的;因为默认会执行结束后,回滚事务。

 @Test
    @Transactional//添加事务的支持
    @Rollback(value = false)
    public void updateCustomerName(){
   
        customerDao.updateCustomerName("学生公寓",4L);
    }

iii.SQL查询

Spring Data JPA 同样也支持 sql 语句的查询,如下:

/**
     * 查询所有用户:使用sql查询
     *  Sql:select * from cst_customer
     *  nativeQuery = true配置查询方式,true表示Sql查询,false表示Jpql查询
     *  注意:返回值是一个Object[]类型的list
     */
//    @Query(value = "select * from cst_customer",nativeQuery = true)
    //    public List<Object []>findSql();
    @Query(value = "select * from cst_customer where cust_name like ?",nativeQuery = true)
    public List<Object []>findSql(String name);

2.动态查询

springdatajpa的接口规范:

  • JpaRepository<操作的实体类型,实体类型中的 主键 属性的类型>

    封装了基本的CRUD的操作,分页等;

  • JpaSpecificationExecutor<操作的实体类类型>

    封装了复杂查询。

上述查询方法使用到的是接口JpaRepository中的方法,下面分析JpaSpecificationExecutor中的方法。

i.为什么需要动态查询

可能有些许疑惑,为什么还需要动态查询呢?有时候我们在查询某个实体的时候哦,给定的查询条件不是固定的,这个时候就需要动态构建相应的查询语句,可以理解为上述的查询条件是定义在dao接口中的,而动态查询条件定义在实现类中。

ii.JpaSpecificationExecutor中定义的方法

public interface JpaSpecificationExecutor<T> {
   
    T findOne(Specification<T> var1);

    List<T> findAll(Specification<T> var1);

    Page<T> findAll(Specification<T> var1, Pageable var2);

    List<T> findAll(Specification<T> var1, Sort var2);

    long count(Specification<T> var1);
}

在上述方法中,我们可以看到接口Specification。可以简单理解为,Specification构造的就是查询条件。我们看看Specification中定义的方法。

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

与上述查询方法不同,复杂查询定义在dao接口中,而动态查询定义在实现类中。

1)单条件查询

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
   
    @Autowired
    private CustomerDao customerDao;
    @Test
    public void conditionTest(){
   
        /**
         * 自定义查询条件
         *      1.实现Specification接口(提供泛型:查询对象类型,需要那个对象就写哪个泛型)
         *      2.实现toPredicate方法(构造查询条件)
         *      3.需要借书方法参数中的两个形参
         *              root:用于获取查询的对象属性
         *              CriteriaBuilder:构造查询条件,内部封装了很多的查询条件(例如:模糊匹配,精准匹配)
         *  需求:根据客户名称查询,查询客户名称为大学
         *          查询条件
         *              1.查询方法 (精准匹配,是否为空...)
         *                  CriteriaBuilder对象
         *              2.比较的属性名称(与哪个字段去以什么方式去比较)
         *                  root对象
         */

        Specification<Customer> spec=new Specification<Customer>() {
   
            @Override
             public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
   
                //1.获取比较的属性(不是字段名)
                Path<Object> custName = root.get("custName");
                //2.构造查询条件
                /**
                 * 第一个参数:需要比较的属性(Path)
                 * 第二个参数:当前比较的取值
                 */
                Predicate predicate = cb.equal(custName, "三峡大学");//进行精准匹配   (比较的属性,比较的属性的取值)
                return predicate;
            }
        };
        //根据返回的对象个数选择findOne或者findAll
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }
}

2)多条件查询

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
   
    @Autowired
    private CustomerDao customerDao;
/**
     * 多条件查询:根据用户名和所属行业进行查询
     *         root:获取属性
     *              用户名
     *              所属行业
     *         cb:构造查询
     *              1.构造客户名的精准匹配查询
     *              2.构造所属行业的精准匹配查询
     *              3,将以上两个查询联系起来
     */
    @Test
    public void findByNmaeAndIndustray(){
   
        Specification<Customer> spec=new Specification<Customer>() {
   
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
   
                //1.获取属性
                Path<Object> custName = root.get("custName");
                Path<Object> industry = root.get("custIndustry");
                //2.构造查询
                Predicate p1 = cb.equal(custName, "6测试数据-coderxz");
                Predicate p2 = cb.equal(industry, "6测试数据-java工程师");
                //3。将多个查询条件组合到一起(and/or)
                Predicate predicate = cb.and(p1, p2);
                return predicate;
            }
        };
        Customer customer = customerDao.findOne(spec);
        System.out.println(customer);
    }
}

3)模糊查询

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpecTest {
   
    @Autowired
    private CustomerDao customerDao;
    /**
     * 案例:根据客户名称进行模糊配置,返回客户列表
     *
     * equal:直接的path对象(属性),然后直接进行比较即可
     *
     * 对于gt,lt,le,like:得到path对象,根据path对象指定比较参数的类型(字符串or数字...),再进行比较
     * 指定参数类型  path.as(类型的字节码对象)
     */
    @Test
    public void findVagueCustomer(){
   
        Specification<Customer>spec=new Specification<Customer>() {
   
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
   
                Path<Object> custName = root.get("custName");
                Predicate predicate = criteriaBuilder.like(custName.as(String.class), "%大学%");
                return predicate;
            }
        };
        List<Customer> customers = customerDao.findAll(spec);
        for(Customer c:customers){
   
            System.out.println(c);
        }
    }
}

4)分页查询

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值