SpringDataJPA(二)分页查询与动态条件

SpringDataJPA(二) 分页查询与动态条件

1、分页查询

Spring Data JPA 中 Pageable 是分页查询的抽象接口,其包含了基本分页信息(如页码、每页大小、排序等)。而PageRequest 则是 Pageable 接口的实现类,提供了具体的分页参数生成功能。常用的几种分页查询构造方法如下:

Return Method Description
static PageRequest of(int pageNumber, int pageSize) 构造分页查询请求:
- pageNumber:查询页码(起始页为0,不得为负数);
- pageSize:查询页大小(必须大于0);
static PageRequest of(int pageNumber, int pageSize, Sort sort) 构造分页查询请求并对结果排序:
- pageNumber:查询页码(起始页为0,不得为负数);
- pageSize:查询页大小(必须大于0);
- sort:排序规则(不能为空,默认以 Sort.unsorted()代替);
static PageRequest of(int pageNumber, int pageSize, Sort.Direction direction, String... properties) 构造分页查询请求并对结果排序:
- pageNumber:查询页码(起始页为0,不得为负数);
- pageSize:查询页大小(必须大于0);
- direction:排序方向(不可为空);
- properties:排序属性(不可为空);
// 第一种实例化 Pageable
Pageable pageable1 = PageRequest.of(1, 2);

//第二种实例化 Pageable
Sort sort = Sort.by(Sort.Direction.ASC, "age");
Pageable pageable2 = PageRequest.of(1, 2, sort);// 升序

//第三种实例化 Pageable
Pageable pageable3 = PageRequest.of(1, 2, Sort.Direction.DESC, "age");// 降序

在 Spring Data JPA 的查询方法中,要实现查询结果的分页,只需确保定义的 Repository 继承自 PagingAndSortingRepository 接口,并在 JPA 查询接口方法参数的最后追加构造好的 Pageable 分页参数即可,JPA 可以自动对分页参数进行解析和执行。返回结果包括以下两种类型:

(1)Slice<T>

Slice 提供了轻量级分页查询功能,其不包含总页数或总记录数信息,仅包含是否有下一页的信息。Slice 返回的数据和 Page 类似,但因为不需要执行 count 查询,因此性能开销较小,适用于数据量较大且不关心总数的场景,比如“加载更多”、“滚动加载”等。

Return Method Description
List<T> getContent() 返回查询当前页的数据列表
int getNumber() 返回查询当前页的页码
int getNumberOfElements() 返回查询当前页的元素数量
int getSize() 返回分页大小
boolean hasNext() 判断是否有下一页
boolean hasPrevious() 判断是否有上一页

(2)Page<T>

Page接口继承自Slice,其在Slice基础之上还包含了总页数、总记录数等信息,适用于需要详细、完整、精确分页信息的场景,比如分页按钮、分页导航等。相比Slice来说,其需要额外查询数据总数count,所以在查询大数据集时性能开销较大、性能略低。

Return Method Description
long getTotalElements() 返回查询元素总数
int getTotalPages() 返回查询总页数

(3)代码示例

// 命名规则方法
public Slice<UserInfo> findByAgeGreaterThan(int age, Pageable pageable);
// JPQL查询
@Query("select u from UserInfo u where u.name like ?1%")
public Page<UserInfo> findByNameLike(String name, Pageable pageable);


// 1.保留方法: Page
PageRequest pageRequest = PageRequest.of(page, size);
Page<UserInfo> pageInfo = userInfoDao.findAll(pageRequest);
System.out.println(pageInfo.getTotalPages()); // 获取总页数
System.out.println(pageInfo.getTotalElements()); // 获取总元素数
System.out.println(pageInfo.getNumberOfElements()); // 获取当前页元素数
return pageInfo.getContent(); // 返回当前页的数据列表
// 2.命名规则方法: Slice
PageRequest pageRequest = PageRequest.of(page, size);
Slice<UserInfo> pageInfo = userInfoDao.findByAgeGreaterThan(18, pageRequest);
return pageInfo.getContent(); // 返回当前页的数据列表
// 3.JPQL查询: Page+Sort
PageRequest pageRequest = PageRequest.of(page, size, Sort.Direction.ASC, "age", "id");// 按照'age'与'id'升序
Page<UserInfo> pageInfo = userInfoDao.findByNameLike("test", pageRequest);
return pageInfo.getContent();

