web-dev-qa-db-ja.com

Spring JPAリポジトリの動的クエリ

現在、私は次の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 + "))";
}
16
Channa

JB Nizetおよび spring-data documentation に従って、カスタムインターフェイス+リポジトリ実装を使用する必要があります。

メソッドを使用してインターフェースを作成します。

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
    }
}

既存のリポジトリインターフェイスで新しいインターフェイスを拡張します。

public interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyEntityRepositoryCustom {
    // other query methods
}

最後に、メソッドを別の場所で呼び出します。

dao.findByFilterText(new HashSet<String>(Arrays.asList(filterText.split(","))));

クエリ実装

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();
}
11
beerbajay

私は自分で解決策を探していました:「カスタム」リポジトリインターフェースと実装の命名は非常に厳密です(そこにあるように 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();
    }
}
3
Barium Scoorge

Spring Data JPAには、「仕様」でカスタムクエリと動的クエリを作成する方法があります。 Spring Data-Specifications

まず、JPARepositoryまたはCRUDRepositoryを拡張するインターフェイスも_JpaSpecificationExecutor<...>_を実装する必要があります。これで十分です。リポジトリに_Specification<...>_オブジェクトを受け入れる新しいメソッドfindAllが追加されました。メソッドtoPredicate(...)をオーバーライドすることにより、Certeriaクエリの作成に使用されるメソッドBeerbajayを使用できます。 (ほぼ)任意のクエリを自由に構築できます。

_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の問題が解決されます(カスタムインターフェイスがないため)

2
Tom Elias