【问题标题】:HibernateException: Couldn't obtain transaction-synchronized Session for current threadHibernateException:无法获取当前线程的事务同步会话
【发布时间】:2014-12-21 04:03:42
【问题描述】:

我在尝试使用 @Service 带注释的类时遇到以下异常:

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
    at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134) ~[spring-orm-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014) ~[hibernate-core-4.3.6.Final.jar:4.3.6.Final]
    at webapp.base.repository.GenericDaoImpl.saveOrUpdate(GenericDaoImpl.java:59) ~[base-0.0.1-SNAPSHOT-classes.jar:na]
    at com.example.repository.PageViewDaoImpl.saveOrUpdate(PageViewDaoImpl.java:19) ~[site-0.0.1-SNAPSHOT.jar:na]
    at com.example.repository.PageViewDaoImpl.saveOrUpdate(PageViewDaoImpl.java:14) ~[site-0.0.1-SNAPSHOT.jar:na]
    at com.example.service.PageViewServiceImpl.savePageView(PageViewServiceImpl.java:26) ~[site-0.0.1-SNAPSHOT.jar:na]
    at com.example.interceptor.PageViewInterceptor.preHandle(PageViewInterceptor.java:29) ~[site-0.0.1-SNAPSHOT.jar:na]
    at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:130) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877) ~[spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:620) [servlet-api-3.0.jar:na]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842) [spring-webmvc-4.1.1.RELEASE.jar:4.1.1.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) [servlet-api-3.0.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:488) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:466) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:337) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:427) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:200) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) [tomcat-catalina-7.0.52.jar:7.0.52]
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) [tomcat-coyote-7.0.52.jar:7.0.52]
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) [tomcat-coyote-7.0.52.jar:7.0.52]
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:313) [tomcat-coyote-7.0.52.jar:7.0.52]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_65]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_65]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_65]

我初始化我的应用程序的方式很复杂,所以我需要提供一个指向完整基本代码的链接以获取更多信息:https://github.com/dtrunk90/webapp-base。 我将其用作 maven 叠加层。

这里是必要的代码:

初始化程序(来自 webapp-base):

public abstract class AbstractWebApplicationInitializer extends AbstractDispatcherServletInitializer {
    @Override
    protected String[] getServletMappings() {
        return new String[] {"/*"};
    }

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");
        encodingFilter.setForceEncoding(true);
        return new Filter[] {encodingFilter};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();

        ConfigurableEnvironment environment = rootContext.getEnvironment();
        environment.setDefaultProfiles("production");

        PropertyUtil propertyUtil = PropertyUtil.getInstance(environment.getActiveProfiles());
        String[] basePackages = propertyUtil.getPropertySplitTrimmed("webapp", "basePackages");
        rootContext.scan(basePackages);

        return rootContext;
    }

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        return new AnnotationConfigWebApplicationContext();
    }
}

初始化程序(来自我的 webapp):

public class WebApplicationInitializer extends AbstractWebApplicationInitializer {
}

@Configuration(来自 webapp-base):

@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {
    @Bean
    public DataSource dataSource() throws IOException {
        Properties conProps = PropertyUtil.getInstance().getProperties("jdbc");
        if (conProps.containsKey("url")) {
            DriverManagerDataSource dataSource = new DriverManagerDataSource(conProps.getProperty("url"), conProps);
            dataSource.setDriverClassName(conProps.getProperty("driverClassName"));
            return dataSource;
        }

        return null;
    }

    @Bean
    public SessionFactory sessionFactory() throws IOException {
        DataSource dataSource = dataSource();
        if (dataSource != null) {
            LocalSessionFactoryBuilder sessionBuilder = new LocalSessionFactoryBuilder(dataSource);
            sessionBuilder.scanPackages(PropertyUtil.getInstance().getPropertySplitTrimmed("hibernate", "packagesToScan"));
            sessionBuilder.addProperties(PropertyUtil.getInstance().getProperties("hibernate"));
            return sessionBuilder.buildSessionFactory();
        }

        return null;
    }

    @Bean
    public HibernateTransactionManager transactionManager() throws IOException {
        SessionFactory sessionFactory = sessionFactory();
        if (sessionFactory == null) {
            return null;
        }

        return new HibernateTransactionManager(sessionFactory);
    }
}

@Configuration(来自我的网络应用):

@Configuration
public class MainConfiguration extends WebMvcConfigurerAdapter {
    @Autowired
    private PageViewInterceptor pageViewInterceptor; // Is annotated with @Component

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(pageViewInterceptor);
    }
}

