如何使用 JPA 和实体对象生命周期
How to work with JPA and the Entity Object Life Cycle
我正在使用 JPA Eclipselink 开发一个简单的 Surveysystem,我在这方面(JPA 和数据库)还很陌生,所以我不确定应该采用哪种方法。
到目前为止,我做事的方式是对所有数据库逻辑使用 DAO-class,并且(希望)我的所有实体关系都得到了正确的注释。
我的教师实体可以有多个调查,每个调查实体可以有多个问题。这就是我得到关系注释的方式:
Teacher.java
@OneToMany(mappedBy = "teacher", cascade = CascadeType.ALL)
private List<Survey> surveys = new ArrayList<Survey>();
Survey.java
@ManyToOne
@JoinColumn(name = "teacherid", referencedColumnName = "id")
private Teacher teacher;
@OneToMany(mappedBy = "survey", cascade = CascadeType.ALL)
private List<Question> questions = new ArrayList<Question>();
Question.java
@ManyToOne
@JoinColumn(name = "surveyid", referencedColumnName = "id")
private Survey survey;
在每个 class 中,我都有一个像这样的 addSometing() 方法:
survey.addQuestion(q)
public void addQuestion(Question q) {
if (q != null) {
q.setSurvey(this);
questions.add(q);
}
}
最后,我的 DAO 中每个实体的另一个 addSomething() 方法如下所示:
public void addSurvey(Survey survey) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.persist(survey);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx.isActive()) {
tx.rollback();
}
} finally {
em.close();
}
}
当我 运行 我的代码时,我成功地创建了一个教师对象并将其添加到数据库中。然后我创建一个问题对象并将其添加到一个新的调查对象并将其添加到我的 Teacher 中,这也能正常工作。但是当我然后尝试将我的问题和调查对象保存到数据库和 merge/update 教师对象时,我得到一个 InvalidStateException:
Encountered unmanaged object "no.hib.dat104.obl3.model.Survey@52892ae7" in life cycle state unmanaged while cascading persistence via field "no.hib.dat104.obl3.model.Question.survey" during flush. However, this field does not allow cascade persist. You cannot flush unmanaged objects or graphs that have persistent associations to unmanaged objects.
我不认为我做这些事情的方式是使用 JPA 实体的正确方式。谁能给我一个简短的解释,说明我应该如何做或指出正确的方向?我知道 Entity Object Life Cycle,但我不确定如何在我的项目中处理它。
您在 addSurvey 方法中获取的 EntityManager 上下文是新的,因此是空的。大概当您坚持调查时,它有问题和分配的老师。在您的模型中,教师实例是数据库中的现有对象,并且从 Survey 到 Teacher 的引用没有级联选项 - JPA 在遇到模型中的非托管对象时需要抛出异常,因为它不知道你打算用它做什么。
在您的模型中有很多方法可以解决这个问题,但它们取决于许多其他外部因素。我能想到的最快的解决方案是稍微改变一下,让 ManyToOne 老师参考使用 cascade = CascadeType.Merge
选项,然后在 addSurvey 方法中使用 em.merge(survey);
。然后 JPA 将确定调查和问题是新的并插入它们,同时它将现有教师实例中的任何更改合并到数据库中。
还有许多其他选项,例如使用扩展的持久性单元,该单元可用于在教师实例中读取以及持久化您的调查和问题实例。什么最适合您的应用程序取决于您期望在不同情况下发生什么。
我正在使用 JPA Eclipselink 开发一个简单的 Surveysystem,我在这方面(JPA 和数据库)还很陌生,所以我不确定应该采用哪种方法。
到目前为止,我做事的方式是对所有数据库逻辑使用 DAO-class,并且(希望)我的所有实体关系都得到了正确的注释。
我的教师实体可以有多个调查,每个调查实体可以有多个问题。这就是我得到关系注释的方式:
Teacher.java
@OneToMany(mappedBy = "teacher", cascade = CascadeType.ALL)
private List<Survey> surveys = new ArrayList<Survey>();
Survey.java
@ManyToOne
@JoinColumn(name = "teacherid", referencedColumnName = "id")
private Teacher teacher;
@OneToMany(mappedBy = "survey", cascade = CascadeType.ALL)
private List<Question> questions = new ArrayList<Question>();
Question.java
@ManyToOne
@JoinColumn(name = "surveyid", referencedColumnName = "id")
private Survey survey;
在每个 class 中,我都有一个像这样的 addSometing() 方法:
survey.addQuestion(q)
public void addQuestion(Question q) {
if (q != null) {
q.setSurvey(this);
questions.add(q);
}
}
最后,我的 DAO 中每个实体的另一个 addSomething() 方法如下所示:
public void addSurvey(Survey survey) {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.persist(survey);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx.isActive()) {
tx.rollback();
}
} finally {
em.close();
}
}
当我 运行 我的代码时,我成功地创建了一个教师对象并将其添加到数据库中。然后我创建一个问题对象并将其添加到一个新的调查对象并将其添加到我的 Teacher 中,这也能正常工作。但是当我然后尝试将我的问题和调查对象保存到数据库和 merge/update 教师对象时,我得到一个 InvalidStateException:
Encountered unmanaged object "no.hib.dat104.obl3.model.Survey@52892ae7" in life cycle state unmanaged while cascading persistence via field "no.hib.dat104.obl3.model.Question.survey" during flush. However, this field does not allow cascade persist. You cannot flush unmanaged objects or graphs that have persistent associations to unmanaged objects.
我不认为我做这些事情的方式是使用 JPA 实体的正确方式。谁能给我一个简短的解释,说明我应该如何做或指出正确的方向?我知道 Entity Object Life Cycle,但我不确定如何在我的项目中处理它。
您在 addSurvey 方法中获取的 EntityManager 上下文是新的,因此是空的。大概当您坚持调查时,它有问题和分配的老师。在您的模型中,教师实例是数据库中的现有对象,并且从 Survey 到 Teacher 的引用没有级联选项 - JPA 在遇到模型中的非托管对象时需要抛出异常,因为它不知道你打算用它做什么。
在您的模型中有很多方法可以解决这个问题,但它们取决于许多其他外部因素。我能想到的最快的解决方案是稍微改变一下,让 ManyToOne 老师参考使用 cascade = CascadeType.Merge
选项,然后在 addSurvey 方法中使用 em.merge(survey);
。然后 JPA 将确定调查和问题是新的并插入它们,同时它将现有教师实例中的任何更改合并到数据库中。
还有许多其他选项,例如使用扩展的持久性单元,该单元可用于在教师实例中读取以及持久化您的调查和问题实例。什么最适合您的应用程序取决于您期望在不同情况下发生什么。