it-swarm.com.ru

Spring JPA Repository динамический запрос

В настоящее время я использую следующий базовый пользовательский запрос 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 + "))";
}
15
Channa

В соответствии с JB Nizet и документацией по весенним данным , вы должны использовать пользовательский интерфейс + реализацию репозитория.

Создайте интерфейс с методом:

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();
}
10
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
Obscur Moirage

Spring Data JPA позволяет создавать пользовательские и динамические запросы с помощью «Спецификаций»: Spring Data - Спецификации

Во-первых, ваш интерфейс, который расширяет JPARepository или CRUDRepository, должен также реализовывать JpaSpecificationExecutor<...>, и это все, что вам нужно . В вашем хранилище теперь есть новый метод findAll, который принимает объект Specification<...>, и вы можете использовать метод Beerbajay, используемый для создания запросов критериев, путем переопределения метод toPredicate(...) и там вы можете построить (почти) любой запрос, который вы хотите, так:

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