#java #spring #criteria #specifications #criteria-api
#java #spring #критерии #спецификации #критерии-api
Вопрос:
У меня есть 2 класса, один отец, у которого много детей, например:
Class Person {
@ManyToMany
@JoinTable(name = "person_tag",
joinColumns = {@JoinColumn(name = "person_id",
referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "tag_id",
referencedColumnName = "id")})
List<Tag> tags;
}
Class Tag {
Long id
}
Мне нужно создать запрос критериев, чтобы найти всех пользователей, у которых есть теги, соответствующие списку идентификаторов тегов
Я сохраняю это отношение в новой таблице с 2 столбцами: person_id tag_id
Я реализовал следующее решение:
private Specification<Person> generatePersonIdsSpecification(List<Long> ids) {
return (root, query, cb) -> {
Subquery<Person> sq = query.subquery(Person.class);
Root<Person> person = sq.from(Person.class);
ListJoin<Person, Tag> tagJoin = person.joinList("tags");
sq.select(person).distinct(true).where(tagJoin.get("id").in(ids));
return cb.in(root).value(sq);
};
}
Но я получаю:
Вызвано: java.lang.Исключение IllegalArgumentException: Запрошенный атрибут не был списком в org.hibernate.jpa.criterions.path.AbstractFromImpl.joinList(AbstractFromImpl.java:497) ~ [hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final] в org.hibernate.jpa.criterions.path.AbstractFromImpl.joinList(AbstractFromImpl.java:484) ~ [hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
Комментарии:
1. Вы что-нибудь пробовали? например, выберите * из тега person join на tag.id = person.tag_id где tag.id в (???) . Потому что эти два класса должны быть взаимосвязаны и должны соответствовать требованиям JPA и Hibernate для объектов.
2. Спасибо. Я не думаю, что этот «person.tag_id» будет работать, поскольку у person есть список «тегов». И я предпочитаю делать это с помощью запроса критериев
3. Чтобы мы могли вам помочь, вам необходимо указать структуру ваших объектов в базе данных
Ответ №1:
Такой запрос может быть построен следующим образом:
List<Long> someTagIds ...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> person = query.from(Person.class);
ListJoin<Person, Tag> tagJoin = person.joinList("tags");
query.select(person).distinct(true)
.where(tagJoin.get("id").in(someTagIds));
List<Person> result = em.createQuery(query).getResultList();
Комментарии:
1. Спасибо. Я попытался реализовать это, но я получаю исключение: Вызвано: java.lang. Исключение IllegalArgumentException: Запрошенный атрибут не был списком, что неверно
Ответ №2:
Хорошо, поэтому вместо поиска всех тегов вместе я просто перебираю идентификаторы и генерирую разные спецификации для каждого идентификатора:
private Specification<Person> generateByTagIdSpecification(Long id) {
return (root, query, cb) -> {
Subquery<Person> sq = query.subquery(Person.class);
Root<Person> person = sq.from(Person.class);
Join<Person, Tag> join = person.join("tags");
sq.select(person).distinct(true).where(cb.equal(join.get("id"), id));
return cb.in(root).value(sq);
};
}