Spring Data JPA Specification(规范)实现复杂查询(H2数据库)

本文详细介绍了Spring Data JPA中JpaRepository与JpaSpecificationExecutor接口的使用,通过具体示例展示了如何实现复杂的数据库查询,包括条件筛选、分页、排序及模糊查询。

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

目录

持久化 API 接口概述

JpaSpecificationExecutor 常用 API

JPA Specification 编码示例


持久化 API 接口概述

1、JPA 持久化 API 接口主要如下:

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> 
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> 
public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> 
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID>

2、JpaRepository 接口拥有常用的 CURD 方法以及分页方法、字段排序等操作,但是没有与或非、like、以及大于等于、小于等于等操作,这些方法都在 JpaSpecificationExecutor 接口中。

3、如果只需要简单的实现 CRUD、分页、排序,则继承 JpaRepository接口即可,如果还需要复杂查询,则可以再继承 JpaSpecificationExecutor 接口。当然也可以直接继承 JpaRepositoryImplementation 接口。

JpaSpecificationExecutor 常用 API

org.springframework.data.jpa.repository.JpaSpecificationExecutor
List<T> findAll(@Nullable Specification<T> spec)规范查询。没有数据时返回空列表。
Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable)规范查询。同时进行分页查询。
List<T> findAll(@Nullable Specification<T> spec, Sort sort)规范查询。同时指定排序字段。
Optional<T> findOne(@Nullable Specification<T> spec)规范查询单条数据。注意如果结果多余一条,则抛异常。

JPA Specification 编码示例

1、本文承接《《Spring Data JPA 常用 CRUD 操作汇总》,环境为 Spring boot 2.1.4。下面列举几项稍微说明,实际中对于其它需求,同理可以参看 jpa 源码进行编写即可。

持久化层

import com.wmx.entity.TV;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
 * Java 接口可以多继承
 * JpaRepository 中有常用的 CRUD 、分页、排序等方法
 * JpaSpecificationExecutor 可以实现任意的复杂查询
 */
public interface TVRepository extends JpaRepository<TV, Integer>, JpaSpecificationExecutor<TV> {
}

service 业务层

@Service
public class TVServiceImpl implements TVService {
    @Resource
    private TVRepository tvRepository;

    @Override
    public List<TV> findAll(Date start, Date end, String tvName) {
        //直接使用匿名内部类实现接口
        Specification specification = new Specification<TV>() {
            @Override
            public Predicate toPredicate(Root<TV> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicateList = new ArrayList<>();
                //条件1:查询 tvName 为 海信 的数据,root.get 中的值与 TV 实体中的属性名称对应
                if (tvName != null && !"".equals(tvName)) {
                    predicateList.add(cb.equal(root.get("tvName").as(String.class), tvName));
                }

                //条件2:TV 生产日期(dateOfProduction)大于等于 start 的数据,root.get 中的 dateOfProduction 必须对应 TV 中的属性
                predicateList.add(cb.greaterThanOrEqualTo(root.get("dateOfProduction").as(Date.class), start));

                //条件3:TV 生产日期(dateOfProduction)小于等于 end
                predicateList.add(cb.lessThanOrEqualTo(root.get("dateOfProduction").as(Date.class), end));

                Predicate[] pre = new Predicate[predicateList.size()];
                pre = predicateList.toArray(pre);
                return query.where(pre).getRestriction();
            }
        };
        return tvRepository.findAll(specification);//没有数据时,返回空列表
    }

    @Override
    public Page<TV> findAll(Date start, int page, int size) {
        page--;
        page = page < 0 ? 0 : page;//page 为页码,数据库从0页开始
        //可以使用重载的 of(int page, int size, Sort sort) 方法指定排序字段
        Pageable pageable = PageRequest.of(page, size);
        //创建查询规范
        Specification<TV> tvSpecification = new Specification<TV>() {
            @Override
            public Predicate toPredicate(Root<TV> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicateList = new ArrayList<>();
                //查询生产日期在 start 与当期时间之间的数据,闭区间
                predicateList.add(cb.between(root.get("dateOfProduction").as(Date.class), start, new Date()));
                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        };
        return tvRepository.findAll(tvSpecification, pageable);//无数据时返回空列表
    }

    @Override
    public List<TV> findAllLike(String tvNameLike) {
        Specification<TV> tvSpecification = new Specification<TV>() {
            @Override
            public Predicate toPredicate(Root<TV> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate[] predicates = new Predicate[1];
                //like(Expression<String> x, String pattern):参数 pattern 表示匹配的格式
                predicates[0] = cb.like(root.get("tvName").as(String.class), "%" + tvNameLike + "%");
                //同理以 xxx 开头,则为 tvNameLike + "%"
                return query.where(predicates).getRestriction();
            }
        };
        //规范查询的同时,指定以主键 tvId 倒序排序
        return tvRepository.findAll(tvSpecification, Sort.by(Sort.Direction.DESC, "tvId"));
    }
}

src/main/java/com/wmx/service/impl/TvServiceImpl.java · 汪少棠/h2Smil - Gitee.com 

控制器层

@Controller
public class TVController {
    @Resource
    private TVService tvService;

    @GetMapping("findAll1")
    @ResponseBody
    public String findAll1(String name) throws ParseException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start = dateFormat.parse("2019-04-27 10:00:00");
        Date end = dateFormat.parse("2019-04-27 23:00:00");
        return tvService.findAll(start, end, name).toString();
    }

    @GetMapping("findAll2")
    @ResponseBody
    public String findAll2(Integer page, Integer size) throws ParseException {
        page = page == null ? 1 : page;
        size = size == null ? 2 : size;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date start = dateFormat.parse("2019-04-28 08:00:00");
        Page<TV> tvPage = tvService.findAll(start, page, size);

        Logger logger = Logger.getAnonymousLogger();
        logger.info("总记录数:" + tvPage.getTotalElements());
        logger.info("总页数:" + tvPage.getTotalPages());
        List<TV> tvList = tvPage.getContent();
        return tvList.toString();
    }

    @GetMapping("findAll3")
    @ResponseBody
    public String findAll3(String like) throws ParseException {
        return tvService.findAllLike(like).toString();
    }
}

src/main/java/com/wmx/controller/TvController.java · 汪少棠/h2Smil - Gitee.com

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蚩尤后裔-汪茂雄

芝兰生于深林,不以无人而不芳。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值