三种方式的查询结果如下:

// 1.保留方法: limit+count
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ limit ?
Hibernate: select count(userinfo0_.id) as col_0_0_ from user_info userinfo0_
//2.命名规则方法: limit
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where userinfo0_.age>? limit ?
//3.JPQL查询: limit+count+order
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where userinfo0_.name like ? order by userinfo0_.age asc, userinfo0_.id asc limit ?
Hibernate: select count(userinfo0_.id) as col_0_0_ from user_info userinfo0_ where userinfo0_.name like ?

需要注意的是:

  • 关于nativeQuery:基于nativeQuery = true本地SQL查询方法在使用分页查询时,JPA 可以基于主查询语句自动生成countQuery,其查询过程与使用方法上述三种方式一致,但是在复杂查询场景下最好手动添加countQuery(生成可能存在问题);

参考文章:

public interface UserRepository extends JpaRepository<User, Long> {
    
    

  @Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
    countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
    nativeQuery = true)
  Page<User> findByLastname(String lastname, Pageable pageable);
    
}
  • 关于countQuerycountQuery的目的是为Page结果计算总记录数,若手动添加了countQuery则会优先执行手动添加的 countQuery;注意若返回结果为Slice,则不会执行countQuery,即使手动加了countQuery
  • 关于分页小优化:在Pageable分页参数中,若当前查询页page的剩余数据量 < 页大小size,则不会再去执行countQuery(恰好相等的话仍需执行);

2、动态条件

参考文章:

类似于 Mybatis 的动态SQL(比如、等),Spring Data JPA 也支持在运行时动态生成查询条件的能力,其提供了一套强大的面向对象的工具集,包括 SpecificationCriteriaBuilderPredicate等,可以帮助我们构建复杂的动态查询。JPA 2 引入了criteria API来构建查询语句中的where条件,这些查询条件通常用CriteriaBuilder来构建,构建的每个条件元素都表示为Predicate对象,而Specification则是对条件元素Predicate的封装(或者说是对JPA本身 Criteria 动态查询的包装)。

2.1 基本组件介绍

要实现动态查询,首先需要将 Repository 接口继承JpaSperiactionExecutor接口,该接口提供了一系列支持Specification动态查询的保留方法,而Specification则提供了构建动态查询条件Predicate的API。在实际应用中,我们只需要实现Specification接口,并提供一个toPredicate方法来定义我们的查询规则,Spring Data JPA就会自动地为我们执行查询。

(1)JpaSpecificationExecutor

public interface JpaSpecificationExecutor<T> {
    
    
    // 查询单个对象
    Optional<T> findOne(@Nullable Specification<T> spec);
 
    // 查询全部列表
    List<T> findAll(@Nullable Specification<T> spec);
 
    // 查询分页列表(Pageable)
    Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable);
 
    // 查询排序列表(Sort)
    List<T> findAll(@Nullable Specification<T> spec, Sort sort);
 
    // 统计查询
    long count(@Nullable Specification<T> spec);
}

(2)Specification

Specification是一个基于JPA Criteria API构建动态查询条件的抽象接口,用于封装和构建代表查询条件的Predicate对象,其核心源码如下:

public interface Specification<T> extends Serializable {
    
    
	// 非
	static <T> Specification<T> not(@Nullable Specification<T> spec) {
    
    

		return spec == null //
				? (root, query, builder) -> null //
				: (root, query, builder) -> builder.not(spec.toPredicate(root, query, builder));
	}

	// 与
	default Specification<T> and(@Nullable Specification<T> other) {
    
    
		return SpecificationComposition.composed(this, other, CriteriaBuilder::and);
	}

	// 或
	default Specification<T> or(@Nullable Specification<T> other) {
    
    
		return SpecificationComposition.composed(this, other, CriteriaBuilder::or);
	}

	// 构建WHERE动态查询条件对象 Predicate
	@Nullable
	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}

可以看出,Specification通过实现抽象方法Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)来构建动态查询条件(多个Predicate支持通过逻辑谓词连接成一个查询条件Predicate),当然Specification本身也提供了逻辑谓词notandor方法来实现多个Specification动态条件的连接和组合。

(3) toPredicate 方法

