Transaction management in EntityHome subclass
mbertelsen Jul 6, 2007 12:20 PMI have an entity with a two-field unique constraint. Due to not being able to write a validator that validates two fields of an entity at once (or am I wrong here?), I ended up subclassing the EntityHome in order to run my object-level validation before the superclass update() and persist() get called. However, because the instance entity is managed, and EntityHome uses the seam-managed persistence context and transaction management, by the time my code for validation runs, the managed entity has already been updated by JSF. So when I try to run a select query to determine if the two fields together are unique, hibernate is automatically flushing the write to the entity, which throws the EntityExistsException i'm trying to avoid.
I ended up with the following solution, and would like to know if there's a better way:
@Name("databaseServerHome") public class DatabaseServerHome extends EntityHome<DatabaseServer> { @In EntityManager entityManager; @In FacesMessages facesMessages; @Override public EntityManager getEntityManager() { return entityManager; } @Override @Factory("databaseServer") public DatabaseServer getInstance() { return super.getInstance(); } // ... (removed for clarity) @Override @Rollback(ifOutcome="invalid") public String update() { // set flush mode to COMMIT so that validateUnique can run a query to // check uniqueness without triggering a flush // This is required because we have a managed entity with dirty changes // (already applied by JSF) and now need to validate them after the // changes have been made getEntityManager().setFlushMode(FlushModeType.COMMIT); if (validateUnique()) { return super.update(); } else { return "invalid"; } } protected boolean validateUnique() { DatabaseServer ds = getInstance(); String existenceQuery = "select ds from DatabaseServer ds where ds.serverName = :serverName and ds.port = :port"; try { DatabaseServer ds1 = (DatabaseServer) entityManager.createQuery(existenceQuery) .setParameter("serverName", ds.getServerName()).setParameter("port", ds.getPort()).getSingleResult(); if (ds1 != null && !(ds1.getId().equals(ds.getId()))) { facesMessages.add(FacesMessage.SEVERITY_ERROR,"You cannot have two database servers with the same Name and Port"); return false; } else { return true; } } catch (NoResultException nre) { return true; } } }
One last minor annoyance is that Seam is adding a FacesMessage that reads "Transaction Failed" when it rolls back - how do I alter this behavior?
Thanks!