Hibernate HQL:找不到要查询的实体

Hibernate HQL : no entity found for query

花了一天时间尝试解决这个问题,但仍然没有成功。 我有 测试实体 :

public class Test {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "duration", nullable = false)
private int duration;
@Column(name = "test_name", nullable = false, unique = true)
private String testName;
@Column(name = "archived", nullable = false)
private boolean archived;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "test", fetch = FetchType.EAGER)
private Set<Question> questions;
@ManyToMany(mappedBy = "tests")
private Set<User> users;

这个测试实体组问题,这样问题实体一组答案

我编写了 HQL 查询来进行测试:

 @NamedQuery(name = "getCurrentTestById",
            query = "SELECT test From Result result JOIN result.test test JOIN result.user user " +
                    "JOIN test.questions question JOIN question.answers answer " +
                    "WHERE test.id = :testId AND user.id = :userId " +
                    "AND result.permission.id = :permissionId AND question.isArchived = false AND answer.isArchived = false")

我的 DAO:

    public Test getCurrentTest(long id, long testId, long permissionId){
    Query query = em.createNamedQuery("getCurrentTestById");
    query.setParameter("userId", id);
    query.setParameter("testId", testId);
    query.setParameter("permissionId", permissionId);
    return (Test)query.getSingleResult();
}

所以,一切正常,直到我将任何问题或答案设置为 "archived"。 我仍然得到所有这些,即使它们已存档。此外,如果我将当前测试的所有 questions\answers 集合标记为已存档 - 我将得到异常:没有找到用于查询的实体

我认为我们对这里的 Hibernate 概念有一些误解。

首先,在使用 Hibernate 时,您不能仅从使用查询的实体的确定集合中获取几行,换句话说,当您检索实体时,获取其中之一集合,也将检索此集合中存在的所有记录,甚至使用查询专门从集合中删除那些不需要的记录

一种方法是使用 Hibernate's filtering,但这需要对您的映射和配置进行一些更改,因此我不会对这种方法做更多解释,而是使用您的实际配置解释如何解决您的问题。

在您显示的查询中,您只是告诉 Hibernate 您想要一个测试记录,其中的问题和答案未存档,因此无论如何 Hibernate 都能够确定测试实体中的集合也应该被 WHERE 子句过滤。

从测试中获取所有未存档问题的一种方法是直接从测试实体中选择问题对象,在其中创建所需的 WHERE,如下所示:

SELECT question FROM Result result 
  JOIN result.test test 
  JOIN result.user user
  JOIN test.questions question
  JOIN question.answers answer
 WHERE test.id = :testId
   AND user.id = :userId
   AND result.permission.id = :permissionId 
   AND question.isArchived = false 
   AND answer.isArchived = false

在此示例中,您将从测试中收到一组问题,这些问题也将被您的 WHERE 子句过滤。

但是,如果您确实需要为您的逻辑使用测试实体,您可以在测试实体中创建一个 get 方法,该方法将 return 一组未归档的问题,然后在您的逻辑中使用此方法的结果:

   public Set<Question> getQuestionsNotArchived() {
        Set<Question> notArchivedQuestions = new HashSet<>();
        for (Question question : questions) {
            if (!question.isArchived()) {
                 notArchivedQuestions.add(question);
            }
        }
        return notArchivedQuestions;
   }

这是一种非常好的过滤 Hibernate 集合的方法,我个人经常使用它。

这样我们就解决了您描述的第一个问题,因为现在您可以获得一个包含问题集合过滤值的测试实体。

现在,对于第二个问题,找不到查询的实体,这很简单:出现错误是因为您正在使用查询中的 getSingleResult() class,所以如果它没有从你的查询中找到任何结果,它将抛出一个异常。你没有得到任何结果的原因是因为你在你的 WHERE 子句中明确地说你只想要至少有一个未存档的 Question/Answer 的测试,正如你也说过的,它只发生在你标记时所有 Question/Answer 都已存档,因此错误是意料之中的,因为实际上您没有任何 Question/Answer 未存档的测试。

希望通过这些解释可以解决您的问题,祝您好运!

我发现最好的解决方案是使用测试实体进行保存(如果您不想创建包装器或多个查询)并编写 NATIVE SQL 查询:

@NamedNativeQuery(name = "getCurrentTestById",
            query = "SELECT t.id as tId, t.test_name, t.duration, q.id as qId, " +
                    "q.question, q.is_multichoice, q.is_open, a.id as aId, a.answer_text  FROM result r " +
                    "JOIN test t ON r.test_id = t.id " +
                    "JOIN user u ON r.user_id = u.id " +
                    "JOIN question q ON t.id = q.test_id JOIN answer a ON q.id = a.question_id " +
                    "WHERE t.id = :testId AND u.id = :userId AND r.permission = :permissionId " +
                    "AND q.archived = false AND a.archived = false")