toPredicate方法接收三个参数RootCriteriaQueryCriteriaBuilder,并返回一个Predicate,其中参数含义如下:

  • Root:Root接口代表查询根对象,可以通过Root获取实体中的查询属性,比如 root.get("lastName")

  • CriteriaQuery:代表顶层查询对象,可以用来自定义查询方式(查询结构),一般使用较少;

    • 比如:query.distinct(true).select(...).where(...).groupBy(...).having(...).getRestriction(),最后需要通过getRestriction()方法获得一个Predicate对象返回;
  • CriteriaBuilder:构建查询条件(WHERE中的条件元素)的构造器,即用于构建Predicate对象。其内部封装有很多查询条件构造方法,常用方法如下:

    Method Description Example
    Predicate equal(Expression<?> x, Object y) 构建等于条件 Predicate predicate = cb.equal(root.get("propertyName"), "Example");
    Predicate notEqual(Expression<?> x, Object y) 构建不等于条件 Predicate predicate = cb.notEqual(root.get("propertyName"), "Example");
    Predicate like(Expression<String> x, String pattern) 构建模糊查询条件 Predicate predicate = cb.like(root.get("propertyName"), "%Example%")
    Predicate greaterThanOrEqualTo(Expression<? extends Y> x, Y y) 构建大于等于条件 Predicate predicate = cb.greaterThanOrEqualTo(root.get("propertyId"), 1)
    Predicate lessThanOrEqualTo(Expression<? extends Y> x, Y y) 构建小于等于条件 Predicate predicate = cb.lessThanOrEqualTo(root.get("propertyId"), 2)
    Predicate between(Expression<? extends Y> v, Y x, Y y) 构建范围条件 Predicate predicate = cb.between(root.get("propertyId"), 1, 10)
    Predicate and(Expression<Boolean> x, Expression<Boolean> y) 构建两个条件的and逻辑与表达式 Predicate and = cb.and(predicate1, predicate2)
    Predicate and(Predicate... restrictions) 构建多个条件的and逻辑与表达式,支持可变参数数组 Predicate and = cb.and(predicateList.toArray(new Predicate[0]))
    Predicate or(Expression<Boolean> x, Expression<Boolean> y) 构建两个条件的or逻辑或表达式 Predicate or = cb.or(predicate1, predicate2)
    Predicate or(Predicate... restrictions) 构建多个条件的or逻辑或表达式,支持可变参数数组 Predicate or = cb.or(predicateList.toArray(new Predicate[predicateList.size()]))
    Predicate not(Expression<Boolean> restriction) 构建条件的not逻辑否表达式 Predicate not = cb.not(predicate)

2.2 单条件查询

背景: 前端向后端服务提交POST表单请求(form-data),其请求参数包括分页数据(page、size)、查询条件(id、name、age、email)等,需要注意各查询条件均是可选项,若同时存在多个条件则按照顺序为优先级进行单条件查询

(1)Controller

@RestController
@RequestMapping("/userinfo")
public class UserInfoController {
    
    
    @Autowired
    UserInfoService userInfoService;
    
    /**
    * accessToken: 接收URL中的查询参数,可不传递
    * page: 接收POST表单中的分页数据,按参数名称映射
    * request: 接收POST表单中的查询数据,按参数名称映射
    */
    @RequestMapping("/queryByPriority")
    public List<UserInfo> queryByPriority(@RequestParam(value = "accessToken", required = false) String accessToken, CommonPage page, CommonRequest request) {
    
    
        System.out.println(accessToken);
        System.out.println(page);
        System.out.println(request);
        return userInfoService.queryBySingleCondition(page,request);
    }
}

(2)Service

@Service
public class UserInfoService {
    
    
    @Autowired
    IUserInfoDao userInfoDao;

    public List<UserInfo> queryBySingleCondition(CommonPage page, CommonRequest request){
    
    
        // 构造动态查询条件
        Specification<UserInfo> specification = new Specification<UserInfo>() {
    
    
            @Override
            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    
    
                if(request != null){
    
    
                    // equal
                    if(request.getId() != -1){
    
    
                        Predicate predicate = criteriaBuilder.equal(root.get("id"), request.getId());
                        return predicate;
                    }
                    // like
                    if(StringUtils.isNotEmpty(request.getName())){
    
    
                        Predicate predicate = criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%");
                        return predicate;
                    }
                    // greaterThanOrEqualTo
                    if(request.getAge() > 0){
    
    
                        Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(root.get("age"), request.getAge());
                        return predicate;
                    }
                    // in
                    if(StringUtils.isNotEmpty(request.getEmail())){
    
    
                        Predicate predicate = root.get("email").in(Arrays.asList("[email protected]","[email protected]","[email protected]"));
                        return predicate;
                    }
                }
                // null: 表示查询所有
                return null;
            }
        };
        // 构造分页参数
        Pageable pageable = PageRequest.of(page.getPage(), page.getSize());
        // JpaSpecificationExecutor.findAll
        Page<UserInfo> pageResult = userInfoDao.findAll(specification, pageable);
        // 返回查询结果
        return pageResult.getContent();
    }
}

