I have a stateless session bean which parses and loads content from an XML file into a database. The document structure gets parse like so:
Code:
foreach section
load descriptive data into descriptive table
foreach alternate ID
load base, alternate ID into mapping table
done
foreach dated entry
load dated information into calendar
done
done
The problem I'm encountering is that, sometimes, the XML file will contain a duplicate entry in an section, e.g., the "alternate ID" part above. I have a "plain" Hibernate application which works fine, using an explicit session and transaction boundaries with rollbacks like this:
Code:
String code = n.selectSingleNode("id");
List<Element> altCodeList = n.selectNodes("keys/alt_id");
for (Element altCode : altCodeList) {
CodeMapEntry cme = new CodeMapEntry();
cme.setCode(code);
cme.setAltCode(altCode.getText());
if ("".equals(cme.getAltCode())) {
continue;
}
try {
session.saveOrUpdate(cme);
} catch (NonUniqueObjectException nue) {
log.warn("duplicate mapping entry " + cme + ", only one will be saved");
}
}
When I moved this to JBoss, I broke the code into methods which handle each piece with annotations describing the transaction, like this:
Code:
@PersistenceContext(unitName="dcs") private EntityManager manager;
...
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
private void loadMapEntry(String code, String altCode) {
CodeMapEntry cme = new CodeMapEntry();
cme.setCode(code);
cme.setAltCode(altCode);
if ("".equals(altCode)) {
return;
}
try {
manager.persist(exex);
} catch (PersistenceException pe) {
if (pe.getCause() instanceof NonUniqueObjectException) {
log.warn("duplicate mapping entry " + cme + ", only one will be saved");
} else {
throw(pe);
}
}
}
The problem is that the exception, even the NonUniqueObjectException, seems to invalidate the EntityManager. This is documented in the EntityManager documentation (Hibernate 3.1 beta7 PDF):
Quote:
If the EntityManager throws an exception (including any SQLException), you should immediately rollback the database transaction, call EntityManager.close() (if createEntityManager() has been called) and discard
the EntityManager instance. Certain methods of EntityManager will not leave the persistence context in a consistent state. No exception thrown by an entity manager can be treated as recoverable. Ensure that the EntityManager will be closed by calling close() in a finally block. Note that a container managed entity manager will do that for you. You just have to let the RuntimeException propagate up to the container.
Once the exception is thrown, even the one I catch and try to ignore, the next call to manager.persist() throws "javax.persistence.TransactionRequiredException: EntityManager must be access within a transaction". This call is in the next method (in my pseudocode, the section processing the dated entries) which is also marked a REQUIRES_NEW.
My question is, how
do I recover and continue with the rest of the file?