MountainClimb / datanucleus-appengine

Automatically exported from code.google.com/p/datanucleus-appengine
0 stars 0 forks source link

JPA dirty checking across transactions behaves incorrectly #202

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago

I'm using the EntityManager-per-Request pattern, but breaking up a request 
(an item in a task queue) into multiple transactions. When I make changes 
to an entity properties, they are ONLY persisted if the entity was 
retrieved with find() in the SAME transaction. If the entity was retrieved 
in a previous transaction, the changes are ignored, even if the 
entityManager with which it was retrieved is STILL open. 

I think I'm interpreting the spec correctly, and a quick dash to the trusty 
Java Persistence with Hibernate (p423) seems to imply that this ought to 
work. 

What steps will reproduce the problem?

@Entity
public class Sample  {

  private String id;
  private String name;

  @Id
  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  @Column
  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

class DataStoreLearningTest { 

  private EntityManagerFactory emf;

  /// ... setup ommitted ... 

  @Test
  public void propertyChangesOnPersistentEntitiesAreFlushed() {

    String sampleId = UUID.randomUUID().toString();

    // In the first unit of work, we persist an entity to the
    // data store with a string id

    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    Sample sample = new Sample();
    sample.setId(sampleId);
    sample.setName("FOO");
    em.persist(sample);
    em.getTransaction().commit();
    em.close();

    // in the second unit of work, we retrieve the entity
    // in one transaction, do some stuff, and then try to
    // persist the results in a second tx.

    em = emf.createEntityManager();
    em.getTransaction().begin();
    Sample resample = em.find(Sample.class, sampleId);
    em.getTransaction().commit();

    // do a bunch of long-running computations

    em.getTransaction().begin();
    // if we retrieve the entity within this transaction, the 
    // test will pass
   // resample = em.find(Sample.class, sampleId); 
    resample.setName("BAR");
    em.getTransaction().commit();

    em.close();

    // VERIFY that our changes have made it to the datastore
    em = emf.createEntityManager();
    Sample check = em.find(Sample.class, sampleId);
    assertThat(check.getName(), is(equalTo("BAR")));
    em.close();
  }
}

What is the expected output? What do you see instead?

sample.name should be updated and contain the value "BAR". It does not. It 
is still set to "FOO"

What version of the product are you using? On what operating system?
datanucleus-appengine-1.0.5.final
appengine-sdk 1.3.2
windows 7

Please provide any additional information below.

Original issue reported on code.google.com by akbertram on 10 Apr 2010 at 8:45

GoogleCodeExporter commented 8 years ago
Actually, it seems that the root cause is that the persistence context appears 
to be 
cleared on tx.commit(). Feature or bug?

  @Test
  public void entitiesRemainPersistentUponCommit() {

    String sampleId = UUID.randomUUID().toString();

    // In the first unit of work, we persist an entity to the
    // data store with a string id

    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    Sample sample = new Sample();
    sample.setId(sampleId);
    sample.setName("FOO");
    em.persist(sample);
    em.getTransaction().commit();

    assertTrue(em.contains(sample));

    em.close();

  } 

Original comment by akbertram on 10 Apr 2010 at 8:58

GoogleCodeExporter commented 8 years ago
Feature, since in the ancient version of datanucleus being used by GAE/J you 
have to
make use of the persistence properties "detachAllOnCommit" and "detachOnClose" 
to
simulate transactional persistence context. When they update their plugin to 
use a
version of DataNucleus that is semi-recent, then that won't be necessary.

Hence not an issue

Original comment by googleco...@yahoo.co.uk on 10 Apr 2010 at 2:55

GoogleCodeExporter commented 8 years ago
Ok, thanks, setting the parameters works. 
For the record, a little frustrated that an implementation of a java standard 
should 
have a "quirks mode"

Here's the code that makes the tests above pass:

public static class EMF {
    private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("transactions-optional", new HashMap() 
{{
          put("datanucleus.DetachOnClose", true);
          put("datanucleus.DetachAllOnCommit", false);
        }});

    private EMF() {}

    public static EntityManagerFactory get() {
      return emfInstance;
    }
  }

Original comment by akbertram on 10 Apr 2010 at 8:04

GoogleCodeExporter commented 8 years ago
No idea what is a "quirks mode". DataNucleus AccessPlatform passes the JPA1 
TCK, and
provides *all* functionality defined in that spec (and the spec is notoriously 
glib
on various points). 

If their TCK doesn't have any explicit tests for that functionality then blame 
then,
not the people who took the time to implement and pass their test. "They" (JPA 
Expert
Group - SUN, Oracle etc) developed the TCK in private and you have to sign 
NDA's just
to see it never mind identify areas where there are no tests.

Original comment by googleco...@yahoo.co.uk on 12 Apr 2010 at 9:29

GoogleCodeExporter commented 8 years ago
Sorry Andy, no offense intended. Quirks mode is a reference to the HTML 
specification 
and its implementation. 

In any case, this issue can be closed as there is an acceptable workaround and 
is not 
specific to the appengine plugin in any case.

Thanks

Original comment by akbertram on 12 Apr 2010 at 12:28

GoogleCodeExporter commented 8 years ago
Refer to Issue 103 for use of these flags by default

Original comment by googleco...@yahoo.co.uk on 27 Jun 2011 at 11:16