PS:禁止拷贝形式转载,转载请以URL形式
1.简介
JPA 原生查询接口SimpleJpaRepository.findAll(@Nullable Specification<T> spec, Pageable pageable),只能同时支持条件和分页查询,但是不能支持自定义查询字段。
2.环境
- jdk1.8
- springboot:2.2.8.RELEASE
3.解决思路
- CriteriaQuery.multiselect(List<Selection<?>> var1) 可以指定JPA查询字段。
- Specification.toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) 自定义查询条件,与查询字段。
- SimpleJpaRepository.findAll(@Nullable Specification<T> spec, Pageable pageable),通过默认实现方法进行查询。
- 3.1. SimpleJpaRepository.findAll 默认实现,会覆盖我们2步中自定义的查询字段,导致我们自定义查询字段无法生效
- 3.2 覆写SimpleJpaRepository.findAll中的调用方法。拷贝父类代码,去除覆盖自定义查询字段代码;
4.实现代码
4.1 Entity:Demo
PS:自定义实体查询字段,需要添加相应的实体构造函数
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
@Data
@Entity
@Table(name = "tb_demo")
public class Demo {
@Id
@Column(name = "c_uuid", unique = true, length = 36, nullable = false)
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String uuid;
@Column(name = "c_name", nullable = false)
private String name;
@Column(name = "c_sex", nullable = false)
private String sex;
@Column(name = "c_age", nullable = false)
private int age;
public Demo() {
}
public Demo(String uuid, String name, int age) {
this.uuid = uuid;
this.name = name;
this.age = age;
}
}
4.2 Dao:DemoDao
PS:
- SimpleJpaRepository为JPA默认查询实现
- 拷贝父类
getQuery(Specification<S> spec, Class<S> domainClass, Sort sort)
方法,注释query.select(root);
代码 - 拷贝
getQuery(Specification<S> spec, Class<S> domainClass, Sort sort)
方法关联的私有方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;
import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
@Repository
public class DemoDao extends SimpleJpaRepository<Demo, String> {
private EntityManager em;
@Autowired
public DemoDao(EntityManager entityManager) {
super(Demo.class, entityManager);
this.em = entityManager;
}
//拷贝父类实现
@Override
protected <S extends Demo> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
CriteriaQuery<S> query = builder.createQuery(domainClass);
Root<S> root = this.applySpecificationToCriteria(spec, domainClass, query);
//注释掉覆盖代码
/*query.select(root);*/
if (sort.isSorted()) {
query.orderBy(QueryUtils.toOrders(sort, root, builder));
}
return this.applyRepositoryMethodMetadata(this.em.createQuery(query));
}
private <S, U extends Demo> Root<U> applySpecificationToCriteria(@Nullable Specification<U> spec, Class<U> domainClass, CriteriaQuery<S> query) {
Assert.notNull(domainClass, "Domain class must not be null!");
Assert.notNull(query, "CriteriaQuery must not be null!");
Root<U> root = query.from(domainClass);
if (spec == null) {
return root;
} else {
CriteriaBuilder builder = this.em.getCriteriaBuilder();
Predicate predicate = spec.toPredicate(root, query, builder);
if (predicate != null) {
query.where(predicate);
}
return root;
}
}
private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {
if (getRepositoryMethodMetadata() == null) {
return query;
} else {
LockModeType type = getRepositoryMethodMetadata().getLockModeType();
TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
//该方无法进行访问,进行注释
//this.applyQueryHints(toReturn);
return toReturn;
}
}
}
4.3 查询
@Autowired
private DemoDao demoDao;
@RequestMapping(value = "/demo", method = RequestMethod.GET)
public String demo() {
Specification<Demo> specification = (Root<Demo> root, CriteriaQuery<?> cq, CriteriaBuilder cb) -> {
//自定义查询字段
cq.multiselect(root.get("uuid"), root.get("name"), root.get("age"));
//自定义查询条件
Predicate predicate = cb.equal(root.get("age"), 10);
return predicate;
};
//分页条件
Pageable pageable = PageRequest.of(0, 2);
Page<Demo> page = demoDao.findAll(specification, pageable);
//打印数据
System.out.println(page.getTotalElements());
System.out.println(JSON.toJSONString(page.getContent(), SerializerFeature.PrettyFormat));
return "success";
}
4.4 结果
Hibernate: select demo0_.c_uuid as col_0_0_, demo0_.c_name as col_1_0_, demo0_.c_age as col_2_0_ from tb_demo demo0_ where demo0_.c_age=10 limit ?
Hibernate: select count(demo0_.c_uuid) as col_0_0_ from tb_demo demo0_ where demo0_.c_age=10
3
[
{
"age":10,
"name":"张三",
"uuid":"1"
},
{
"age":10,
"name":"王五",
"uuid":"3"
}
]