简体   繁体   中英

Hibernate LazyInitializationException using Spring CrudRepository

I always get the exception:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.knapp.vk.domain.Business.businessCategorySet, could not initialize proxy - no Session

I don´t want to set fetch to eager. What would be other good solutions?

Business Entity class:

@Entity
public class Business
{
    @Id
    @GeneratedValue
    private int pk;

    @ManyToMany
    private Set<BusinessCategory> businessCategorySet = new HashSet<BusinessCategory>();

...
}

BusinessRepository interface:

import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Transactional;

@Transactional
public interface BusinessRepository extends CrudRepository<Business, Integer>
{

}

Config:

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.hibernate4.HibernateExceptionTranslator;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableJpaRepositories(basePackages = "com.knapp.vk.repositorynew")
@EnableTransactionManagement
public class JPAConfiguration
{
    @Bean
    public EntityManagerFactory entityManagerFactory() throws SQLException
    {
        return Persistence.createEntityManagerFactory("standardManager");
    }

    @Bean
    public EntityManager entityManager(EntityManagerFactory entityManagerFactory)
    {
        return entityManagerFactory.createEntityManager();
    }

    @Bean
    public PlatformTransactionManager transactionManager() throws SQLException
    {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory());
        return txManager;
    }

    @Bean
    public HibernateExceptionTranslator hibernateExceptionTranslator()
    {
        return new HibernateExceptionTranslator();
    }
}

So you can do it by initializing businessCategoruSet in Service so instead of using @Transactional in Repository use it in Service. Let write some method like get business by id or list of business in repository and access that method from service.

@Transactional
public Class BusinessService{

@resource
BusinessRepository businessRepository;

public Business getBusiness(){
  Business business = businessRepository.getBusiness();
  //Here you should initialize BusinessCategorySet
  Object object = business.getBusinessCategoriesSet().size();
  }
}

For more details about @Transactional Refer this link

Also you can add method in repository:

@Transactional(readOnly = true)
public List<Business> findAllEagerly() {
    CriteriaBuilder builder = em.getCriteriaBuilder();
    CriteriaQuery<Business> query = builder.createQuery(Business.class);
    Root<Business> root = query.from(Business.class);
    root.fetch(Business_.businessCategorySet);
    return em.createQuery(query).getResultList();
}

or easier to use @Query in JpaRepository:

@Query("SELECT b FROM Business b JOIN FETCH b.businessCategorySet")
public List<Business> findAll();

I think good solution is to use @NamedEntityGraph

@Entity
@NamedEntityGraph(name = "Business.detail",
        attributeNodes = @NamedAttributeNode("businessCategorySet"))
public class Business {...}

and in Repository

public interface BusinessRepository extends CrudRepository<Business, Integer> {
    @EntityGraph(value = "Business.detail", type = EntityGraphType.LOAD)
    List<Business> findAll();
}

See also:

Custom implementations for Spring Data repositories

Configuring Fetch- and LoadGraphs

Hibernate session has its scope within transaction. So, if you are starting your transaction at service layer your hibernate session will be available till service layer. And if you are calling any object inside another object eg (business.getBusinessCategorySet()) which needs to be loaded lazy, will cause to throw "org.hibernate.LazyInitializationException". To resolve this issue, you have two solution 1) Load all related objects within service layer 2) Use Open Session In View (OSIV) configuration, it will make your hibernation session open till view layer.

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