Hello. I have been developing/maintaining an application over the last couple of years that runs in JBoss and uses JPA + Hibernate. I've observed the following in both JBoss 5.1.0 and 6.1.0 (Hibernate 3.3.1 & 3.6.6). Long ago I saw some weird behavior when constraint violations were encountered: some of the entity tables had rows inserted but others did not, even though I was within a transaction that was rolled back when the exception was caught. At the time my fix was to simply correct the constraint violation issues and the problem went away, even though the lack of transactional behavior was a bit disturbing.
Two years later I am revisiting this issue and have come across a troubling observation that explains the symptoms that I observed. It turns out that, even though I am within a transaction, my connection's autocommit flag is still true. I have verified this in several ways:
1) Step through the code. The moment I step over a call to EntityManager.flush() I can see modified data in a separate DB session (using pgAdmin)
2) Turn on SQL logging at the database (PostgreSQL 8.3 if it matters), no BEGIN/COMMIT/ROLLBACK ever appears
3) Add the following code before after my calls to EntityManager.persist/flush:
Code:
Session s = (Session)entityManager.getDelegate();
boolean autocommit = s.connection().getAutoCommit(); // always true
The way that I understand things, the proper behavior is that whenever I start a transaction autocommit should be disabled. I spent the better part of yesterday stepping through Hibernate/JBoss/Arjuna code and cannot find where this would ever happen. When grepping the Hibernate code, I find very few calls to Connection.setAutoCommit(false) and they appear in classes that wouldn't be used in my situation anyway (certain connection providers and transaction implementations). Based on that, I'm not sure that this post even belongs in the Hibernate forum (maybe the JBoss forum instead), but I have to start somewhere. Although grepping through the JBoss/Arjuna code, I don't see anywhere that it would happen either.
Here is the relevant persistence context definition and code that exhibits the autocommit behavior (note that I mentioned an entity that exists across several tables earlier, and this sample shows only 1 entity class, but both DAOs/PUs show the same symptoms):
Code:
<persistence-unit name="georeo">
<jta-data-source>java:/Georeo_DS</jta-data-source>
<class>com.orci.websol.georeo.Georeo</class>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<!-- this property wasn't here initially but adding it didn't help -->
<property name="org.hibernate.transaction.manager_lookup_class"
value="org.hibernate.transaction.JBossTransactionManagerLookup" />
</properties>
</persistence-unit>
Code:
@PersistenceContext(unitName = "georeo")
private EntityManager entityManager;
@Resource
private UserTransaction utx;
public void updateGeoreos(String eventId,
Map<Georeo, PersistableGeometry> georeosToGeoms) throws GeoreoException {
try {
utx.begin();
for (Entry<Georeo, PersistableGeometry> entry : georeosToGeoms.entrySet()) {
Georeo georeo = entry.getKey();
PersistableGeometry geom = entry.getValue();
entityManager.persist(georeo);
// result of persist is visible to others immediately after this flush
entityManager.flush();
// insert the geometry
String insert = createInsertStmt(georeo, geom);
Query q = entityManager.createNativeQuery(insert);
q.executeUpdate();
}
utx.commit();
} catch (Exception e) {
LOG.error("Could not update georeo for event " + eventId, e);
try {
utx.rollback();
} catch (Exception ee) {
LOG.warn("Failed to rollback", ee);
}
throw new GeoreoException("Could not update georeo for event " + eventId, e);
}
}
From the code you can see that I am using JTA, so I don't/can't try to use EntityTransactions instead, and the code is using bean managed transactions (although it doesn't really need to be), but I have also tried container managed transactions with no luck. So my questions are
1) Am I correct in assuming that autocommit should be disabled when a transaction is started? (Cause otherwise it's not really a transaction, right?)
2) Who is responsible for disabling autocommit, Hibernate or the container?
3) Why isn't it being disabled? Am I missing a setting?
Thanks