Home page‎ > ‎Google Tech‎ > ‎Google App Engine‎ > ‎

Google App Engine Key Assignment after persist()

I was doing some tutorial (principally for myself) about using the JPA provider in GAE like an advanced API on top of the DataStore API. This is a goal for me to start learning NoSQL and coding well with GAE.

After some years using ORMs I think I have a good experience on their use and the way they should behave, but the time to learn something new about things you think you know always shine.

One of the tutorial wanted to persist some objects, put the parent key into a request attribute and then redirect to a servlet that shows them. Nothing difficult, but with surprise I was unable to do that in a way I know. Infact at the rendering servlet I always received a null key.

Debugging I saw that after the em.persist the key was still not assigned. Really strange I thougth, I used this approach a lot of times in the past and this has nothing to do with Relational or DataStore world, is a matter of specifications. The only way to let it generate the key was a commit the transaction or a flush of the entire session, something that touch the storage in a too aggressive way from my point of view.

At first I thought that was a problem regarding the way the GAE DataStore works (I was sure about the fact that I should have the key after persist), so I started developing a test case that uses DataNucleus JPA Engine for a relational DB. 

A simple test case

For this test case I used a Maven Project configured with a H2 DataBase instance that run embedded in the application (not for EclipseLink that doesn't support H2, for that I used Oracle 11g)

To do that I created a simple Entity with only Id and a persistent field:
@Entity
public class MyEntity {

    private Long key;
    private String description;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getKey() {
        return key;
    }

    public void setKey(Long key) {
        this.key = key;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String descrizione) {
        this.description = descrizione;
    }
}
then I configured the persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence 
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
    <persistence-unit name="test-persist">
        <class>net.lucamasini.testpersistence.MyEntity</class>
        <properties>
            <!-- DataNucleus custom properties -->
            <property name="datanucleus.ConnectionDriverName" value="org.h2.Driver"/>
            <property name="datanucleus.ConnectionURL" value="jdbc:h2:./test"/>
            <property name="datanucleus.ConnectionUserName" value="sa"/>
            <property name="datanucleus.ConnectionPassword" value=""/>
            <property name="datanucleus.autoCreateSchema" value="true"/>
            <property name="datanucleus.validateTables" value="false"/>
            <property name="datanucleus.validateConstraints" value="false"/>
        </properties>
    </persistence-unit>
</persistence>
Running a simple test case showed me that the problem was not in the DataStore but with DataNucleus:
public class TestPersistenceProvider {
    @Test
    public void testKeyAfterPersist() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-persist");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
    
        MyEntity e = new MyEntity();
        e.setDescription("A String");
        em.persist(e);
    
        assertNotNull("Entity Key is null", e.getKey());

        tx.commit();
        em.close();
        emf.close();
    }
}

Running the test

Running my test with a simple "mvn clean test" this is what I get:
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 1.491 sec <<< FAILURE!
testKeyAfterPersist(net.lucamasini.testpersistence.TestPersistenceProvider)  Time elapsed: 1.475 sec  <<< FAILURE!
java.lang.AssertionError: Entity Key is null
To be sure that everything was correct I did the same test with Hibernate 3.4.0GA, EclipseLink 1.0.1 and OpenJPA 1.0.1 and for all of them:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.598 sec

Conclusions

Resuming, I have 4 Persistence Provider and I have 3 of them that work the same way (and this for me is a standard de-facto) but what about JPA1 specs ???

From there we can read that (paragraph 3.1.1):

"A managed entity instance is an instance with a persistent identity that is currently associated with a persistence context." 



and because the persist() method of the EntityManager change the state of my Entity from "New" to "Managed" I should have its persistence identity that, paragraph 2.1.4, is in the simpler cases the field/property annotated with @Id.

Another test convinced me about a bug in the DataNucleus (and transitively in Google App Engine DataStore). In fact if I change the Id generation type from IDENTITY to AUTO it works like all the others JPA Engine !!!!

But wait, from DataNucleus documentation (http://www.datanucleus.org/products/accessplatform_1_1/jpa/value_generation.html) a value of AUTO for H2 DataBase means IDENTITY generation !!!!

This is a really annoining bug, because force me to use the JPA API in a really strange way, so I asked on Google App Engine group:


but the guy the has datanucleus nick (I hope not a datanucleus representative) was not able to understand the problem. I think last resort is to file a bug to Google App Engine, hoping the GAE team will be more persuasive than me.

Appendix

I attached the test project for convenience and in case you want to help me understanding this problem. 

 
  

Conversation Element

ċ
test-persistence-provider.zip
(12k)
Luca Masini,
May 19, 2010, 10:31 PM
Comments