使用Vaadin Hibernates ThreadLocal <session> .get()在第一次调用

时间:2016-06-30 08:35:58

标签: java hibernate vaadin thread-local hibernate-session

我正在使用Vaadin框架中的Web应用程序从MySQL数据库后端检索一些数据。应用程序本身是一个调查系统,用于跟踪软件顾问技能(并对其进行评级)。我的一些同事已经为此编写了后端代码。现在我正在尝试将Vaadin前端与它结合起来,我遇到了一个非常讨厌的问题。我有两个DAO课程,PersonDAO和SurveyDAO。

首先调用PersonDAO,然后调用SurveyDAO。 PersonDAO从HibernateUtil类(不是我自己编写)中检索Session,该类存储在 ThreadLocal Session 变量中。当SurveyDAO完全相同时,ThreadLocal Session.get()方法返回一个空引用!经过一番阅读后,我知道Vaadin有与Sessions合作的奇怪方式。但我无法看到我如何解决这个问题,或者如何使用DAO的另一种方式。

以下是完整的代码:

在我的主UI中,加载webapp后,我创建了一个ComboBox组件,该组件由PersonDAO返回的List填充。如此:

private ComboBox createConsultantsComboBox() {

    ComboBox comboBox = new ComboBox("Surveyed consultants:");

    BeanItemContainer<Person> consultantContainer = new BeanItemContainer<>(Person.class);
    consultantContainer.addAll(personDAO.findAll());
    comboBox.setContainerDataSource(consultantContainer);

    comboBox.addValueChangeListener(new ConsultantSelectedListener());

    return comboBox;

}

这个DAO无缝地工作。但是,当该ComboBox中的值发生更改(选择了顾问)时,我使用该Person的id从该顾问调查的SurveyDAO中获取List<Survey>。这是使用以下代码完成的,这是一个附加到填充了Person对象的ComboBox的ValueChangeListener:

private class ConsultantSelectedListener implements ValueChangeListener {

    @Override
    public void valueChange(ValueChangeEvent event) {
        if (event.getProperty().getValue() != null) {
            Person selectedConsultant = (Person) event.getProperty().getValue();
            leftPanel.addComponent(createSurveysComboBox(selectedConsultant.getId()));
        }
    }
}

这是出错的地方。 createSurveysComboBox()方法的工作方式与createConsultantsComboBox()方法相同,不同之处在于它将一个int id参数传递给SurveyDAO,因为它在查询中需要作为参数。但是,当SurveyDAO调用findByPersonId时,我得到一个NullPointerException并打印以下堆栈跟踪:

jun 30, 2016 10:15:27 AM com.vaadin.server.DefaultErrorHandler doDefault
SEVERE: 
java.lang.NullPointerException
    at be.kapture.util.HibernateUtil.getSession(HibernateUtil.java:55)
    at be.kapture.dao.AbstractDAO.getCurrentSession(AbstractDAO.java:16)
    at be.kapture.dao.SurveyDAO.findByPersonId(SurveyDAO.java:24)
    at be.kapture.web.ConsultantSkillsUI.createSurveysComboBox(ConsultantSkillsUI.java:92)
    at be.kapture.web.ConsultantSkillsUI.access$100(ConsultantSkillsUI.java:27)
    at be.kapture.web.ConsultantSkillsUI$ConsultantSelectedListener.valueChange(ConsultantSkillsUI.java:82)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:508)
    at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:198)
    at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161)
    at com.vaadin.server.AbstractClientConnector.fireEvent(AbstractClientConnector.java:1008)
    at com.vaadin.ui.AbstractField.fireValueChange(AbstractField.java:1159)
    at com.vaadin.ui.AbstractField.setValue(AbstractField.java:570)
    at com.vaadin.ui.AbstractSelect.setValue(AbstractSelect.java:732)
    at com.vaadin.ui.AbstractField.setValue(AbstractField.java:468)
    at com.vaadin.ui.ComboBox.changeVariables(ComboBox.java:730)
    at com.vaadin.server.communication.ServerRpcHandler.changeVariables(ServerRpcHandler.java:603)
    at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:422)
    at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:273)
    at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:79)
    at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1409)
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:364)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2508)
    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2497)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

