【发布时间】:2016-11-28 17:12:15
【问题描述】:
我是 JPA 和 Hibernate 的新手。我有两个实体,供应商和产品,如下所述。
@Entity
public class Vendor extends BaseEntity
{
private static final long serialVersionUID = 267250313080292374L;
@NotNull
private String name;
@NotNull
private String city;
@OneToMany(mappedBy = "vendor", cascade = CascadeType.ALL)
private List<Product> products = new ArrayList<Product>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public List<Product> getProducts() {
return products;
}
public void setProducts(List<Product> products) {
this.products = products;
}
}
@Entity
public class Product extends BaseEntity
{
private static final long serialVersionUID = -4676899182130380017L;
@NotNull
private String name;
@Column(nullable = false, precision = 10, scale = 2)
private double price;
@NotNull
private int quantity;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="VENDOR_ID", nullable = false)
private Vendor vendor;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public Vendor getVendor() {
return vendor;
}
public void setVendor(Vendor vendor) {
this.vendor = vendor;
}
}
我的问题与@ManyToOne注解中属性nullable = false的使用有关。我希望用@ManyToOne 注释的相应列不为空。但是,当我使用以下测试测试实体的持久性时:
public void testCreateEntity()
{
// create vendor1, set name and city
vendor1 = new Vendor();
vendor1.setName("MediaMarkt");
vendor1.setCity("Berlin");
vendor1 = service.createOrUpdateEntity(vendor1);
// create product1, set name, price and quantity
product1 = new Product();
product1.setName("Samsung Galaxy S7");
product1.setPrice(new Double("819.00"));
product1.setQuantity(1);
vendor1.getProducts().add(product1);
product1.setVendor(vendor1);
// create product at service
product1 = service.createOrUpdateEntity(product1);
...
}
createOrUpdateEntity 方法是 DAO Bean 对象的一部分
@PersistenceContext
private EntityManager em;
public <T extends BaseEntity> T createOrUpdateEntity(T entity)
{
// as we have no id yet, it must be freshly brewed
if (entity.getId() == 0)
{
log.debug("createOrUpdateEntity::create:: {}", entity);
this.em.persist(entity);
}
// if there is an id, we must have dealt with it before
else
{
log.debug("createOrUpdateEntity::update:: {}", entity);
this.em.merge(entity);
}
this.em.flush();
return entity;
}
我得到错误:
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: de.brockhaus.stock.entity.Stock at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:765) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:758) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80) at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:398) at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:323) at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:162) at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:111) at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:425) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:249) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:178) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121) at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753) at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146) ... 138 more
编辑:实现这个example,它使用注释为nullable = false 的@ManyToOne 列,我意识到我的问题与此无关。如之前的服务器日志所示,问题是由 Cascade 类引起的。 SO中的一些solutions建议通过CascadeType.MERGE更改CascadeType.ALL。其他ones 显示不同的策略。甚至一些ones 告诉只有OneToMany 的关联方应该使用级联。坦率地说,我认为在关联的双方都使用 CascadeType.ALL 是最好的选择。但似乎我错了。尽管正如我所指出的,我是 JPA 和 Hibernate 的新手,但我不明白其中的原因。
【问题讨论】:
-
能给我们看看createOrUpdateEntity(Object)方法的代码吗?
-
@ArthurNoseda 我已经添加了。
-
您可能需要确保正确使用
merge。它实际上在 PersistenceContext 中创建了一个新实例,并在不更改输入参数的情况下返回该实例。persist实际上使传递的参数成为托管的。 -
@Naros 我不明白你想告诉我什么。
-
docs.oracle.com/javaee/6/api/javax/persistence/… - 基本上你应该使用
entity = this.em.merge(entity)。