Visibility of changes in Transaction is not visible to a JPA QL Query

#1

I have two simple Entities ( see attachment ) and a simple SLSB with container managed transactions which is called by a trivial servlet.

Inside a UserTransaction I add a number of items then check to see that they have been added

long newTotal = -1;
  try {
   InitialContext context = new InitialContext();
   javax.transaction.UserTransaction tx = (javax.transaction.UserTransaction) context
     .lookup("java:comp/UserTransaction");
   tx.begin();
   SLSBObjDbRemote bean = (SLSBObjDbRemote) context
     .lookup("SLSBObjDb");
   long oldAmount = bean.countItems();
   bean.addItems(howMany);
   newTotal = bean.countItems();
   if(newTotal < (howMany+oldAmount)) {
    System.err.println("Only found " + newTotal + " items expected " + (howMany + oldAmount));
   }
   tx.commit();
  } catch

 

The problem is that the items that I add in the transaction are not visible when I go to count them;

TypedQuery<Long> q = em.createQuery("SELECT COUNT(x) FROM Item x", Long.class);
  try {
   return q.getSingleResult();
  } catch (javax.persistence.NoResultException ex) {
   return -1;
  }

 

Here are the two Entities;

 

@Entity
public class Item {
@SuppressWarnings("unused") //used by JPA
@ManyToOne
private Singleton singleton;

@Basic
private String cachedKey;

@Id
private String id;

@Version
private int version;

@Basic
private Integer property;


// Bonus empty constructor generated for JPA
protected Item() {
  super();
}

public Item(Singleton singleton, int intValue) {
  id = java.util.UUID.randomUUID().toString();
  this.singleton = singleton;
  this.cachedKey = new Integer(intValue).toString();
  setProperty(intValue);
 
}

public Integer getJPAVersion() {
  return version;
}

public String getCachedKey() {
  return cachedKey;
}

public void setProperty(Integer newValue) {
     property = newValue;
}

public Integer getProperty() {
     return property;
}

public String getInfo() {
  return "Item: id=" + id + ", version=" + version + ", key=" + cachedKey + ", property=" + property;
}

public String getId() { return id; }
}
@Entity
public class Singleton {

public static Singleton getInstance(EntityManager em) {
  Singleton c = em.find(Singleton.class, "1");
  if (c == null) {
   c = new Singleton();
   em.persist(c);
  }
  return c;
}

@Id
protected String id = "1";

@SuppressWarnings("unused")
// used by JPA
@Version
private int version;

@Basic
protected java.lang.Integer lastInstance;


public void generateItems(EntityManager em, int howMany) {
  if (lastInstance == null) {
   lastInstance = 0;
  }
  int endCnt = lastInstance + howMany;
  int startCnt = lastInstance;
  while (endCnt > startCnt) {
   startCnt++;
   Item item = new Item(this, startCnt);
   em.persist(item);
   lastInstance = startCnt;
  }
}

public long howManyItemsExist(EntityManager em) {
  TypedQuery<Long> q = em.createQuery("SELECT COUNT(x) FROM Item x", Long.class);
  try {
   return q.getSingleResult();
  } catch (javax.persistence.NoResultException ex) {
   return -1;
  }
}
}

Here is the bean

@TransactionAttribute(TransactionAttributeType.REQUIRED)
@Stateless(mappedName = "SLSBObjDb")
public class SLSBObjDb implements SLSBObjDbRemote {

@PersistenceContext(unitName = "Experiment8objPU" )
EntityManager emInj;

    /**
     * Default constructor.
     */
    public SLSBObjDb() {
    }

    @Override
    public void addItems(int howMany) {
     Singleton singleton = Singleton.getInstance(emInj);
     singleton.generateItems(emInj, howMany);
    }
   
    @Override
    public long countItems() {
     Singleton singleton = Singleton.getInstance(emInj);
     long numItems = singleton.howManyItemsExist(emInj);
     return numItems;
    }
}

and 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" version="2.0"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">


<persistence-unit name="Experiment8objPU" transaction-type="JTA">
     <provider>com.objectdb.jpa.Provider</provider>
     <class>objdbTest.Singleton</class>
  <class>objdbTest.Item</class>
     <properties>
       <property name="javax.persistence.jdbc.url" value="C:/development/ObjectDb/objectdb-2.2.9_04/db/ObjectTestApp.odb"/>
       <property name="javax.persistence.jdbc.user" value="admin"/>
       <property name="javax.persistence.jdbc.password" value="admin"/>
     </properties>
  </persistence-unit>

</persistence>

Is there something I am doing wrong, or is this the intended behaviour?

I attach the Eclipse projects . The only complexity is that everything is structured into separate projects ( an EAR, The EJBs, the EJB interfaces, the JPA Model, and the Web tier ). The objectdbee.jar is added as a module to the EAR, and the persistence.xml is located where it is used in the EJB project ( this was the only way I could get things to work on glassfish for other JPA providers )

I am using glassfish 3.1 and java 1.6b24 on Windows Server 2008 SP2 32bit. I have not added any objectdb jars  in any other location apart from the EAR

#2

Please check if this is due to the default flush mode.

In most JPA implementations the default is AUTO. In ObjectDB the default is COMMIT (which is more efficient).

ObjectDB Support
#3

Yes, changing the Query to FlushModeType.AUTO meant that the added items are 'visible'

#4

However if I Query for an Item that I have added in the uncommitted transaction I find it, even though I don't set the FlushMode. So in that case I can see the results of the transaction before commit in a Query...

For example if I implement the following in Singleton;

 

public Item findItem(EntityManager em, int which) {
  TypedQuery<Item> q = em.createQuery("SELECT o FROM Item o WHERE o.cachedKey = :cachedKey", Item.class);
  q.setParameter("cachedKey", Integer.toString(which));
  return q.getSingleResult();
 
}

and call it within the UserTransaction;

ResultStuff results = new ResultStuff();
  try {
   InitialContext context = new InitialContext();
   javax.transaction.UserTransaction tx = (javax.transaction.UserTransaction) context
     .lookup("java:comp/UserTransaction");
   tx.begin();
   SLSBObjDbRemote bean = (SLSBObjDbRemote) context
     .lookup("SLSBObjDb");
   long oldAmount = bean.countItems();
   if(oldAmount + howMany > Integer.MAX_VALUE) {
    System.err.println("Can't add any more");
    throw new ExampleException("Can't have any more than MaxInt items");
   }
   bean.addItems(howMany);
   results.newTotal = bean.countItems();
   if(results.newTotal < (howMany+oldAmount)) {
    System.err.println("Only found " + results.newTotal + " items expected " + (howMany + oldAmount));
   }
   else {
    System.err.println("Found " + results.newTotal + " items as expected = " + (howMany + oldAmount));
   }
   double which = oldAmount + (((double)howMany) / 2);
   results.item = bean.findItem((int)which); //bean merely passes call to singleton with injected EntityManager
   tx.commit();
  }

So are there rules for "table level/entity class level" operations which only show the values for all entities if the Query is in AUTO mode?

 

Thanks

#5

> So are there rules for "table level/entity class level" operations which only show the values for all entities if the Query is in AUTO mode?

No. Changes during transaction are not be visible to queries with no flush. Probably you do have a flush in that code. Maybe in the setting of the EntityManagerFactoryEntityManager - FlushModeType.AUTO is specified.

ObjectDB Support
#6

You are right! I was doing a count with the flush mode auto on... If I don't I can't see any of the objects I added!

So if I want to do a query based on the transactional state of the app ( i.e. what was there at the beginning of the transaction as then operated on by the subsequent transactional ops ) then I must flush the stuff first.

 

Reply