(3)查询结果

在这里插入图片描述

accesstoken_test_20241130
CommonPage(page=0, size=5)
CommonRequest(id=-1, name=user, age=25, email=null, description=null)
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where userinfo0_.name like ? limit ?

2.3 多条件查询

背景: 前端向后端服务提交POST表单请求(form-data),其请求参数包括分页数据(page、size)、查询条件(id、name、age、email)等,需要注意各查询条件均是可选项,若同时存在多个条件则需保证查询结果要同时满足所有条件

多条件查询的关键在于多条件的连接,常见的条件连接方式包括两种:

  • 基于CriteriaBuilder:通过CriteriaBuilder的逻辑谓词andor等来实现多个Predicate条件对象的连接和组合;
  • 基于Specification:通过Specification接口提供的notandor方法来实现多个Specification动态条件的连接和组合

(1)Controller

@RestController
@RequestMapping("/userinfo")
public class UserInfoController {
    
    
    @Autowired
    UserInfoService userInfoService;

    @RequestMapping("/queryByCombination")
    public List<UserInfo> queryByCombination(@RequestParam(value = "accessToken", required = false) String accessToken, CommonPage page, CommonRequest request) {
    
    
        System.out.println(accessToken);
        System.out.println(page);
        System.out.println(request);
        return userInfoService.queryByConditions(page,request);
    }

}

(2)Service

  • 基于CriteriaBuilder
@Service
public class UserInfoService {
    
    
    @Autowired
    IUserInfoDao userInfoDao;

    public List<UserInfo> queryByConditions(CommonPage page, CommonRequest request){
    
    
        // 构造动态查询条件
        Specification<UserInfo> specification = new Specification<UserInfo>() {
    
    
            @Override
            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    
    
                // 收集查询条件的列表
                List<Predicate> predicateList = new ArrayList<>();
                // equal
                if(request.getId() != -1){
    
    
                    Predicate predicate = criteriaBuilder.equal(root.get("id"), request.getId());
                    predicateList.add(predicate);
                }
                // like
                if(StringUtils.isNotEmpty(request.getName())){
    
    
                    Predicate predicate = criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%");
                    predicateList.add(predicate);
                }
                // greaterThanOrEqualTo
                if(request.getAge() > 0){
    
    
                    Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(root.get("age"), request.getAge());
                    predicateList.add(predicate);
                }
                // 多条件and组合方式
                // 1). criteriaBuilder.and 返回 predicate
                Predicate predicate = criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));// where 1=1
                return predicate;
                // 2). query.where.getRestriction 返回 predicate
                //Predicate predicate = query.where(predicateList.toArray(new Predicate[predicateList.size()])).getRestriction();
                //return predicate;
                // 3). query.where 可以返回 null
                //query.where(predicateList.toArray(new Predicate[predicateList.size()]))
                //return null;
            }
        };
        // 构造分页参数
        Pageable pageable = PageRequest.of(page.getPage(), page.getSize());
        // JpaSpecificationExecutor.findAll
        Page<UserInfo> pageResult = userInfoDao.findAll(specification, pageable);
        // 返回查询结果
        return pageResult.getContent();
    }
}

在这里插入图片描述

accesstoken_test_20241130
CommonPage(page=0, size=3)
CommonRequest(id=-1, name=user, age=25, email=null, description=null)
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where (userinfo0_.name like ?) and userinfo0_.age>=25 limit ?

注意:and连接的PredicateList条件列表为空,则查询语句会追加 where 1=1 的恒成立条件;若or连接的PredicateList条件列表为空,则查询语句会追加 where 0=1 的恒成立条件;若直接返回null则不会追加where条件。

  • 基于Specification
