views:

111

answers:

3

I have a Spring/JPA/Hibernate application and am trying to get it to pass my Junit integration tests against H2 and MySQL. Currently I am using Atomikos for transactions and C3P0 for connection pooling.

Despite my best efforts my DAO integration one of the tests is failing with org.hibernate.NonUniqueObjectException. In the failing test I create an object with the "new" operator, set the ID and call persist on it.

@Test
@Transactional
public void save_UserTestDataNewObject_RecordSetOneLarger() {
    int expectedNumberRecords = 4;
    User newUser = createNewUser();

    dao.persist(newUser);   
    List<User> allUsers = dao.findAll(0, 1000);

    assertEquals(expectedNumberRecords, allUsers.size());
}

In the previous testmethod I do the same thing (createNewUser() is a helper method that creates an object with the same ID everytime). I am sure that creating and persisting a second object with the same Id is the cause, but each test method is in own transaction and the object I created is bound to a private test method variable. I can even see in the logs that Spring Test and Atomikos are rolling back the transaction associated with each test method.

I would have thought the rollback would have also cleared the persistence context too. On a hunch, I added an a call to dao.clear() at the beginning of the faulty test method and the problem went away!! So rollback doesn't clear the persistence context??? If not, then who does??

My EntityManagerFactory config is as follows:

<bean id="myappTestLocalEmf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="myapp-core" />
        <property name="persistenceUnitPostProcessors">
            <bean class="com.myapp.core.persist.util.JtaPersistenceUnitPostProcessor">
                <property name="jtaDataSource" ref="myappPersistTestJdbcDataSource" />
            </bean>
        </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="database" value="$DS{hibernate.database}" />
                <property name="databasePlatform" value="$DS{hibernate.dialect}" />
            </bean>
        </property>

        <property name="jpaProperties">
            <props>
                <prop key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory</prop>
                <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
                <prop key="hibernate.connection.autocommit">false</prop>
                <prop key="hibernate.format_sql">true"</prop>
                <prop key="hibernate.use_sql_comments">true</prop>
    </property>
</bean>
+1  A: 

This is weird. From the JPA specification:

3.3.2 Transaction Rollback

For both transaction-scoped and extended persistence contexts, transaction rollback causes all pre-existing managed instances and removed instances to become detached. The instances’ state will be the state of the instances at the point at which the transaction was rolled back. Transaction rollback typically causes the persistence context to be in an inconsistent state at the point of rollback. In particular, the state of version attributes and generated state (e.g., generated primary keys) may be inconsistent. Instances that were formerly managed by the persistence context (including new instances that were made persistent in that transaction) may therefore not be reusable in the same manner as other detached objects—for example, they may fail when passed to the merge operation.

The way I read the above section is that when a transaction is rolled back, JPA should clear its persistence context.

Pascal Thivent
That's the same way I see it. However, the Hibernate Entity Manager document says "Extended context means that a persistence context is created when the entity manager is retrieved (using EntityManagerFactory.createEntityManager(...) ) and closed when the entity manager is closed." I create my EntityManager's via the emf.createEntityManager() call which apparently creates an extended persistence context.Perhaps I need to open another question on how to create and inject JTA Transactional Entity Manager into a DAO without @PersistenceContext.
HDave
+1  A: 

Did you try this? http://www.atomikos.com/Documentation/HibernateIntegration#With_JPA

Guy Pardon
I saw that while reading the hibernate docs as well. I tried it out -- and it works without that in the configuration....interesting.
HDave
A: 

The problem in this case ended up being that I had an application managed, extended transaction entity manager being injected into my DAOs. The reason for this can be found here:

http://stackoverflow.com/questions/3024466/problem-creating-jpa-entitymananger-in-spring-context

Once I fixed my entity manager -- everything worked.

HDave