2016-07-25 11 views
0

Ich bin ein Neuling mit JPA und Hibernate. Ich habe die zwei Entitäten, Anbieter und Produkt, die unten beschrieben werden.Detached Entity Fehler durch Cascade-Attribut

@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; 
    } 
} 

ist mein Problem auf die Verwendung des Attributs nullable = false in der @ManyToOne Annotation zusammen. Ich möchte, dass die entsprechende mit @ManyToOne annotierte Spalte nicht null ist. Allerdings, wenn ich die persitence der Entitäten mit dem folgenden Test testen:

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); 
... 
} 

wo die createOrUpdateEntity Methode Teil eines DAO Bean ist 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; 
    } 

ich den Fehler:

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 

BEARBEITEN: Implementieren dieser example, die eine @ ManyToOne-Spalte verwendet, die mit nullable = false annotiert ist, habe ich festgestellt, dass mein Problem nicht damit zusammenhängt. Wie im vorherigen Serverprotokoll gezeigt, wird das Problem durch die Cascade-Klasse verursacht. Einige solutions in SO vorschlagen CascadeType.ALL von CascadeType.MERGE zu ändern. Andere ones zeigen unterschiedliche Strategien. Sogar einige ones sagen, dass nur die OneToMany-Seite der Assoziation Kaskade verwenden sollte. Ehrlich gesagt, dachte ich, dass die Verwendung von CascadeType.ALL auf beiden Seiten der Assoziation die beste Option ist. Aber es scheint, dass ich falsch lag. Obwohl, wie ich ausgeführt habe, bin ich ein Neuling bei JPA und Hibernate und verstehe die Gründe nicht.

+0

Können Sie uns den Code der Methode createOrUpdateEntity (Object) anzeigen? –

+0

@ArthurNoseda Ich habe es bereits hinzugefügt. – julian

+0

Sie sollten sicherstellen, dass Sie 'merge' richtig verwenden. Es erstellt tatsächlich eine neue Instanz im PersistenceContext und gibt diese Instanz zurück, ohne das Eingabeargument zu ändern. 'persist' bewirkt, dass das übergebene Argument tatsächlich verwaltet wird. – Naros

Antwort

1

Sie können optional Tag für null Kontrolle verwenden:

@ManyToOne(cascade = CascadeType.ALL, optional = false) 
@JoinColumn(name="VENDOR_ID") 
private Vendor vendor; 
+0

Obwohl es eine Option sein könnte, möchte ich wissen, was mit dem Fall 'nullable = false' passiert. – julian

1