@Service:

@Service
public class PageViewServiceImpl implements PageViewService {
    @Autowired
    private PageViewDao pageViewDao;

    @Override
    public void savePageView(long ip, String visitPage, String userAgent) {
        PageView obj = new PageView();
        obj.setVisitDate(new Date());
        obj.setUserAgent(userAgent);
        obj.setPage(visitPage);
        obj.setIp(ip);

        pageViewDao.saveOrUpdate(obj);
    }
}

@Repository:

@Repository
public class PageViewDaoImpl extends GenericDaoImpl<PageView, Long> implements PageViewDao {
    @Override
    public void saveOrUpdate(PageView obj) {
        if (!obj.isBot()) {
            super.saveOrUpdate(obj);
        }
    }
}

public abstract class GenericDaoImpl<T extends Identifier<I>, I extends Serializable> implements GenericDao<T, I> {
    @Autowired
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        if (sessionFactory == null) {
            throw new IllegalStateException("SessionFactory has not been set on DAO before usage");
        }

        return sessionFactory;
    }

    @Transactional
    public void saveOrUpdate(T obj) {
        getSessionFactory().getCurrentSession().saveOrUpdate(obj);
    }
}

然后我会自动装配 PageViewService 并使用它的方法。

我知道这里有几个相同问题的问题,但我已经检查了任何内容:

Could not obtain transaction-synchronized Session for current thread

  • 提供@EnableTransactionManagement
  • 服务将作为接口自动装配

HibernateException: Could not obtain transaction-synchronized Session for current thread

  • 在我使用getSessionFactory().getCurrentSession() 的所有地方检查@Transactional

Spring Hibernate - Could not obtain transaction-synchronized Session for current thread

  • 提供@EnableTransactionManagement
  • 在我使用getSessionFactory().getCurrentSession() 的任何地方检查@Transactional

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread

  • 没有有用的答案。我希望对我的所有组件进行组件扫描,而不仅仅是控制器

【问题讨论】:

  • 我在您的 saveOrUpdate() 方法上没有看到任何事务注释。
  • 因为super.saveOrUpdate() 拥有它。但是在那里添加注释仍然会给出相同的异常。
  • 我怀疑你有一个根 spring 上下文,其中应用了事务配置,以及一个 web spring 上下文,它不是。并且服务和 DAO 应该只在根上下文中声明/扫描,但它们也可以在 Web 上下文中使用。在这种情况下,Web bean 会获得一个与根上下文中不同的 DAO 实例,并且不会被事务拦截器代理。
  • 有一个根上下文可以扫描配置和组件/服务/存储库(请参阅我的问题中的 WebApplicationInitializer)。并且有 3 个配置类:2 个来自 webapp-base(WebMvcConfiguration 和 TransactionConfiguration),1 个来自我的 webapp(MainConfiguration)。
  • 我应该在 root 和 servlet 上下文中执行不同的扫描吗?

标签: java spring hibernate orm transactions


【解决方案1】:

查看您的日志,我可以立即看出您的交易设置设置错误。那是因为您的堆栈跟踪中没有 TransactionInterceptor 调用。

TransactionInterceptor 在您的 Web 控制器调用实际的 Service 方法时由 Spring Service 代理调用。

  1. 确保使用 Spring hibernate4 类:

    org.springframework.orm.hibernate4.HibernateTransactionManager
    
  2. 不要覆盖@Transactional 方法,而是使用模板模式。

  3. 尝试改用JPATransactionManager,这样你就可以用@PersistenceContext 注解注入当前的EntityManager。这比在每个 DAO 方法中调用 sessionFactory.getCurrentSession() 要优雅得多。

【讨论】:

  • 1.确定:github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/… 2. 我做了一个模板模式(没有解决问题) 3. 我想使用 Hibernate,而不是 JPA。
  • 移动到 JpaTransactionManager 有效。这实际上并没有解决问题,但我会给你 +200。我是否需要使用EntityManager.createQuery(String, Class) 来获取基于外键或其他字段的列表?这真的很难看,因为 Hibernate 为此使用了 @Entity 类。所以我不需要编写 SQL 查询。
  • 您可以同时使用 JPQL 或 HQL 语法,只要您使用 Hibernate 作为您的 JPQ 提供程序。如果愿意,您也可以使用本机查询,但 createQuery 使用的是 JPQL/HQL,因此您不必像在 SQL 查询中那样直接使用 FK。对于实体查询,您只需在可导航关联上使用 join 指令。
  • 那么不使用查询语言就没有办法创建查询吗?您将如何意识到这一点:github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/….createQuery("count(*) from " + getType().getName())?这有多丑?
  • 您也可以使用 JPA 2.0 Criteria 并避免字符串连接查询。
