Spring / Hibernate @Transactional - закрытие сессии раньше, чем должно - PullRequest
16 голосов
/ 30 марта 2011

Я не могу понять, почему, но сессия Hibernate закрывается раньше, чем нужно, поэтому я не могу получить лениво загруженные списки.

В журналах показано, что сеанс закрывается сразу после hibernateTemplate.findByNamedParam () внутри DAO.

Когда я запускаю свое веб-приложение, я получаю следующую ошибку:

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)

Мой DAO:

@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 ]

5 голосов
/ 30 марта 2011

Вы установили OpenSessionInViewFilter ?

4 голосов
/ 30 марта 2011

Ассоциации по умолчанию лениво выбираются. Поэтому, как только вы выйдете из области действия аннотации @Transactional, вы не сможете получить коллекцию.

Хотя OpenSessionInViewFilter является опцией, я согласен с вами, что это не очень хороший подход. В общем, вы хотите осуществлять жесткий контроль над транзакционными границами.

Если вы знаете, что всегда хотите сделать выборочное соединение, просто включите это в свой HQL или установите fetch = FetchType.EAGER в своем @ ManyToMany.

Если вы хотите включить теги в некоторых случаях, но не в других, вы можете сделать Hibernate.initialize (project.getTags ()) для случая, когда вам нужны теги, которые будут выполнять отдельный запрос для тегов, но это не п + 1. В качестве альтернативы вы можете иметь два отдельных запроса HQL (один, который включает в себя выборочное соединение, а другой - нет). Единственное, что если у вас есть несколько коллекций, вы не можете делать выборочные объединения для более чем одной. Так что Hibernate.initialize () довольно полезен в таких контекстах.

1 голос
/ 08 апреля 2011

Вы пытались явно определить реализацию менеджера транзакций, а также использовать аспектj для ткачества (поскольку ваша аннотация относится к конкретному классу)?

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

<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" />
0 голосов
/ 06 января 2016

У меня была такая же проблема.Моя проблема была в том, что мои servlet-context.xml и root-context.xml (мой контекст приложения)

, если у вас есть сканирование компонентов, указанное в обоих контекстах, вы можете исправить это, изменив это следующим образом: 1. В контексте сервлета укажите компонент-проверять только на наличие пакета контроллера 2. В корневом контексте укажите компонентное сканирование для других пакетов (здесь не указывайте пакет контроллера)

Я получил решение из этого ответа.Вы также можете обратиться по ссылке и понять, почему @Transactional не работает

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

0 голосов
/ 04 апреля 2013

Проблема может быть связана с отображением в OpenViewSessionFilter, определенном в файле web.xml, где каждый сеанс http связан с сеансом гибернации.

0 голосов
/ 10 февраля 2012

У меня просто была такая же проблема, я не хотел менять свой компонент на сервис, и я хотел сохранить @ManyToMany как FetchType.Lazy, потому что он вызывался много раз, и детали объединения не нужны. Поэтому мой грязный обходной путь - это вызов новой сущности вместо использования существующей. Обратимся к вашему примеру, который я использовал: (допустим, вы также внедрили метод getProjectById в свой DAO )

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);
        }
)
0 голосов
/ 09 апреля 2011

Аннотируйте свой класс ProjectManager как @Service, а не @ Component.

0 голосов
/ 30 марта 2011

Какой диспетчер транзакций используется - это должен быть HibernateTransactionManager или JTATransactionManager.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...