无法懒惰地初始化角色集合,..无法初始化代理 - 无会话 - JPA + SPRING

Posted

技术标签:

【中文标题】无法懒惰地初始化角色集合,..无法初始化代理 - 无会话 - JPA + SPRING【英文标题】:failed to lazily initialize a collection of role,..could not initialize proxy - no Session - JPA + SPRING 【发布时间】:2014-05-03 19:10:08 【问题描述】:

我在 Spring (3.2.2) 中使用 JPA(使用 Hibernate 4.3.3 作为持久性提供程序),我的所有字段都加载正常,但是当我尝试访问我的 Collection 时,它会抛出错误-

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.br.common.catalog.entity.Category.allParentCategoryXrefs, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212)
    at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551)
    at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:140)
    at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:526)
    at java.lang.String.valueOf(String.java:2827)
    at java.io.PrintStream.println(PrintStream.java:771)
    at test.Test.main(Test.java:30)

当我对此进行调试时,我的实体类中定义的每个集合都出现错误 - com.sun.jdi.InvocationException occurred invoking method.

我尝试使用 collection.size() 和 Hibernate.initialize() 但这些都不起作用。 在网上搜索时,我发现扩展 Persitence 可以解决问题,即。

 @PersistenceContext(type=PersistenceContextType.EXTENDED)

        protected EntityManager em;

这很好,但通过这个我发现 em 将始终保持打开状态,现在 spring 不会处理这个。有没有办法使用 Spring 来解决这个问题。非常感谢任何帮助。

我的实体是 -

  @Entity
    @Inheritance(strategy = InheritanceType.JOINED)
    @Table(name="CATEGORY")
    public class Category implements Serializable 
       @Id
        @GeneratedValue(generator= "CategoryId")
         @Column(name = "CATEGORY_ID")
        protected Long id;

        @ManyToOne(targetEntity = Category.class)
        @JoinColumn(name = "DEFAULT_PARENT_CATEGORY_ID")
        @Index(name="CATEGORY_PARENT_INDEX", columnNames="DEFAULT_PARENT_CATEGORY_ID")
        protected Category defaultParentCategory;

        @OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.category")
        @Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region="test")
        @OrderBy(value="displayOrder")
        @BatchSize(size = 50)

        protected List<Categoryref> childCategoryRefs = new ArrayList<Categoryref>(10);
        @OneToMany(targetEntity = Categoryref.class, mappedBy = "categoryrefPK.subCategory",fetch=FetchType.LAZY)
        @Cascade(value=org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.PERSIST)
        @OrderBy(value="displayOrder")
        @BatchSize(size = 50)
        protected List<Categoryref> parentCategoryRefs = new ArrayList<Categoryref>(10);

    


@Entity
@Polymorphism(type = PolymorphismType.EXPLICIT)
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "CATEGORY_REF")
public class Categoryref implements Serializable 
    /** The category id. */
    @EmbeddedId
    CategoryrefPK categoryrefPK = new CategoryrefPK();

    public CategoryrefPK getCategoryrefPK() 
        return categoryrefPK;
    

    public void setCategoryrefPK(final CategoryrefPK categoryrefPK) 
        this.categoryrefPK = categoryrefPK;
    
  

@Embeddable
public class CategoryrefPK implements Serializable 

    @ManyToOne(targetEntity = Category.class, optional=false)
    @JoinColumn(name = "CATEGORY_ID")
    protected Category category = new Category();

    @ManyToOne(targetEntity = Category.class, optional=false)
    @JoinColumn(name = "SUB_CATEGORY_ID")
    protected Category subCategory = new Category();


我的 Xml 配置为 -

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/tx  
           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.br" />

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        ....
    </bean>
<!--  this is also used we can used this also  -->
  <tx:annotation-driven transaction-manager="transactionManager" />  
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
        <property name="dataSource" ref="dataSource" />
         <property name="persistenceUnitName" value="abc" />   
        <property name="packagesToScan" value="com.br.common.*" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true" />
                <property name="databasePlatform" value="org.hibernate.dialect.mysqlDialect" />
            </bean>
        </property>
    </bean>  


</beans>  

Persitence.xml

    <?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="abc">
        transaction-type="RESOURCE_LOCAL">
          <mapping-file>META-INF/category.orm.xml</mapping-file>
        <class>com.br.common.Category</class>
        <class>com.br.common.Categoryref</class>
        <class>com.br.common.CategoryrefPK</class>

        <properties>
            <property name="javax.persistence.jdbc.user" value="user"
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"></property>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test>
            <property name="javax.persistence.jdbc.password" value="...">
            <property name="hibernate.show_sql" value="true" />

            <property name="hibernate.transaction.flush_before_completion"
                value="false" />
            <property name="hibernate.connection.autocommit" value="true" />
            <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
            <property name="hibernate.cache.use_second_level_cache" value="true" /> 
            <property name="hibernate.cache.use_query_cache" value="true"/>
            <property name="hibernate.generate_statistics" value="false" />
            <property name="hibernate.archive.autodetection" value="false" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.id.new_generator_mappings" value="true" />
        </properties>
    </persistence-unit>
</persistence>

这是我的 dao 我通过服务层调用 dao

