在 jpa 2.1 中使用 ManyToMany 时分离的实体
detached entity when using ManyToMany in jpa 2.1
我正在创建一个应用程序,我在 Recipe
和 Ingredients
之间有一个 ManyToMany 关系,
但是当我尝试使用 JPA 2.1 在 RecipeIngredient
中插入新行时,我得到了:
Caused by: javax.persistence.PersistenceException:
org.hibernate.PersistentObjectException: detached entity passed to
persist.
这是我的实体:
`
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CrossCheck
public class Ingredient implements ValidEntity {
static final String PREFIX = "com.architech.sales.ingredient.entity.";
public static final String FIND_ALL = PREFIX + "FIND_ALL";
public static final String FIND_BY_ID = PREFIX + "FIND_BY_ID";
@Id
@GeneratedValue
private long id;
@NotNull
private String label;
private String description;
// added just to reply to @Maciej-Kowalski
@OneToMany(targetEntity = RecetteIngredient.class, mappedBy = "ingredient")
private List<RecetteIngredient> recetteIngredients;
@Version
private long version;
// Getter / Setter
}
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CrossCheck
public class Recette implements ValidEntity{
static final String PREFIX = "sales.recette.entity.Recette.";
public static final String FIND_ALL = PREFIX + "FIND_ALL";
public static final String FIND_BY_ID = PREFIX + "FIND_BY_ID";
@Id
@GeneratedValue
private long id;
@Size(min = 1, max = 256)
private String label;
private String description;
@OneToMany(targetEntity = RecetteIngredient.class, mappedBy = "recette")
private List<RecetteIngredient> recetteIngredients;
@Version
private long version;
// Getter/Setter
}
`
`
@Entity
@IdClass(RecetteIngredientPK.class)
public class RecetteIngredient implements Serializable{
@Id
private long id;
private double qte;
@Id
@ManyToOne(targetEntity = Ingredient.class)
private Ingredient ingredient;
@Id
@ManyToOne(targetEntity = Recette.class)
private Recette recette;
// Getter/Setter
}
`
和我的持久化单元
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="prod" transaction-type="JTA">
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<!--<property name="hibernate.enable_lazy_load_no_trans" value="true" />-->
</properties>
</persistence-unit>
</persistence>
这是堆栈跟踪:
`
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.architech.sales.ingredient.entity.Ingredient
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1171)
at org.jboss.as.jpa.container.AbstractEntityManager.merge(AbstractEntityManager.java:565)
at com.architech.sales.recette_ingredient.boundary.RecetteIngredientManager.save(RecetteIngredientManager.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
`
这就是我努力坚持 RecetteIngredient 的方式:
`
@Stateless
@Interceptors(BoundaryLogger.class)
public class RecetteIngredientManager {
@PersistenceContext
EntityManager em;
public RecetteIngredient findById(long id) {
return this.em.find(RecetteIngredient.class, id);
}
public void delete(long id) {
try {
RecetteIngredient reference = this.em.getReference(RecetteIngredient.class, id);
this.em.remove(reference);
} catch (EntityNotFoundException e) {
//we want to remove it...
}
}
public RecetteIngredient save(RecetteIngredient recetteIngredient) {
return this.em.merge(recetteIngredient);
}
}
`
在您的链接 class 中,在 @ManyToOne
映射上添加合并级联选项:
@Entity
@IdClass(RecetteIngredientPK.class)
public class RecetteIngredient implements Serializable{
@Id
private long id;
private double qte;
@Id
@ManyToOne(targetEntity = Ingredient.class, cascade = CascadeType.MERGE)
private Ingredient ingredient;
@Id
@ManyToOne(targetEntity = Recette.class, cascade = CascadeType.MERGE)
private Recette recette;
这将建议 PersistenceContext 合并 Ingredient 和 Recette 实体,如果它们恰好在当时分离的话。
如果您不允许更改注释,那么您必须在执行链接之前手动保留/合并 Ingredient 和 Recette 实体 table:
public RecetteIngredient save(RecetteIngredient recette) {
this.em.merge(recette.getIngredient());
this.em.merge(recette.getRecette());
return this.em.merge(recette);
}
当然这是一种解决方法,第一种方法肯定更优雅,更可取。
我正在创建一个应用程序,我在 Recipe
和 Ingredients
之间有一个 ManyToMany 关系,
但是当我尝试使用 JPA 2.1 在 RecipeIngredient
中插入新行时,我得到了:
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist.
这是我的实体: `
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CrossCheck
public class Ingredient implements ValidEntity {
static final String PREFIX = "com.architech.sales.ingredient.entity.";
public static final String FIND_ALL = PREFIX + "FIND_ALL";
public static final String FIND_BY_ID = PREFIX + "FIND_BY_ID";
@Id
@GeneratedValue
private long id;
@NotNull
private String label;
private String description;
// added just to reply to @Maciej-Kowalski
@OneToMany(targetEntity = RecetteIngredient.class, mappedBy = "ingredient")
private List<RecetteIngredient> recetteIngredients;
@Version
private long version;
// Getter / Setter
}
@Entity
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@CrossCheck
public class Recette implements ValidEntity{
static final String PREFIX = "sales.recette.entity.Recette.";
public static final String FIND_ALL = PREFIX + "FIND_ALL";
public static final String FIND_BY_ID = PREFIX + "FIND_BY_ID";
@Id
@GeneratedValue
private long id;
@Size(min = 1, max = 256)
private String label;
private String description;
@OneToMany(targetEntity = RecetteIngredient.class, mappedBy = "recette")
private List<RecetteIngredient> recetteIngredients;
@Version
private long version;
// Getter/Setter
}
`
`
@Entity
@IdClass(RecetteIngredientPK.class)
public class RecetteIngredient implements Serializable{
@Id
private long id;
private double qte;
@Id
@ManyToOne(targetEntity = Ingredient.class)
private Ingredient ingredient;
@Id
@ManyToOne(targetEntity = Recette.class)
private Recette recette;
// Getter/Setter
}
`
和我的持久化单元
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="prod" transaction-type="JTA">
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
<!--<property name="hibernate.enable_lazy_load_no_trans" value="true" />-->
</properties>
</persistence-unit>
</persistence>
这是堆栈跟踪:
`
Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.architech.sales.ingredient.entity.Ingredient
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1692)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1171)
at org.jboss.as.jpa.container.AbstractEntityManager.merge(AbstractEntityManager.java:565)
at com.architech.sales.recette_ingredient.boundary.RecetteIngredientManager.save(RecetteIngredientManager.java:47)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
`
这就是我努力坚持 RecetteIngredient 的方式:
`
@Stateless
@Interceptors(BoundaryLogger.class)
public class RecetteIngredientManager {
@PersistenceContext
EntityManager em;
public RecetteIngredient findById(long id) {
return this.em.find(RecetteIngredient.class, id);
}
public void delete(long id) {
try {
RecetteIngredient reference = this.em.getReference(RecetteIngredient.class, id);
this.em.remove(reference);
} catch (EntityNotFoundException e) {
//we want to remove it...
}
}
public RecetteIngredient save(RecetteIngredient recetteIngredient) {
return this.em.merge(recetteIngredient);
}
}
`
在您的链接 class 中,在 @ManyToOne
映射上添加合并级联选项:
@Entity
@IdClass(RecetteIngredientPK.class)
public class RecetteIngredient implements Serializable{
@Id
private long id;
private double qte;
@Id
@ManyToOne(targetEntity = Ingredient.class, cascade = CascadeType.MERGE)
private Ingredient ingredient;
@Id
@ManyToOne(targetEntity = Recette.class, cascade = CascadeType.MERGE)
private Recette recette;
这将建议 PersistenceContext 合并 Ingredient 和 Recette 实体,如果它们恰好在当时分离的话。
如果您不允许更改注释,那么您必须在执行链接之前手动保留/合并 Ingredient 和 Recette 实体 table:
public RecetteIngredient save(RecetteIngredient recette) {
this.em.merge(recette.getIngredient());
this.em.merge(recette.getRecette());
return this.em.merge(recette);
}
当然这是一种解决方法,第一种方法肯定更优雅,更可取。