【解决方案2】:

一个

对于@Service@Repository,您必须使用@Transactional。它允许 Spring 应用和创建具有事务支持的代理。

在您的代码中,您的 @Service 类在类级别或方法级别都没有 @Transacional

第二

实现WebApplicationInitializer 的类在哪里? 我看到你正在扩展一个类。无论如何我的观点是,如下所示:

@Override
public void onStartup(ServletContext container) {
    // Create the 'root' Spring application context
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
    rootContext.register(CentralServerConfigurationEntryPoint.class);

    // Manage the lifecycle of the root application context
    container.addListener(new ContextLoaderListener(rootContext));

    // Create the dispatcher servlet's Spring application context
    AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
    dispatcherServlet.register(CentralWebConfigurationEntryPoint.class);

    // Register and map the dispatcher servlet
    ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping("/");

}

CentralServerConfigurationEntryPoint.class 必须只扫描必须在服务器端工作的组件(@Service@Repository@Configuration 用于事务、休眠、数据源等)

CentralWebConfigurationEntryPoint 必须仅扫描必须在客户端/Web 端工作的组件(@Controller@Configuration 用于格式化程序、磁贴、转换器等)

我不明白你的代码

@Override
protected WebApplicationContext createRootApplicationContext() {
    AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();

    ConfigurableEnvironment environment = rootContext.getEnvironment();
    environment.setDefaultProfiles("production");

    PropertyUtil propertyUtil = PropertyUtil.getInstance(environment.getActiveProfiles());
    String[] basePackages = propertyUtil.getPropertySplitTrimmed("webapp", "basePackages");
    rootContext.scan(basePackages);

    return rootContext;
}

@Override
protected WebApplicationContext createServletApplicationContext() {
    return new AnnotationConfigWebApplicationContext();
}

我的意思是:您必须拥有两个AnnotationConfigWebApplicationContext,一个用于服务器端和Web 端。

【讨论】:

  • 我已将@Transactional 添加到我所有的@Repository@Service 课程中,但均未成功。请查看 Spring JavaDocs:docs.spring.io/spring/docs/current/javadoc-api/org/…
  • 我的方法或配置已被相同的 Spring 开发人员在 Github 上提供的 Spring Samples 引用。我第一次看到你的方法,只是好奇,你的博客/教程是什么?
  • 例子:saltnlight5.blogspot.de/2013/10/… 网上有几个教程使用AbstractAnnotationConfigDispatcherServletInitializer作为超类。我查看了代码以了解那里发生了什么,然后我意识到使用 AbstractDispatcherServletInitializer 对我的方法来说更好,因为我想扫描包而不是直接添加它们。
  • 感谢链接,他的代码只覆盖web端,不包括如何引用或使用服务器端。
  • “服务器端”和“网页端”是什么意思?有一个根和一个 servlet 上下文:getRootConfigClasses/getServletConfigClasses
【解决方案3】:

这个问题的答案非常简短,您只需将@Transactional 与您的dao 类一起使用,将您的配置类标记为@EnableTransactionManagement 并创建一个bean

**@Bean
public PlatformTransactionManager transactionManager() {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
    return transactionManager;
}**

这里,如果您看到 EnableTransactionManagement 注解中提供的代码示例,建议使用 DataSourceTransactionManager 而不是 HibernateTransactionManager。

【讨论】:

    【解决方案4】:
    @Bean
    @Autowired
    public HibernateTransactionManager transactionManager(SessionFactory sessionFactory)
    { 
        HibernateTransactionManager htm = new HibernateTransactionManager();
        htm.setTransactionSynchronization(HibernateTransactionManager.SYNCHRONIZATION_ALWAYS);
        htm.setDataSource(dataSource());
        htm.setSessionFactory(sessionFactory);
        return htm;
    }
    
        You just need to add
        -------------------------------
        htm.setTransactionSynchronization(HibernateTransactionManager.SYNCHRONIZATION_ALWAYS);
        -------------------------------
        I used dependencies 
        -----------------------------------
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.2.2.Final</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.0.1.RELEASE</version>
    </dependency>
    

    【讨论】:

    • 欢迎来到 SO,回答问题时不要只提供代码。添加一些有关代码以及您如何解决问题的信息。
    猜你喜欢
    • 2017-01-18
    • 2014-11-28
    • 2015-02-04
    • 2017-01-01
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多