@Repository("categoryDaoImpl")
public class CategoryDaoImpl implements ICategoryDAO 

    @PersistenceContext
    protected EntityManager em;


    public Category save(Category category) 
      Category category2=   em.merge(category);

         em.flush();
         return category2;
  
    public Category readCategoryById(Long categoryId) 
        return em.find(Category.class, categoryId);
    



服务层

    @Service("blCatalogService")
@Transactional(propagation=Propagation.REQUIRED)
public class CatalogServiceImpl implements ICatalogService 

    @Resource(name="categoryDaoImpl")
    protected ICategoryDAO categoryDao;

    @Transactional
    public Product saveProduct(Product product) 
        return productDao.save(product);
    

    public Category findCategoryById(Long categoryId) 
        return categoryDao.readCategoryById(categoryId);
    


这是主要的

public class Test 


        public static void main(String[] args) 
                ApplicationContext context = new ClassPathXmlApplicationContext(
                    "applicationContext-persistence.xml");
            ICatalogService serviceCategory= (ICatalogService) context
                    .getBean("blCatalogService");
                Category parentCategory=serviceCategory.findCategoryById(2l);
    System.out.println(parentCategory.getAllParentCategoryrefs());//here error is coming while accessing collection
    
    

【问题讨论】:

这个问题已经被问过好几次了,当你试图在会话上下文之外访问子集合时,你可能必须在会话上下文中加载它才不会出现这个错误。 hibernate: LazyInitializationException: could not initialize proxy的可能重复 @Zeus 我知道这个错误延迟初始化何时出现,但我希望通过使用 spring 来解决它 【参考方案1】:

问题是当这个方法调用返回时:

Category parentCategory=serviceCategory.findCategoryById(2l);

在这里,您不再处于 @Transactional 上下文中。这意味着链接到 parentCategory 的会话已关闭。现在,当您尝试访问链接到已关闭会话的集合时,会出现 No Session 错误。

需要注意的一点是,main 方法在任何 spring bean 之外运行,并且没有持久性上下文的概念。

解决方案是从事务上下文中调用parentCategory.getAllParentCategoryrefs(),这永远不会成为应用程序的主要方法。

然后将 parentCategory 重新附加到新的持久化上下文,然后调用 getter。

例如,尝试将 parentCategory 传递回同一服务的事务方法:

serviceCategory.nowItWorks(parentCategory);

服务上的方法是事务性的:

@Transactional(readOnly=true)
public void nowItWorks(Category category) 
    dao.nowItWorks(category);

在 DAO 中:

public void nowItWorks(Category category) 
    Category reattached = em.merge(category);
    System.out.println("It works: " + reattached.getAllParentCategoryrefs());

【讨论】:

就我而言,我有两种方法。一个用于获取主要实体,另一个用于延迟获取关系实体。我必须将@Transactional 放在这两种方法中,并且它起作用了【参考方案2】:

就像@Zeus 说的那样,这个问题已经被回答过很多次了。您在测试类中遇到了这个问题,因为您的事务在您的服务调用开始和结束:

Category parentCategory=serviceCategory.findCategoryById(2l);

从 Hibernate 文档中回想一下,延迟加载仅适用于 Hibernate 会话(在这种情况下,休眠会话以您的服务调用开始和结束)。您无法重新连接到休眠会话(简单地)来初始化集合。

当您想“在春季”解决它时,我不确定您的意思。因为这不是 Spring 问题。基本上解决这个问题的两种方法是在你加载父节点的休眠会话中加载集合,或者在原始休眠会话之外执行单独的提取。

【讨论】:

我不知道为什么你们的反应好像这是某种简单的解决方案。我一直在关注宙斯的回应,但没有一个对我有用。我已经尝试过 Transactional 并且返回 null 而不是错误响应只会使情况变得更糟。我尝试过其他人的解决方案,他们的大多数解决方案都是不推荐使用的解决方案。我在 JUNIT 中使用我的并嵌入了休眠。 我认为这是一个答案很简单但解决方案很复杂的情况。我并不是要轻视这个问题。答案实际上取决于您的代码是如何设计的,以及您尝试测试的级别。 Spring 为你抽象了很多样板代码,但你必须知道它是如何工作的才能有效地使用它。【参考方案3】:

在您的域集集合上使用@Fetch(FetchMode.SELECT)@LazyCollection(LazyCollectionOption.FALSE),它将起作用

【讨论】:

@LazyCollection(LazyCollectionOption.FALSE) 只有这样它才能工作,你有使用非休眠库的解决方案吗?谢谢 这和 fetch=FetchType.EAGER 完全一样【参考方案4】:

尝试使用fetch=FetchType.EAGER,它会工作

【讨论】:

这是一个快速但并不总是最好的解决方案。 但还是值得一提的。

以上是关于无法懒惰地初始化角色集合,..无法初始化代理 - 无会话 - JPA + SPRING的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 中的 Hibernate 无法懒惰地初始化角色集合,无法初始化代理 - 没有 Session 异常

在将对象转换为json期间,无法懒惰地初始化角色集合

懒惰地初始化角色集合失败

无法写入内容:无法延迟初始化角色集合,无法初始化代理 - 无会话

org.hibernate.LazyInitializationException:无法延迟初始化角色集合:FQPropretyName,无法初始化代理 - 无会话

无法延迟初始化角色集合:myapp.myapp.models.Contact.messages,无法初始化代理 - 没有会话