关于java:LazyInitializationException问题无法初始化代理 – 没有Session

Issue with LazyInitializationException could not initialize proxy - no Session

我在Hibernate中遇到了LazyinitializationException的这个著名问题。我已经看到很多关于这个的问题,但仍然不能解决我的问题。

我有这样一种多对多的关系:

java青少年

1
2
3
4
5
6
7
8
public class Teen implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name ="TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name ="teenEmail"),
            inverseJoinColumns = @JoinColumn(name ="followerEmail"))
    private List<Follower> followerList;
}

追随者.java

1
2
3
4
5
6
7
8
public class Follower implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinTable(name ="TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name ="followerEmail"),
            inverseJoinColumns = @JoinColumn(name ="teenEmail"))
    private List<Teen> teenList;
}

一个青少年有N个追随者,一个追随者可以跟踪N个青少年。

我的数据库中已经有一些条目,我正在从中提取所有的青少年。

1
2
3
4
5
6
7
8
9
List<Teen> teens = (List<Teen>) teenDao.findAll();

for (Teen item : teens) {
    System.out.println("teen" + item.getEmail());
    List<Follower> followers = item.getFollowerList();
    for (Follower follower : followers) {
        System.out.println("follower" + follower.getEmail());
    }
}

当我试图读取getFollowerList()方法得到的关注者列表时,上面的代码出现了异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.capstone.server.model.Teen.followerList, could not initialize proxy - no Session
    org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572)
    org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212)
    org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551)
    org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:140)
    org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294)
    com.capstone.server.controller.TeenController.visualizar(TeenController.java:38)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:606)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:214)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:748)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:822)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
    org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

真的不知道该怎么办了。我已经尝试在我的方法中添加@Transaction注释,这会导致错误,而且这是有效的。但是,当我将teen对象发送到我的android应用程序时,在将该对象转换为json时,会得到相同的异常。

我的配置文件是:

servlet-context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd"
>

    <!-- DispatcherServlet Context: defines this servlet's request-processing
        infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
   

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving
        up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources
        in the /WEB-INF/views directory -->
    <beans:bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <beans:bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <beans:property name="basename" value="classpath:messages" />
        <beans:property name="defaultEncoding" value="UTF-8" />
    </beans:bean>

    <beans:bean id="localeResolver"
        class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <beans:property name="defaultLocale" value="en" />
        <beans:property name="cookieName" value="myAppLocaleCookie"></beans:property>
        <beans:property name="cookieMaxAge" value="3600"></beans:property>
    </beans:bean>

    <interceptors>
        <beans:bean
            class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
            <beans:property name="paramName" value="locale" />
        </beans:bean>
    </interceptors>

    <!-- Configure to plugin JSON as request and response in method handler -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jsonMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>

    <!-- Configure bean to convert JSON to POJO and vice versa -->
    <beans:bean id="jsonMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    </beans:bean>

    <!-- Enable @Transactional annotation -->
    <tx:annotation-driven />

    <mvc:interceptors>
        <beans:bean class="com.capstone.server.interceptor.LoginInterceptor" />
    </mvc:interceptors>

    <beans:bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

        <!-- Setting maximum upload size -->
        <beans:property name="maxUploadSize" value="1000000" />
    </beans:bean>

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

</beans:beans>

持久jpaconfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class PersistenceJPAConfig {

    @Resource
    private Environment env;

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] {
                Constants.PACKAGE_NAME
        });

        JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        em.setJpaProperties(additionalProperties());

        return em;
    }

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();

        // Connection data
        dataSource.setDriverClassName(env.getRequiredProperty("db.driver"));
        dataSource.setUrl(env.getRequiredProperty("db.url"));
        dataSource.setUsername(env.getRequiredProperty("db.username"));
        dataSource.setPassword(env.getRequiredProperty("db.password"));
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    Properties additionalProperties() {
        Properties properties = new Properties();

        // Hibernate properties
        properties.setProperty("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
        properties.setProperty("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
        properties.setProperty("hibernate.format_sql",
                env.getRequiredProperty("hibernate.format_sql"));

        // Updates the database and generate tables, if needed
        properties.setProperty("hibernate.hbm2ddl.auto",
                env.getRequiredProperty("hibernate.hbm2ddl.auto"));

        // Initializes database with admin entry in User table
        properties.setProperty("hibernate.hbm2ddl.import_files",
                env.getRequiredProperty("hibernate.hbm2ddl.import_files"));
        properties.setProperty("hibernate.hbm2ddl.import_files_sql_extractor",
                env.getRequiredProperty("hibernate.hbm2ddl.import_files_sql_extractor"));

        return properties;
    }
}


我最终是这样做的:

相反,我只有一个仅获取惰性数据的teenDao.findAll()方法,我创建了另一个接收布尔参数forceLoad的方法。实现过程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Transactional
public Teen find(String email) {
    return find(email, false);
}

@Transactional
public Teen find(String email, boolean forceLoad) {
    Teen teen = em.find(Teen.class, email);
    if(teen != null && forceLoad) {
        Hibernate.initialize(teen.getUser());
        Hibernate.initialize(teen.getFollowerList());
        Hibernate.initialize(teen.getPendingFollowerList());
        Hibernate.initialize(teen.getCheckInList());
    }
    return teen;
}

这样,当传递forceLoad为真时,我只初始化我想要的列表。


对于事务,当您有一个懒惰的字段时,您无法获取该字段的值,因为您在事务之外,并且您的会话已被释放。

一种方法,将Transactional注释放在Controller上,或者将Controller的内容委托给标记为Transactional的服务类,并在此服务类的方法中进行操作。

当然,不能在事务外部调用惰性字段。

当做,


另一种方法是让您的字段具有延迟加载,并传递这样的查询:*从青少年加入fecth t.teenlist followerlist

当做,

英尺


您必须扩大事务上下文,以便代理可以获取实体的其余关系数据:

1
2
3
4
5
6
7
8
@Transactional
public void foo() {
   List<Teen> teens = (List<Teen>) teenDao.findAll();

    for (Teen item : teens) {
     ...
    }
}


1
2
3
4
5
6
7
8
public class Teen implements Serializable {

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name ="TEEN_FOLLOWER",
            joinColumns = @JoinColumn(name ="teenEmail"),
            inverseJoinColumns = @JoinColumn(name ="followerEmail"))
    private List<Follower> followerList;
}

您可以获取类型"热切"而不是"懒惰"。

它将在加载父对象时获取所有子对象。

但它会导致性能问题。

其他解决方案,您可以根据请求模式尝试会话

http://docs.jboss.org/hibernate/orm/4.2/devguide/en-us/html/ch02.html