@Service
public class UserInfoService {
    
    
    @Autowired
    IUserInfoDao userInfoDao;

    public List<UserInfo> queryBySpecifications(CommonPage page, CommonRequest request){
    
    
        // 构造多动态查询条件 Specification 的组合
        Specification<UserInfo> specification = new Specification<UserInfo>() {
    
    
            @Override
            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    
    
                // 收集查询条件
                List<Predicate> predicateList = new ArrayList<>();
                // equal
                if(request.getId() != -1){
    
    
                    Predicate predicate = criteriaBuilder.equal(root.get("id"), request.getId());
                    predicateList.add(predicate);
                }
                // like
                if(StringUtils.isNotEmpty(request.getName())){
    
    
                    Predicate predicate = criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%");
                    predicateList.add(predicate);
                }
                // 多条件 or 组合
                Predicate predicate = criteriaBuilder.or(predicateList.toArray(new Predicate[predicateList.size()]));
                return predicate;
            }
        }.and(new Specification<UserInfo>() {
    
    
            @Override
            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    
    
                // greaterThanOrEqualTo
                if(request.getAge() > 0){
    
    
                    Predicate predicate = criteriaBuilder.greaterThanOrEqualTo(root.get("age"), request.getAge());
                    return predicate;
                }
                return null;
            }
        });
        // 构造分页参数
        Pageable pageable = PageRequest.of(page.getPage(), page.getSize());
        // JpaSpecificationExecutor.findAll
        Page<UserInfo> pageResult = userInfoDao.findAll(specification, pageable);
        // 返回查询结果
        return pageResult.getContent();
    }
}

在这里插入图片描述

accesstoken_test_20241130
CommonPage(page=0, size=3)
CommonRequest(id=6, name=user, age=25, email=null, description=null)
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where (userinfo0_.id=6 or userinfo0_.name like ?) and userinfo0_.age>=25 limit ?
Hibernate: select count(userinfo0_.id) as col_0_0_ from user_info userinfo0_ where (userinfo0_.id=6 or userinfo0_.name like ?) and userinfo0_.age>=25

2.4 关系优先级

关系型运算符的优先级由高到低分别为:notandorCriteriaQuery构建查询语句时会自动根据优先级进行优化,决定是否需要加()来组合子条件。

    public List<UserInfo> queryByMerge(CommonPage page, CommonRequest request){
    
    
        // 构造动态查询条件
        Specification<UserInfo> specification = new Specification<UserInfo>() {
    
    
            @Override
            public Predicate toPredicate(Root<UserInfo> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    
    
                // or_1
                Predicate predicate_id = criteriaBuilder.equal(root.get("id"), request.getId());
                Predicate predicate_name = criteriaBuilder.like(root.get("name"), "%" + request.getName() + "%");
                Predicate orP_1 = criteriaBuilder.or(predicate_id, predicate_name);
                // or_2
                Predicate predicate_age = criteriaBuilder.greaterThanOrEqualTo(root.get("age"), request.getAge());
                Predicate predicate_email = criteriaBuilder.like(root.get("email"), "%" + request.getEmail() + "%");
                Predicate orP_2 = criteriaBuilder.or(predicate_age, predicate_email);
                // and
                Predicate andP = criteriaBuilder.and(orP_1, orP_2);
                return andP;
            }
        };
        // JpaSpecificationExecutor.findAll
        List<UserInfo> findResult = userInfoDao.findAll(specification);
        // 返回查询结果
        return findResult;
    }
CommonPage(page=0, size=3)
CommonRequest(id=6, name=user, age=25, [email protected], description=null)
Hibernate: select userinfo0_.id as id1_1_, userinfo0_.age as age2_1_, userinfo0_.des_info as des_info3_1_, userinfo0_.email as email4_1_, userinfo0_.name as name5_1_ from user_info userinfo0_ where (userinfo0_.id=6 or userinfo0_.name like ?) and (userinfo0_.age>=25 or userinfo0_.email like ?)

2.5 复杂查询

构建复杂查询(比如子查询、多表连接等)需要使用 CriteriaQuery<?> query构建查询结构;以子查询为例,其需要使用CriteriaQuery.subquery(Class<U> type)方法来构建子查询:

