JPA vs Hibernate.cfg und Entitymanager

dmike

Bekanntes Mitglied
Ich habe gerade ein Problem mit dem Wegschreiben von Instanzen in die Datenbank wobei ich den Entitymanager von JPA 2.0 verwenden möchte.

Die Instanz um die es geht hat intern eine Liste (ein HashSet) mit Kind-Instanzen.

Also z.B. in der Art:

Mother mother = new Mother();
mother.addChild(new Child("child #1"));
mother.addChild(new Child("child #2"));

Jedesmal, wenn per mother.addChild() ein Kind hinzugefügt wird, wird dessen setMother() Methode aufgerufen und eine Verbindung zur Mother Instanz hergestellt.

Java:
class Mother {
  public void addChild(Child child) {
     this.children.add(child);
     child.setMother(this);
  }
}

Nun kann es aber passieren, dass zum Beispiel "child #2" bereits in der Datenbank existiert. Daher suche ich in der Datenbank zuerst nach den vorhandenen Kindern, wenn ich welche finde, dann werden diese sofort der Mutter zugewiesen mother.addChild(childFromDatabase). Neue Kinder werden per "mother.add(new Child(child #n))" wie oben gezeigt hinzugefügt.

Jetzt habe ich mal zwei Alternative durchgespielt:

Zum einen verwende ich den

a) JPA EntityManager und konfiguriere ihn über eine Hibernate Konfiguration.

b) Beim zweiten mal nehme ich den EntityManager und benutze aber eine reine JPA 2.0 Konfiguration.

Das Object "mother", dass ich in die Datenbank schreiben möchte, lässt sich nur mit a) persistieren (also plain Hibernate)

Über b) bekomme ich ein "detached entity passed to persist error" und damit ist eins der Kinder gemeint, dass sich bereits in der Datenbank befindet.

In beiden Fällen machen ich ein "em.persist(mother)" (em = EntityManager Instanz)


Vielleicht sieht ja jemand was genau das Problem ist. Liegt's an der Transaktion?

Den EntityManager für Hibernate konfigurier ich so:

EntityManager em = Persistence.createEntityManagerFactory("hbm.conf").createEntityManager();

Das ist die Hibernate Konfiguration hbm.conf

[XML]
<hibernate-configuration>
<session-factory name="moreevent">
<!-- postgreSQL configuration -->
<property name="connection.url">${jdbc.url}/${jdbc.db.name}</property>
<property name="connection.driver_class">${jdbc.driver}</property>
<property name="connection.username">${jdbc.user}</property>
<property name="connection.password">${jdbc.password}</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<!-- property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property-->

<!-- Session handling -->
<property name="current_session_context_class">thread</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.connection.autocommit">true</property>

<!-- Connection Pool -->
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>

<!-- Debug Ausgaben de/aktivieren -->

<!-- Diese Einstellung aktiviert die Uebergabe der SQL-Statements an log4j -->
<property name="show_sql">true</property>

<!-- Diese Einstellung sorgt bei der SQL-Ausgabe für eine schoene Formatierung.
Siehe auch log4j.xml wo die Log-Einstellungen für Hibernate SQL gesetzt werden.
-->
<property name="format_sql">true</property>

<!-- Die Einstellung ergaenzt einen Kommentar, der anzeigt durch welche HQL oder Criteria-Abfrage das SQL erzeugt wurde. -->
<property name="use_sql_coments">true</property>

<!-- Annotation - Mapping -->
<property name="transaction.auto_close_session">true</property>
<property name="transaction.flush_before_completion">true</property>


</session-factory>
</hibernate-configuration>
[/XML]



Den EntityManager für JPA instanziere ich über Spring's DI

[XML]
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="pgsql_integration_local_testing" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
</bean>
</property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}/${jdbc.db.name}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

[/XML]
 
Zuletzt bearbeitet:
"detached entity" bedeutet normalerweise, dass der Entity zuerst mit dem Persitence Manager synchronisiert werden muss.

Das geht mit EntityManager.merge(entity).
 
AFAIK muss man den Entitymanager explizit closen oder clearen damit eine Instanz detached wird. So lange das nicht passiert lebt das Objekt im Persistence Context, ist also attached.
Ich habe in beiden Fällen den gleichen Code (nur die Konfiguration ist anders: einmal direkt über die hbm.conf das andere mal über spring). Was ich nicht ganz verstehe ist, warum es überhaupt zum detached kommen kann. Ich mache ja nirgends ein close oder so was.

