Spring / Hibernate @Transactional - 会话应该关闭之前

时间:2011-03-29 22:11:34

标签: hibernate spring spring-mvc transactions

我无法弄清楚为什么,但是Hibernate会话才应该关闭,所以我无法获取延迟加载的列表。

在日志中,它显示会话在DAO内的hibernateTemplate.findByNamedParam()之后立即关闭。

当我运行我的网络应用时,我收到以下错误:

Mar 29, 2011 3:13:21 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Spring MVC Dispatcher Servlet] in context with path [/apps] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.communitydriven.apps.entities.Project.tags, no session or session was closed] with root cause
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.communitydriven.apps.entities.Project.tags, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:365)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:108)
    at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272)
    at com.communitydriven.apps.managers.ProjectManager.getProject(ProjectManager.java:98)
    at com.communitydriven.apps.controllers.ProjectController.getViewProject(ProjectController.java:86)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:426)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:414)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:240)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:164)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:498)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:164)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:562)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:394)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:243)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:188)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:166)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:619)

我的道:

@Repository
public class ProjectDao implements IProjectDao {

    private HibernateTemplate hibernateTemplate;

    @Autowired
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    ...

    /**
     * {@inheritDoc}
     */
    @Override
    public Project getProject(Project project) {
        // Validate required parameters
        if ( (project.getId() == null) ) {
            throw new NullPointerException("Missing required parameter: " + 
                    project.toString());
        }

        // Parameters
        List<String> paramNames = new ArrayList<String>();
        List<Object> values = new ArrayList<Object>();

        // Construct HQL
        StringBuffer hql = new StringBuffer();
        hql.append("from Project p ");

        boolean whereUsed = false; // track of "where" clause has been used

        // Filter by Id
        if (project.getId() != null) {
            whereUsed = DaoUtils.appendFilter(whereUsed, hql);
            hql.append("p.id = :id ");
            paramNames.add("id");
            values.add(project.getId());
        }

        // Get list of matching projects
        @SuppressWarnings("unchecked")
        List<Project> projects = 
            hibernateTemplate.findByNamedParam(
                    hql.toString(),
                    paramNames.toArray(new String[paramNames.size()]), 
                    values.toArray());

        // Get unique result
        Project projectResult = DataAccessUtils.uniqueResult(projects);

        return projectResult;
    }
}

我的经理:

@Component
public class ProjectManager implements IProjectManager {

    @Autowired
    private IProjectDao projectDao;

    ...


    /**
     * {@inheritDoc}
     */
    @Transactional
    @Override
    public ProjectMO getProject(Long projectId) {
        Project project = new Project();
        project.setId(projectId);
        project = projectDao.getProject(project);

        ProjectMO projectMO = new ProjectMO();
        projectMO.setId(project.getId());
        projectMO.setName(project.getName());
        projectMO.setDescription(project.getDescription());

        StringBuffer tags = new StringBuffer();
        final String DELIMITER = ", ";
        for (Tag tag : project.getTags()) {
            tags.append(tag.getName() + DELIMITER);
        }
        projectMO.setTags(tags.toString());

        return projectMO;
    }
}

实体:

@Entity
@Table
public class Project {
    private Long id;
    private String name;
    private String description;
    private User submittedBy;
    private List<Tag> tags;

    public String toString() {
        final String DELIMITER = ", ";
        StringBuffer sb = new StringBuffer();

        sb.append(getClass().getName() + ": [");
        sb.append("id: " + id).append(DELIMITER);
        sb.append("name: " + name).append(DELIMITER);
        sb.append("description: " + description).append("]");

        return sb.toString();
    }

    // GETTERS AND SETTERS //

    @Id
    @Column
    @GeneratedValue
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    @Column
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @Column
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }

    @OneToOne
    @JoinColumn
    public User getSubmittedBy() {
        return submittedBy;
    }

    public void setSubmittedBy(User submittedBy) {
        this.submittedBy = submittedBy;
    }

    @ManyToMany
    @JoinTable(name="Projects_Tags", 
            joinColumns={@JoinColumn(name="project_id")}, 
            inverseJoinColumns={@JoinColumn(name="tag_id")})
    public List<Tag> getTags() {
        return tags;
    }

    public void setTags(List<Tag> tags) {
        this.tags = tags;
    }
}

我的数据库上下文:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 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/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-3.0.xsd">

    <context:property-placeholder location="classpath:database.properties"/>

    <bean id="dataSource" 
             class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.MySQLDialect
                hibernate.hbm2ddl.auto=update
            </value>
        </property>
        <property name="packagesToScan" value="com.communitydriven.apps.entities" />
    </bean>

    <tx:annotation-driven />

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>

</beans>

8 个答案:

答案 0 :(得分:5)

您是否设置了OpenSessionInViewFilter

答案 1 :(得分:4)

默认情况下,关联是延迟提取的。因此,一旦退出@Transactional注释的范围,就无法获得该集合。

虽然OpenSessionInViewFilter是一个选项,但我同意你的看法,这不是一个好方法。通常,您希望严格控制事务边界。

如果您知道您总是想要进行获取连接,只需将其包含在您的HQL中,或者在@ManyToMany中设置fetch = FetchType.EAGER。

如果你想在某些情况下包含标签而在其他情况下不包含标签,你可以在需要标签的情况下进行Hibernate.initialize(project.getTags()),这将对标签进行单独的查询,但是它不是n + 1。或者,您可以有两个单独的HQL查询(一个包含提取连接,另一个不包含提取连接)。唯一的问题是,如果您有多个集合,则无法在多个集合上获取连接。所以Hibernate.initialize()在这种情况下非常有用。

答案 2 :(得分:1)

您是否尝试过明确定义事务管理器实现,以及使用aspectj进行编织(因为您的注释是在具体类上)?

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" />

答案 3 :(得分:0)

什么是正在使用的事务管理器 - 它应该是HibernateTransactionManager或JTATransactionManager。

答案 4 :(得分:0)

我遇到了同样的问题,我不想将我的组件更改为服务,我也希望将@ManyToMany保留为FetchType.Lazy,因为它已多次调用并且不需要加入实体详细信息。所以我的脏解决方法是调用一个新实体而不是使用现有实体。请参阅我使用的示例:(假设您已在 DAO 中实现了 getProjectById 方法)

List<Tags> tags = projectDAO.getProjectById(project.getId()).getTags();
if(tags.size()>0)
for (Tag tag : ) {
            tags.append(tag.getName() + DELIMITER);
        }
)

而不是:

for (Tag tag : project.getTags()) {
            tags.append(tag.getName() + DELIMITER);
        }
)

答案 5 :(得分:0)

问题可能是由于在web.xml中定义的OpenViewSessionFilter中提供的映射,其中每个http会话都由hibernate会话绑定。

答案 6 :(得分:0)

我遇到了同样的问题。我的问题是我的servlet-context.xmlroot-context.xml(我的申请上下文)

如果您在两个上下文中都指定了组件扫描,则可以通过更改来更正它 1.在servlet-context中,仅为控制器包指定component-scan 2.在根上下文中为其他包指定组件扫描(此处不包括控制器包)

我从这个答案得到了解决方案。您还可以参考该链接并了解@Transactional无效的原因

http://forum.spring.io/forum/spring-projects/data/65235-using-jpa-with-transactional-and-i-get-a-session-is-closed-exception

答案 7 :(得分:-2)

将ProjectManager类注释为@Service而不是@Component。