Eigentlich mit diesem Code, kann ich Ihr Beispiel an die Arbeit:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>demo</groupId> 
    <artifactId>hibernate4-demo</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <packaging>jar</packaging> 

    <name>hibernate4-demo</name> 
    <url>http://maven.apache.org</url> 

    <properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <hibernate-core.version>4.3.11.Final</hibernate-core.version> 
    </properties> 

    <dependencies> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-core</artifactId> 
     <version>${hibernate-core.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>javax.validation</groupId> 
     <artifactId>validation-api</artifactId> 
     <version>1.1.0.Final</version> 
    </dependency> 
    <dependency> 
     <groupId>org.hibernate</groupId> 
     <artifactId>hibernate-entitymanager</artifactId> 
     <version>${hibernate-core.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
     <version>5.1.6</version> 
     <scope>runtime</scope> 
    </dependency> 
    </dependencies> 
    <build> 
    <plugins> 
     <plugin> 
      <groupId>org.apache.maven.plugins</groupId> 
      <artifactId>maven-compiler-plugin</artifactId> 
      <version>3.5.1</version> 
      <configuration> 
       <source>1.7</source> 
       <target>1.7</target> 
      </configuration> 
     </plugin> 
    </plugins> 
    </build> 
</project> 

BaseEntity.java (mit freundlicher Genehmigung von Brockhaus)

Paket demo.hibernate4;

import java.io.Serializable; 
import java.util.Date; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 
import javax.persistence.MappedSuperclass; 
import javax.persistence.Temporal; 
import javax.persistence.TemporalType; 
import javax.persistence.Version; 

@MappedSuperclass 
public abstract class BaseEntity implements Serializable { 

    @Version 
    private long version; 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 

    @Temporal(TemporalType.DATE) 
    private Date creationDate; 

    public BaseEntity() { 
    //lazy 
    } 

    public Date getCreationDate() { 
    return creationDate; 
    } 

    public void setCreationDate(Date creationDate) { 
    this.creationDate = creationDate; 
    } 

    public long getId() { 
    return id; 
    } 

} 

GenericDAOBean.java

package demo.hibernate4; 

import java.util.List; 
import javax.persistence.EntityManager; 
import javax.persistence.TypedQuery; 

public class GenericDAOBean { 

    private EntityManager em; 

    @Override 
    public <T extends BaseEntity> T createOrUpdateEntity(T entity) { 
    if (entity.getId() == 0) { 
     this.em.persist(entity); 
    } else { 
     this.em.merge(entity); 
    } 
    this.em.flush(); 

    return entity; 
    } 

    public void setEm(EntityManager em) { 
     this.em = em; 
    } 

} 

persistence.xml

<?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="hibernate4-demo" transaction-type="RESOURCE_LOCAL"> 
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <properties> 
     <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/mysql?zeroDateTimeBehavior=convertToNull"/> 
     <property name="javax.persistence.jdbc.password" value=""/> 
     <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> 
     <property name="javax.persistence.jdbc.user" value="root"/> 
     <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/> 
     <property name="javax.persistence.schema-generation.database.action" value="none"/> 
    </properties> 
    </persistence-unit> 
</persistence> 

Main.java

package demo.hibernate4; 

import javax.persistence.EntityManager; 
import javax.persistence.EntityManagerFactory; 
import javax.persistence.EntityTransaction; 
import javax.persistence.Persistence; 

public class Main { 

    public static void main(String[] args) { 
     EntityManagerFactory emf = Persistence.createEntityManagerFactory("hibernate4-demo"); 
     EntityManager em = emf.createEntityManager(); 
     GenericDAOBean service = new GenericDAOBean(); 
     service.setEm(em); 

     EntityTransaction transaction = em.getTransaction(); 
     transaction.begin(); 

     // create vendor1, set name and city 
     Vendor vendor1 = new Vendor(); 
     vendor1.setName("MediaMarkt"); 
     vendor1.setCity("Berlin"); 
     vendor1 = service.createOrUpdateEntity(vendor1); 

     // create product1, set name, price and quantity 
     Product 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); 
     transaction.commit(); 

     System.exit(0); 
    } 

} 

Mit diesem Code und Ihrem Mapping, bekomme ich zwei Zeilen in MySQL und die Beziehung zwischen dem Produc t und der Verkäufer ist gegründet.

Allerdings verstehe ich nicht, was Sie mit Ihrer Liste der Herstellerschlüssel erreichen möchten. Für den Moment bleibt die Tabelle PRODUCT_VENDORKEY leer.

Sie müssen Ihren Kontext weiter beschreiben.

+0

Die Verwendung der Liste 'vendor keys' sollte nur erfahren, wie die '@ ElementCollection' Annotation funktioniert. – julian

+0

In meiner Lösung verwende ich kein Transaction-Objekt, daher bin ich mir nicht sicher, ob es funktionieren wird, weil in Ihrer Hauptklasse der Code für das Behalten der Lieferanten- und Produktobjekte derselbe ist wie in meinem Test. – julian

+0

Sie benötigen eine Transaktion auf die eine oder andere Weise. Es kann von dem Container verwaltet werden, wenn Sie einen verwenden, der von Spring bearbeitet wird, wenn Sie Spring damit umgehen lassen, oder es liegt in Ihrer Verantwortung. Vielleicht könnten Sie dem letzten Teil Ihres Codes auf den Grund gehen? –