1 Reply Latest reply on Jul 6, 2007 2:23 PM by gavin.king

    Transaction management in EntityHome subclass

    mbertelsen

      I 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!