JPA - найти по нескольким атрибутам в коллекциях объектов - PullRequest
0 голосов
/ 06 сентября 2018

У меня есть объект события со следующими атрибутами:

class Event {
    String name;
    String location;
    LocalDateTime date;
    String description;
}

Допустим, я получаю из веб-API список событий:

List<Events> events = getEvents(); // e.g. 5 events

А теперь я хочу проверить, сколько из этих событий у меня уже есть в моей БД.

Событие является уникальным, если комбинация значений: имя, местоположение и дата также уникальны.

Итак, в основном я хочу создать запрос для этого:

Optional<Event> getByNameAndLocationAndDate(String name, String location, LocalDate date);

, но для списка элементов в одном запросе. Что-то вроде:

Optional<Event> getByNameAndLocationAndDate(List<Events> events);

Возможно ли это с JPA?

Ответы [ 2 ]

0 голосов
/ 07 сентября 2018

try

List<Event> findByNameInAndLocationInAndDateIn(List<String> names,List<String> locations,List<Date> dates);

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

Я не уверен, что эта функция работает так, как вы хотите

0 голосов
/ 06 сентября 2018

Нет встроенного или особенно красивого способа сделать это. Но вы можете сгенерировать запрос, используя цикл:

public List<Event> getByNameAndLocationAndDate(List<Event> events) {
    if (events.isEmpty()) {
        return new ArrayList<>();
    }
    final StringBuilder queryBuilder = new StringBuilder("select e from Event e where ");
    int i = 0;
    for (final Event event : events) {
        if (i > 0) {
            queryBuilder.append("or")
        } 
        queryBuilder.append(" (e.name = :name" + i);
        queryBuilder.append(" and e.location = :location" + i);
        queryBuilder.append(" and e.date = :date" + i + ") ");
        i++;
    }
    final TypedQuery<Event> query = em.createQuery(queryBuilder.toString());

    int j = 0;
    for (final Event event : events) {
        query.setParameter("name" + j, event.getName());
        query.setParameter("location" + j, event.getLocation());
        query.setParameter("date" + j, event.getDate());
    }
    return query.getResultList();
}

Как я уже сказал, не очень красиво. Может быть лучше с критериями API. Опять же, если у вас нет очень строгих требований к скорости выполнения, вам может быть лучше перебрать список, проверяя одно событие за раз. Это приведет к большему количеству запросов к базе данных, а также к более красивому коду.

Редактировать: Вот попытка с использованием API критериев, не использовала его так, созданный просто путем поиска в Google, нет гарантии, что он работает как есть.

public List<Event> getByNameAndLocationAndDate(List<Event> events) {
    if (events.isEmpty()) {
        return new ArrayList<>();
    }
    final CriteriaBuilder cb = em.getCriteriaBuilder();
    final CriteriaQuery<Event> query = cb.createQuery(Event.class);
    final Root<Event> root = query.from(Event.class);

    final List<Predicate> predicates = new ArrayList<>();

    final List<Predicate> predicates = events.stream().map(event -> {
                return cb.and(cb.equal(root.get("name"), event.getName()),
                              cb.equal(root.get("location"), event.getLocation()),
                              cb.equal(root.get("date"), event.getDate()));
    }).collect(Collectors.toList());

    query.select(root).where(cb.or(predicates.toArray(new Predicate[]{})));

    return em.createQuery(query).getResultList();
}
...