Фильтрация объектов в поле версии с помощью API-критериев JPA
У меня есть объект со встроенным идентификатором, который содержит идентификатор и поле версии.
@Entity
@Table(name = "MyEntity")
public class MyEntity {
@EmbeddedId
private MyEntityEmbeddedId compositeId;
@Column
private DateTime begin;
@Column
private DateTime end;
}
@Embeddable
public class MyEntityEmbeddedId {
@Column(name = "ID", nullable = false, updatable = false)
private Long id;
@Column(name = "VERSION", nullable = false, updatable = false)
private Long version;
public MyEntityEmbeddedId () {}
}
Мне нужно запросить базу данных с некоторыми критериями в качестве фильтра для сущности. Например, я буду запрашивать дату и список идентификаторов.
List<MyEntity> listEntities = getMyEntities(List<Long> ids, DateTime date);
Мне удается создать запрос, который получает все объекты, для которых их идентификатор находится в списке идентификаторов, и что указанная дата находится между свойствами начала и конца даты в MyEntity.
Но так как у этого объекта есть составной идентификатор, существует много MyEntity с таким же "идентификатором", который может соответствовать параметрам запроса. Я хочу добавить еще один фильтр для извлечения MyEntity с наибольшим номером "версии", если есть много с одинаковым "идентификатором".
Вот пример того, что может быть в базе данных:
+------+-----------+--------------------+
| id | version | began | end |
+------+-----------+--------------------+
| 1 | 1 | ... | ... |
| 1 | 2 | ... | ... |
| 1 | 3 | ... | ... |
| 2 | 1 | ... | ... |
| 2 | 2 | ... | ... |
+------+-----------+--------------------+
Если все предыдущие записи базы данных соответствуют параметрам фильтрации, мне нужно получить только эти, потому что они имеют наибольший номер версии для соответствующего идентификатора:
+------+-----------+--------------------+
| id | version | began | end |
+------+-----------+--------------------+
| 1 | 3 | ... | ... |
| 2 | 2 | ... | ... |
+------+-----------+--------------------+
В настоящее время я делаю запрос, но фильтрую версию в методе внутри моего сервиса, но я бы предпочел, чтобы версия уже была отфильтрована в запросе базы данных. Мне не нужно извлекать записи, которые будут удалены после фильтрации версии.
Я ищу способ самостоятельного соединения с JPA Criteria-API.
По следующей ссылке кажется, что принятый ответ решит мою проблему, потому что мне интересно, как бы я перевел предложенный SQL в JPA Criteria-API.
SQL Выберите только строки с максимальным значением в столбце
select yt1.*
from yourtable yt1
left outer join yourtable yt2
on (yt1.id = yt2.id and yt1.rev < yt2.rev)
where yt2.id is null;
Моя проблема, когда я пытаюсь создать соединение. Вам необходимо предоставить свойство от первого объекта, который ссылается на второй объект в объединении. Поскольку я хочу сделать самостоятельное соединение, в моей сущности нет свойства, которое указывало бы на себя.
Join<MyEntity, MyEntity> selfJoin = root.join(MyEntity_.???);
Как видите, я использую статическую метамодель MyEntity. Есть ли способ сделать самостоятельное соединение с JPA Criteria-API?
Или, может быть, есть другой способ построить мой запрос, чем с помощью самостоятельного левого соединения, как было предложено в другом вопросе stackru.
Я использую Spring Data JPA Specification для построения моего запроса с помощью Predicate. Затем используется findAll(спецификация). У меня уже есть два других метода спецификации для генерации предиката для withIds() и withDate(). Это позволило мне создавать запросы с "неопределенным числом" параметров, предоставленных пользователем.
public static Specification<MyEntity> withIdAndDate(final List<Long> ids,
final DateTime date) {
return new Specification<MyEntity>() {
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query,
CriteriaBuilder cb){
Join<MyEntity, MyEntity> selfJoin = root.join();
Predicate pIds = withIds(ids).toPredicate(root, query, cb);
Predicate pDate = withDate(date).toPredicate(root, query, cb);
return cb.and(pIds, pDate);
}
};
}
Большое спасибо за вашу помощь и предложение!
1 ответ
Наконец, мне удалось найти способ отфильтровать мою MyEntity до самой последней версии с помощью API JPA Criteria, поэтому я поделюсь с вами своим кодом, надеясь, что он кому-нибудь здесь поможет.
public static Specification<MyEntity> withIdAndDate(final List<Long> ids,
final DateTime date) {
return new Specification<MyEntity>() {
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query,
CriteriaBuilder cb){
Subquery<Long> subqueryVersion = query.subquery(Long.class);
Root<MyEntity> entity1 = subqueryVersion.from(MyEntity.class);
Expression<Long> expMaxVersion = cb.greatest(entity1
.get(MyEntity_.compositeId).get(MyEntityEmbeddedId_.version));
Expression<Long> expIdRoot = root.get(MyEntity_.compositeId)
.get(MyEntityEmbeddedId_.id);
Expression<Long> expIdEntity1 = entity1 .get(MyEntity_.compositeId)
.get(MyEntityEmbeddedId_.id);
Expression<Long> expVersionRoot = root.get(MyEntity_.compositeId)
.get(MyEntityEmbeddedId_.version);
Predicate pId = cb.equal(expIdRoot, expIdEntity1);
Predicate pIds = withIds(ids).toPredicate(entity1, query, cb);
Predicate pDate = withDate(date).toPredicate(entity1, query, cb);
subqueryVersion.select(expMaxVersion);
subqueryVersion.where(pId, pIds, pDate);
Predicate pQuery = cb.equal(expVersionRoot, subqueryVersion);
return cb.and(pQuery);
}
};
}
В следующей ссылке принятый ответ помогает мне построить запрос в JPQL, и после этого мне удается перевести его в JPA Criteria API. Я удаляю последнюю часть запроса, которая является частью "И НЕ СУЩЕСТВУЕТ". JPA Выберите последний экземпляр для каждого элемента