Im konkreten Beispiel ist Mother = TextResource und Child = TextResourceType
.

Und so erhält jede TextResource eine Liste von TextResourceTypes
Java:
   @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, fetch = FetchType.EAGER)
    @JoinTable(name = "TEXT_RESOURCE_WITH_TYPE", joinColumns = { @JoinColumn(name = "TEXT_RESOURCE_ID") }, inverseJoinColumns = { @JoinColumn(name = "TEXT_RESOURCE_TYPE_ID") })
    private Set<TextResourceType> textResourceTypes = new HashSet<TextResourceType>();




Der Code sieht so aus. Bei Aufruf von em.persist gibts die Exception.



Java:
@Transactional(propagation=Propagation.REQUIRED)
private void fillTables() {

 for (TextResource textResourceFromXML : textResourcesFromXML) {
                Set<TextResourceType> typesFromXML = textResourceFromXML.getTextResourceTypes();
                Set<TextResourceType> typesForDB = new HashSet<TextResourceType>();

                for (TextResourceType typeFromXML : typesFromXML) {
                    TextResourceType typeFromDB = textResourceTypeRepo.findOne(TextResourceType.$.type.eq(typeFromXML.getType()));
                    if (typeFromDB!=null) {
                        log.debug("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  FOUND: " + typeFromDB.getDescription());
                        typesForDB.add(typeFromDB);
                    } else {
                        typesForDB.add(typeFromXML);
                    }
                }

                TextResource databaseTextResource = new TextResource(textResourceFromXML.getKey()
                                                                   , textResourceFromXML.getLanguageCode()
                                                                   , textResourceFromXML.getValue()
                                                                   , typesForDB);


                em.persist(databaseTextResource);
            }
} //eof filltables
 
Hallo Mike,

verwendest du in beiden Fällen einund denselben Persistence Provider?
da kann es mämlich durchaus unterschiede geben und sich das Verhalten erklären lassen.
 
Hallo Mike,

verwendest du in beiden Fällen einund denselben Persistence Provider?
da kann es mämlich durchaus unterschiede geben und sich das Verhalten erklären lassen.

Ja, schon das ist beides mal Hibernate (auch die Version ist identisch)

Ich vermute, dass irgendwo irgendwer die Session schliesst, ich versteh halt bloss noch nicht wer das macht, denn eigentlich steuert ja die umgebende Transaktion den Gesamtablauf. Wie es dann trotz laufender Transaktion zu detachten Objekten kommen kann ist schon merkwürdig.
 
Hi Mike,
wie ich sehe, verwendest du unterschiedliche Transaction-Manager.

Versuch im zweiten Beispiel mal den

anstatt
org.springframework.orm.jpa.JpaTransactionManager

den für Hibernate

org.springframework.orm.hibernate.HibernateTransactionManager

Das fällt mir jetzt auf die Schnelle ein.
 
Hi Mike,
wie ich sehe, verwendest du unterschiedliche Transaction-Manager.

Versuch im zweiten Beispiel mal den

anstatt
org.springframework.orm.jpa.JpaTransactionManager

den für Hibernate

org.springframework.orm.hibernate.HibernateTransactionManager

Das fällt mir jetzt auf die Schnelle ein.

Jep, das ist es gewesen. Super danke!
Ich werde die Tage die XML Konfiguration dazu posten.
wobei ich hibernate3 benutze... also...
org.springframework.orm.hibernate3.HibernateTransactionManager


Wohl doch zu früh gefreut:

object is an unsaved transient instance - save the transient instance before merging: de.mm.moreevent.types.TextResource

D.h. ich muss mich darum kümmern in welcher Reihenfolge ich den von einaner abhängigen Objete in die Datenbank schreibe. Eigentlich nicht das was ich von CascadeType.PERSIST, CascadeType.MERGE erwarte. Mir kommts so vor als würden die Annotations in den Klassen nicht beachtet.

Aber merkwürdig ist das alles schon. In einem Fall reicht ein em.persist und im anderen muss ich mit persist und merge arbeiten.
 
Zuletzt bearbeitet:

Zurück
Oben