#java #hibernate #jpa #criteria-api
#java #спящий режим #jpa #критерии-api
Вопрос:
Модель:
@Entity
public class User {
@Id
private Integer id;
@JoinColumn(name = "user_id")
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Project> projects;
}
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Type")
public abstract class Project {
@Id
private Integer id;
private String name;
}
@Entity
@DiscriminatorValue("Administrative")
public class AdminProject extends Project {
private String departmentName;
}
@Entity
@DiscriminatorValue("Design")
public class DesignProject extends Project {
private String companyName;
}
Я пытаюсь использовать api критериев JPA для запроса User
объектов на основе атрибута реализации Project
. Например, запросите всех пользователей, у которых есть проект с отделом «SOME_NAME» (это поле не существует DesignProject
).
Я вижу, что есть способ сделать это путем понижения значения Project
объекта для запроса. Я пытаюсь сделать что-то похожее на:
CriteriaBuilder cb...
Root<User> userRoot...
root = ((From) root).join("projects", JoinType.LEFT);
root = cb.treat(root, AdminProject.class);
root = root.get("departmentName");
Исключение:
исключение org.springframework.dao.InvalidDataAccessApiUsageException: исключение org.hibernate.hql.internal.ast.QuerySyntaxException: недопустимый путь: ‘generatedAlias2.DepartmentName’ [выберите generatedAlias0 из io.github.perplexhub.rsql.model.Пользователь как сгенерированный alias0 оставил соединение сгенерированным alias0.projects как сгенерированный alias1, где treat(сгенерироВанный alias2 как io.github.perplexhub.rsql.model.AdminProject).DepartmentName=:param0]; вложенным исключением является java.lang.Исключение IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Недопустимый путь: ‘generatedAlias2.DepartmentName’ [выберите generatedAlias0 из io.github.perplexhub.rsql.model.Пользователь как сгенерированный alias0 оставил соединение сгенерированным alias0.projects как сгенерированный alias1, где treat(сгенерироВанный alias2 как io.github.perplexhub.rsql.model.AdminProject).DepartmentName=:param0]
Чего мне не хватает? Связано ли это с объединением или с тем, как впоследствии происходит понижение?
Редактировать
После ответа @K.Николас, мне удалось заставить запрос работать в изолированном сценарии, но не в моем приложении. Но я заметил, что entityManager.createQuery(query)
вызов вызывает исключение, указанное выше, при первом вызове, и оно работает, если я вызываю его снова без изменения объекта запроса. Вот запрос, сгенерированный при втором вызове (этот запрос находит нужные мне объекты из базы данных):
выберите generatedAlias0 от пользователя как generatedAlias0 левое соединение generatedAlias0.projects как generatedAlias2, где treat(generatedAlias2 как io.github.perplexhub.rsql.model.AdminProject).DepartmentName=:param0
Почему диспетчер объектов создает два разных запроса при вызове два раза подряд?
Ответ №1:
Я бы сделал Entitys
это немного по-другому, как вы увидите. Основная проблема заключается в том, что вы используете User
в качестве своего корня соединение со списком Projects
. Это вызывает беспокойство, потому что у вас должен быть внешний ключ в Project
классе и использовать projects
поле как поле только для запроса. Это то, что я сделал. Так работает лучше. Это также вызывает беспокойство, потому что вам нужно сделать a join fetch
вместо a join
, чтобы projects
получить выборку вместе с users
.
Итак, во-первых, объекты выглядят так:
@Entity
public class User {
@Id
private Integer id;
@OneToMany(mappedBy="user")
private List<Project> projects;
}
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Type")
public abstract class Project {
@Id
private Integer id;
private String name;
@ManyToOne
private User user;
}
@Entity
@DiscriminatorValue("Administrative")
public class AdminProject extends Project {
private String departmentName;
}
@Entity
@DiscriminatorValue("Design")
public class DesignProject extends Project {
private String companyName;
}
Немного покопавшись, я нашел запрос JPQL, который делает трюк. Это было отправной точкой:
List<User> users = entityManager.createQuery("select distinct(u) from User u join fetch u.projects p where TYPE(p) = 'Administrative' and p.departmentName = 'dept1'", User.class).getResultList();
После еще немного копания я обнаружил, что все treat
работает нормально, если вы делаете это правильно, и что с JPA 2.1 вы должны использовать EntityGraph
do get join
для выполнения a fetch
.
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class);
Join<User, Project> join = root.join("projects");
query.select(root).where(builder.equal(builder.treat(join, AdminProject.class).get("departmentName"), "dept1"));
EntityGraph<User> fetchGraph = entityManager.createEntityGraph(User.class);
fetchGraph.addSubgraph("projects");
users = entityManager.createQuery(query.distinct(true)).setHint("javax.persistence.loadgraph", fetchGraph).getResultList();
В качестве примечания запросы, сгенерированные как немного отличающиеся, но я не смотрел на них так внимательно. Вы должны.
Комментарии:
1. Спасибо за ответ. Я протестировал двунаправленную связь, и график выборки на самом деле не нужен. Ваше решение работает даже без него. У меня есть то, что исключение по моему вопросу возникает при первом вызове
entityManager.createQuery(query)
, но не во второй раз. Я отредактирую свой вопрос с учетом достижений, которые я сделал с вашим ответом .