findByPersonId代码可以在上面找到。我现在将提供AbstractDAO.getCurrentSession()和HibernateUtil.getSession()方法的代码片段(再次,我没有自己编写 - 因此我无法弄清楚出了什么问题!)

AbstractDao的

package be.kapture.dao;

import org.hibernate.Session;

import be.kapture.util.HibernateUtil;

public abstract class AbstractDAO<T> {

    final Class<T> typeParameterClass;

    public AbstractDAO(Class<T> typeParameterClass) {
        this.typeParameterClass = typeParameterClass;
    }

    public Session getCurrentSession() {
        return HibernateUtil.getSession();
    }

    public void create(T t) {
        getCurrentSession().save(t);
    }

    public void update(T t) {
        getCurrentSession().update(t);
    }

    public void delete(T t) {
        getCurrentSession().delete(t);
    }

    public T read(int id) {
        return (T) getCurrentSession().get(typeParameterClass, id);
    }

}

的HibernateUtil

package be.kapture.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;

import java.util.Objects;

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();
    private static final ThreadLocal<Session> sessionManagers = buildSessionManagers();

    private static SessionFactory buildSessionFactory() {
        try {
            StandardServiceRegistry standardRegistry = new StandardServiceRegistryBuilder()
                    .configure("/hibernate.cfg.xml").build();

            Metadata metadata = new MetadataSources(standardRegistry).addResource("be/kapture/entities/Person.hbm.xml")
                    .addResource("be/kapture/entities/Skill.hbm.xml")
                    .addResource("be/kapture/entities/SkillGroup.hbm.xml")
                    .addResource("be/kapture/entities/SkillNature.hbm.xml")
                    .addResource("be/kapture/entities/Survey.hbm.xml")
                    .addResource("be/kapture/entities/SurveyDetail.hbm.xml").getMetadataBuilder()
                    .applyImplicitNamingStrategy(ImplicitNamingStrategyJpaCompliantImpl.INSTANCE).build();

            SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
            return sessionFactory;

        } catch (Throwable ex) {
            System.err.println("initial SessionFactory creation failed " + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }


    private static ThreadLocal<Session> buildSessionManagers() {
        ThreadLocal<Session> sessionManagers = new ThreadLocal<>();
//      System.out.println("sessionFactory.getCurrentSession == null ??");
//      System.out.println(sessionFactory.getCurrentSession() == null);
        sessionManagers.set(sessionFactory.getCurrentSession());
        return sessionManagers;
    }

    public static Session getSession() {
//      Objects.requireNonNull(sessionFactory, "sessionFactory was null");
//      Objects.requireNonNull(sessionManagers, "sessionManagers was null");
//      System.out.println(sessionManagers.get());
        Session session = sessionManagers.get();
//      Objects.requireNonNull(session, "session was null");
        if(!session.isOpen()){
            sessionManagers.remove();
            session = sessionFactory.getCurrentSession();
            sessionManagers.set(session);           
        }
        session.beginTransaction();
        return session;
    }

}

在会话检索方法中,您可以看到一些调试输出。取消注释后,HibernateUtil.getSession()方法中的会话变量将触发,因为ThreadLocal返回了空引用。

为什么会这样?

1 个答案:

答案 0 :(得分:0)

问题通过向SessionFactory询问每个事务的新会话来解决 - 如Hibernate Session JavaDoc中所述。在问题的代码中实现,这是使其工作所需的唯一更改

在HibernateUtil中

public static Session getSession() {

        return sessionFactory.openSession();

}

AbstractDAO(以及所有DAO继承它)将调用其方法getCurrentSession - 因此应该更好地命名为getNewSession。调用代码应该在其上启动新事务,执行其数据库操作并相应地提交/回滚。

但是,我觉得这是处理Sessions的一种草率方式。我还没有读到有关SessionFactory关闭其会话本身的任何信息。 JavaDoc还说应该使工厂的实现者线程安全Hibernate SessionFactory JavaDoc here

我认为有人可以通过更好的解决方案来增强这个答案。随意这样做。