Spring JPA Repository动态查询

Cha*_*nna 16 java spring jpa spring-data spring-data-jpa

目前我一直在使用以下Spring JPA Repository基础自定义查询,它工作正常,

 @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);
Run Code Online (Sandbox Code Playgroud)

当过滤器文本将成为逗号分隔值时,我需要修改此查询.但是按照以下方式,它将是一个动态查询,我该如何执行它.

我需要构建动态查询,

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 + "))";
}
Run Code Online (Sandbox Code Playgroud)

bee*_*jay 12

根据JB Nizet和spring-data文档,您应该使用自定义接口+存储库实现.

使用方法创建接口:

public interface MyEntityRepositoryCustom {
    List<User> findByFilterText(Set<String> words);
}
Run Code Online (Sandbox Code Playgroud)

创建一个实现:

@Repository
public class MyEntityRepositoryImpl implements MyEntityRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;

    public List<User> findByFilterText(Set<String> words) {
        // implementation below
    }
}
Run Code Online (Sandbox Code Playgroud)

扩展现有Repository接口中的新接口:

public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntityRepositoryCustom {
    // other query methods
}
Run Code Online (Sandbox Code Playgroud)

最后,在其他地方调用该方法:

dao.findByFilterText(new HashSet<String>(Arrays.asList(filterText.split(","))));
Run Code Online (Sandbox Code Playgroud)

查询实现

生成sql变量的方法,即通过将一些字符串连接到查询中是不好的.不要这样做.

word你所串联必须是一个有效的JPQL标识符,即一个:接着一个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();
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢您提供有价值的完整答案。由于编译器失败,我应用了以下更正, cb.lower(word) --&gt; cb.lower(wordLiteral ) q.select(doc) --&gt; q.select(user) 但是当我实施这个解决方案时,我做了这些更正将在应用程序部署时出现以下错误:- 原因:org.springframework.data.mapping.PropertyReferenceException:未找到 com.ord.model.User 类型的属性过滤器 (2认同)
  • @Channa我在存储库名称中有一个错误,它必须是`MyEntityRepositoryImpl`,而不是`MyEntityRepositoryCustomImpl`.看看改变是否有帮助. (2认同)

Bar*_*rge 8

我自己一直在寻找解决方案:“自定义”存储库接口的命名和实现非常严格(如How to add custom method to Spring Data JPA中所述)

所以,要清楚的是,整个代码:(但@beerbajay 是对的)

自定义方法接口

public interface MyEntityRepositoryCustom {
    List<MyEntity> findSpecial();
}
Run Code Online (Sandbox Code Playgroud)

自定义方法的实现

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

“原始”存储库

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity,Long>, MyEntityRepositoryCustom {
    //original methods here... do not redefine findSpecial()...
}
Run Code Online (Sandbox Code Playgroud)

您现在可以将“原始”存储库与新的自定义方法一起使用

@Service
public class MyService {
    @Autowired
    private DataRepository r;

    public void doStuff() {
        List<Object> list = r.findSpecial();
    }
}
Run Code Online (Sandbox Code Playgroud)