[英]Spring JPA Repository dynamic query
目前我一直在使用以下 Spring JPA 存储库基础自定义查询,它工作正常,
@Query("SELECT usr FROM User usr WHERE usr.configurable = TRUE "
+ "AND (" +
"lower(usr.name) like lower(:filterText) OR lower(usr.userType.classType.displayName) like lower(:filterText) OR lower(usr.userType.model) like lower(:filterText)"
+ ")"
+ "")
public List<User> findByFilterText(@Param("filterText") String filterText, Sort sort);
当过滤文本将成为逗号分隔值时,我需要修改此查询。 但按照以下方式,它将是一个动态查询,我该如何执行它。
我需要构建的动态查询,
String sql = "SELECT usr FROM User usr WHERE usr.configurable = TRUE";
for(String word : filterText.split(",")) {
sql += " AND (lower(usr.name) like lower(:" + word + ") OR lower(usr.userType.classType.displayName) like lower(:" + word + ") OR lower(usr.userType.model) like lower(:" + word + "))";
}
根据 JB Nizet 和spring-data 文档,您应该使用自定义接口 + 存储库实现。
使用该方法创建一个接口:
public interface MyEntityRepositoryCustom {
List<User> findByFilterText(Set<String> words);
}
创建一个实现:
@Repository
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
public List<User> findByFilterText(Set<String> words) {
// implementation below
}
}
在现有的 Repository 界面中扩展新界面:
public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntityRepositoryCustom {
// other query methods
}
最后,在其他地方调用该方法:
dao.findByFilterText(new HashSet<String>(Arrays.asList(filterText.split(","))));
查询实现
您生成sql
变量的方法,即通过将一些字符串连接到查询中是不好的。 不要这样做。
您要连接的word
必须是一个有效的 JPQL 标识符,即 a :
后跟一个java 标识符开始,可选地后跟一些java 标识符部分。 这意味着如果您的 CSV 包含foo bar,baz
,您将尝试使用foo bar
作为标识符,并且您将收到异常。
您可以改为使用CriteriaBuilder
以安全的方式构造查询:
public List<User> findByFilterText(Set<String> words) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> q = cb.createQuery(User.class);
Root<User> user = q.from(User.class);
Path<String> namePath = user.get("name");
Path<String> userTypeClassTypeDisplayName =
user.get("userType").get("classType").get("displayName");
Path<String> userTypeModel = user.get("userType").get("model");
List<Predicate> predicates = new ArrayList<>();
for(String word : words) {
Expression<String> wordLiteral = cb.literal(word);
predicates.add(
cb.or(
cb.like(cb.lower(namePath), cb.lower(wordLiteral)),
cb.like(cb.lower(userTypeClassTypeDisplayName),
cb.lower(wordLiteral)),
cb.like(cb.lower(userTypeModel), cb.lower(wordLiteral))
)
);
}
q.select(doc).where(
cb.and(predicates.toArray(new Predicate[predicates.size()]))
);
return entityManager.createQuery(q).getResultList();
}
我自己一直在寻找解决方案:“自定义”存储库接口和实现的命名非常严格(正如那里所说的如何向 Spring Data JPA 添加自定义方法)
所以,要清楚的是,整个代码:(但@beerbajay 是对的)
自定义方法接口
public interface MyEntityRepositoryCustom {
List<MyEntity> findSpecial();
}
自定义方法实现
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
@PersistenceContext
private EntityManager em;
//custom method implementation
public List<Object> findSpecial() {
List<Object> list = em.createNativeQuery("select name, value from T_MY_ENTITY").getResultList();
return list;
}
}
“原始”存储库
@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity,Long>, MyEntityRepositoryCustom {
//original methods here... do not redefine findSpecial()...
}
您现在可以将“原始”存储库与新的自定义方法一起使用
@Service
public class MyService {
@Autowired
private DataRepository r;
public void doStuff() {
List<Object> list = r.findSpecial();
}
}
Spring Data JPA 有一种使用“规范”创建自定义和动态查询的方法: Spring Data - Specifications
首先,扩展JpaRepository
或CrudRepository
接口也应该实现JpaSpecificationExecutor<...>
这就是你所需要的。 您的存储库现在有一个新方法findAll
,它接受一个Specification<...>
对象,您可以使用 Beerbajay 方法通过覆盖toPredicate(...)
方法来创建 Criteria Queries,并且您可以自由构建(几乎) 任何你想要的查询:
Specification<...> spec = new Specification<...>() { @Override public Predicate toPredicate(Root<...> entity, CriteriaQuery<?> query, CriteriaBuilder cb) { List<Predicate> conditions = buildManyPredicates(cb, entity); return cb.and(conditions.toArray(new Predicate[conditions.size()])); } }; repository.findAll(spec, PageRequest.of(0, 10));
这就解决了Spring Data试图解析你在自定义接口中添加的方法的问题(因为没有自定义接口)
尝试按照官方文档中的说明查询 DSL
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.