public void testSubSpecification() {
    
    
    Specification<RetPrdInfo> specification = (root, query, cb) -> {
    
    
        // 1.父查询条件
        Predicate parentPredicate = cb.equal(root.get("crToolRunMode"), "CLN");
 
        // 2.子查询 Params: type – the subquery result type
        Subquery<RetPrdAbn> subQuery = query.subquery(RetPrdAbn.class);
        // 1) 子查询对象 subRoot 及子查询 SELECT 参数
        Root<RetPrdAbn> subRoot = subQuery.from(RetPrdAbn.class);
        subQuery = subQuery.select(subRoot.get("prdSeqIdFk"));
        // 2) 子查询条件 WHERE
        Predicate subPredicate1 = cb.equal(subRoot.get("lotIdFk"), "LW0001");
        Predicate subPredicate2 = cb.equal(subRoot.get("lotSpltIdFk"), "00");
        subQuery.where(cb.and(subPredicate1, subPredicate2));
 
        // 3.将父查询条件和子查询合并 IN
        Predicate parentAndSubConjunctPredicate = root.get("prdSeqId").in(subQuery);
        // 4.最终查询语句
        return query.where(parentPredicate, parentAndSubConjunctPredicate).getRestriction();
    };
    // ...
}
select
    ...
from 
    ret_prd_info
where 
    cr_tool_run_mode = ?
    and (prd_seq_id in 
             (
                select prd_seq_id_fk
                from ret_prd_abn
                where lot_id_fk = ? and retprdabn1_.lot_splt_id_fk = ?
             )
        )

3、批处理

参考文章:

  • https://www.cnblogs.com/blog5277/p/10661096.html
  • https://blog.csdn.net/JingAi_jia917/article/details/138284510
@Service
public class ProductServiceImpl implements ProductService {
    
    
 
    @Resource
    private EntityManager entityManager;
 
    /**
     * 批量插入数据
     */
    @Transactional
    @Override
    public int batchInsert(List<ProductEntity> list) {
    
    
        int batchSize = 2000;
        if(list.isEmpty()) {
    
    
            return 0;
        }
        // 单次批量插入条数
        StringBuffer sb = new StringBuffer();
        String insertSql = "insert into tb_product(name, delivery_no, customer, security_code, create_time, validate_num) values ";
        sb.append(insertSql);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        int index = 0;
        for(ProductEntity entity : list) {
    
    
            index ++;
            // 拼接sql字符串
            sb.append("('").append(entity.getName()).append("','").append(entity.getDeliveryNo()).append("','")
                    .append(entity.getCustomer()).append("','").append(entity.getSecurityCode()).append("','")
                    .append(format.format(entity.getCreateTime())).append("', 0),");
            if(index % batchSize == 0) {
    
    
                Query query = entityManager.createNativeQuery(sb.substring(0, sb.length() - 1) + ";");
                int rs = query.executeUpdate();
                if(rs != batchSize) {
    
    
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return 0;
                }
                sb = sb.replace(0, sb.length(), insertSql);
                index = 0;
            }
        }
        if(index > 0) {
    
    
            Query query = entityManager.createNativeQuery(sb.substring(0, sb.length() - 1) + ";");
            int rs = query.executeUpdate();
            if(rs != list.size() % batchSize) {
    
    
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return 0;
            }
        }
        return list.size();
    }
 
    /**
     * 批量修改
     */ 
    @Transactional
    @Override
    public int batchUpdate3(List<ProductEntity> list) {
    
    
        if(list.isEmpty()) {
    
    
            return 0;
        }
        StringBuffer sb = new StringBuffer();
        String updateSql = "update tb_product set ";
        for(int i = 0 ; i < list.size() ; i ++) {
    
    
            ProductEntity entity = list.get(i);
            sb.append(updateSql).append("name = '").append(entity.getName()).append("', customer = '")
                    .append(entity.getCustomer()).append("' where pid = ").append(entity.getPid()).append(";");
            if(i > 0 && i % 2000 == 0) {
    
    
                Query query = entityManager.createNativeQuery(sb.toString());
                int rs = query.executeUpdate();
                if(rs <= 0) {
    
    
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return 0;
                }
                sb = sb.replace(0, sb.length(), Strings.EMPTY);
            }
        }
        if(sb.length() > 0) {
    
    
            Query query = entityManager.createNativeQuery(sb.toString());
            int rs = query.executeUpdate();
            if(rs <= 0) {
    
    
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                return 0;
            }
        }
        return list.size();
    }
}