简体   繁体   中英

Hibernate LazyInitializationException with Spring MVC

Problem

Using Kolorbot's Spring MVC 4 Quickstart archetype I created an entity, a repository and a service class which is accessed by a controller method. In my entity I have a ManyToOne relationship to another entity by fetching it lazily.

In my controller method I invoke the service's method which itself invokes a method on the repository which eventually fetches the needed data and passes it on through the layers.

Accessing the entity's attributes in the controller's method that point to the related entity I receive a "LazyInitializationException".

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Research and attempts to fix the problem

While reading up on the problem I stumbled upon solutions describing the usage of the @Transactional annotation which does not help or the inclusion of the OpenEntityManagerInViewFilter that is supposed to force the session to stay open until the controller's method are processed.

The inclusion of the OpenEntityManagerInViewFilter:

@Override
protected Filter[] getServletFilters() {
    OpenEntityManagerInViewFilter openEntityManagerInViewFilter = new OpenEntityManagerInViewFilter();
    openEntityManagerInViewFilter.setBeanName("openEntityManagerInViewFilter");

    ...

    return new Filter[] {openEntityManagerInViewFilter, characterEncodingFilter, securityFilterChain};
}

The usage of the filter leads to the following exception:

org.hibernate.HibernateException: Javassist Enhancement failed: com.example.package.RelatedEntity
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.getProxy(JavassistLazyInitializer.java:143)
at org.hibernate.proxy.pojo.javassist.JavassistProxyFactory.getProxy(JavassistProxyFactory.java:73)

RelatedEntity is the object I try to access through the original entity I am interested in.

By setting the lazy fetching mechanism to false I receive the following exception (thrown in my controller's method):

 
 
 
 
  
  
  Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly] with root cause javax.persistence.RollbackException: Transaction marked as rollbackOnly at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:72)
 
 
  

By removing the try/catch block in the repository's findEntityById() method (see below) and adding the annotation @Transactional(noRollbackFor = PersistenceException.class) I was able to enable eager loading. But this is just a temporary solution. Nevertheless I'd like to use lazy fetching.


Source code section

The entity class that is returned by the service method:

@Entity
@Table(name = "Entity")
@NamedQuery(
        name = Entity.FIND_BY_ID,
        query = "select e from Entity e where e.id = :id"
)
public class Entity {

    public static final String FIND_BY_ID = "Entity.findById";

    @Id
    @Column(name = ID)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Getter
    private Long id;

    ...

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name=RelatedEntity.ID)
    @Getter
    private RelatedEntity relatedEntity;

    ...

    protected Entity() {}

    public Entity(String param1) {
        ...
    }
}

And the RelatedEntity that I try to access through an instance of Entity:

@Entity
@Table(name = "RELATED_ENTITY")
@NamedQuery(name = RelatedEntity.FIND_BY_ID, query = "select re from RelatedEntity re where re.id = :id")
public class RelatedEntity {

    public static final String FIND_BY_ID = "RelatedEntity.findById";
    public static final String ID = "RE_ID";

    @Id
    @Column(name=ID)
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    ...

    @OneToMany(mappedBy="relatedEntity")
    private List<Entity> entities;

    ...

    protected RelatedEntity() {}

    public RelatedEntity(String param1) {
        ...
    }

}

Finally, the repository and the service class:

@Repository
public class EntityRepository {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional(noRollbackFor = PersistenceException.class)
    public List<Entity> findEntityById(Long id) {
        return entityManager.createNamedQuery(Entity.FIND_BY_ID, Entity.class)
                .setParameter("id", id)
                .getResultList();
    }

}

@Service
public class DefaultEntityService implements EntityService {

    @Autowired
    EntityRepository entityRepository;

    ...

    @Override
    public List<Entity> findEntitiesById(Long id) {
        return entityRepository.findAgreementById(id);
    }

    ...
}

Solution

I am not exactly sure what happened but suddenly the OpenEntityManagerInViewFilter seems to work and the session's still open when accessing the entity's properties in my controller and view. A further insight into the lazy fetching mechanism and the possible solutions to avoid the LazyInitializationException is well documented in the Hibernate Community Documentation .

The initialization of the lazily fetched object through Hibernate.initialize() in the service layer is a valid alternative but generates too much unnecessary overhead.

In order to assure that your lazy-loading request is bound to a session, you should initialize it in your service layer:

@Override
public List<Entity> findEntitiesById(Long id) {
    List<Entity> entityList = entityRepository.findAgreementById(id);
    for (Entity e: entityList){
        Hibernate.initialize(e.getRelatedEntity());
    }
    return entityList; 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM