0

I use Spring / JPA / Hibernate. I have a fairly standard Dao and Service layer implementations.

class GenericDao<T> {
   public save(T entity) {
       if (!entity.isIdSet()) {
           getEntityManager().persist(entity);
       }
       // other cases are update, 
       // so a transactional service method will suffice
       // we dont need to do anything
   }
}

class ParentEntity {
    // Nothing special here, just an entity with auto-generated PK, if that matters
}

class RelatedEntity {
    // Fairly standard many-to-one relation
    @Column(name = "parent_id",nullable = false,insertable = false,updatable = false)
    public Integer getParentId() {
         return parentId;
    }

    @NotNull
    @JoinColumn(name = "parent_id", nullable = false)
    @ManyToOne(cascade = {PERSIST, MERGE}, fetch = LAZY)
    public ParentEntity getParent() {
        return parent;
    }
}

class Repository<T> { // There is an implementation of this for both entities above
   @Transactional
   public void save(T entity) { getDao().save(entity); }
}

Now for the problem, I am having -

I am reading a file which contains data for the RelatedEntity, creating several records in the database. While doing that I need to set the parent reference. I dont want to lookup the parent entity everytime I insert a child record. So I create a list of all parent records and keep a map of parent entities (this set is fairly small, less than 10). I loop through the data and set the parent reference in the child and save the child.

public void getParentList {
    List parentList = parentRepository.find();
    // create a map with the list items for easy lookup on name
}
public void importData() {
  for (line : file) {
      RelatedEntity re = new RelatedEntity();
      re.setParent(map.get(line.parentName)); // The refered object is got once in getParentList
      // lookup the map for the parent
      // set all properties of re here
      childRepository.save(re);          
  }
}

So far, all is well.

I did not want to validate the incoming data explicitly, instead want to use JPA validation that is already set on the entity. So I want to handle constraint voilation exception around save() and ignore the records that do not validate. But want to continue with the rest of the data.

When I do that, I get a an exception :

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.myads.domain.ParentEntity

public void importData() {
  for (line : file) {
      RelatedEntity re = new RelatedEntity();
      // lookup the map for the parent
      // set all properties of re here
      try {
         childRepository.save(re);
     } catch (CVE cve) {
         // log the record that failed validation 
         /*************
         // Note: If I land here on line(x), I get PersistenceException on save for the
         // next iteration(x+1).
         **************/
     }
  }
}

So looks like the parent entity is detached from the session when a child entity throws a persistence exception. If there are no exceptions during perist of the child, everything works fine.

So what is the problem and what is the solution?

Any help is appreciated.

Anupama
  • 167
  • 1
  • 9

1 Answers1

0

I did not want to validate the incoming data explicitly, instead want to use JPA validation that is already set on the entity

That's where your problem is. The documentation explicitely says:

If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • In my case, the exception, I guess is being thrown by the Hibernate validator, pre-perist. So it is not exactly an SQL exception, as the JSR 303 validation will kick in before hibernate tries to persist. Isn't it ? In that case the session state should not be affected at all ? It does not sound very logical to setup all constraints on the bean, use a bean validator only to end up invoking the validator manually only because you want to recover from the exception and continue ? – Anupama Apr 23 '13 at 07:34
  • The doc says "including any SQLException". It doesn't say "excluding all exceptions except SQLExceptions". The JSR-303 validation on entities must be seen as smart chcked constraints in database. You use them to make sre the database doesn't contain junk, but it's a last resort validation, which should not prevent you from validating the data before, and which force you to rollback. You're of course free to ignore what the documentation says. But then don't complain if it doesn't work. – JB Nizet Apr 23 '13 at 08:33