Подтвердить что ты не робот

Spring Динамический запрос репозитория 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);

Мне нужно изменить этот запрос, когда текст фильтра будет разделен запятой. Но следующим образом это будет динамический запрос и как его выполнить.

Динамический запрос, который мне нужно создать,

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 + "))";
}
4b9b3361

Ответ 1

Per 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 identifier part. Это означает, что если ваш 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();
}

Ответ 2

Я сам искал решение: Именование интерфейса "Пользовательский" репозитория и его внедрение очень строгие (как сказано там Как добавить пользовательский метод к 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();
    }
}