I am newbie with JPA and Hibernate. I have the two entities, Vendor and Product, described below.
@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;
}
}
My problem is related to the usage of the attribute nullable = false
in the @ManyToOne annotation. I want that the corresponding column annotated with @ManyToOne to be not null. However when I test the persitence of the entities with the following test:
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);
...
}
where the createOrUpdateEntity method is part of a DAO Bean Object
@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;
}
I get the error:
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
EDIT: Implementing this example, which uses a @ManyToOne column annotated as nullable = false, I have realized that my problem is not related to this. Such as shown in the previous server log, the problem is being caused by the Cascade class. Some solutions in SO suggest to change CascadeType.ALL by CascadeType.MERGE. Other ones show different strategies. Even some ones tell that only the OneToMany side of the association should use cascade. Frankly, I thought that using CascadeType.ALL in both sides of the association was the best option. But it seems that I was wrong. Although, such as I have pointed out, I am newbie with JPA and Hibernate and I do not understand the reasonings.