16

I'm getting the following exception when trying to use my @Service annotated classes:

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]

The way I initialize my application is complicated so I need to provide a link to the full base code to get additional information: https://github.com/dtrunk90/webapp-base. I'm using this as a maven overlay.

And here is the necessary code:

Initializer (from 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();
    }
}

Initializer (from my webapp):

public class WebApplicationInitializer extends AbstractWebApplicationInitializer {
}

@Configuration (from 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 (from my webapp):

@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);
    }
}

Then I'm autowiring PageViewService and use its methods.

I know there are several questions with the same problem here but I already checked anything:

Could not obtain transaction-synchronized Session for current thread

  • @EnableTransactionManagement is provided
  • Services wil be autowired as interfaces

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

  • Checked for @Transactional everywhere I use getSessionFactory().getCurrentSession()

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

  • @EnableTransactionManagement is provided
  • Checked for @Transactional everywhere I use getSessionFactory().getCurrentSession()

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

  • There's no helpful answer. I want component scanning for all my components, not only controller
9
  • 1
    I don't see any Transactional annotation on your saveOrUpdate() method.
    – JB Nizet
    Commented Oct 25, 2014 at 13:59
  • Cause super.saveOrUpdate() has it. But adding the annotation there as well is still giving the same exception.
    – dtrunk
    Commented Oct 25, 2014 at 14:15
  • 2
    What I suspect is that you have a root spring context, where the transactional configuration is applied, and a web spring context, where it's not. And the services and DAOs should only be declared/scanned in the root context, but they're also available in the web context. In that case, the web beans get an instance of the DAO that is different from the one in the root context, and which is not proxied by the transaction interceptor.
    – JB Nizet
    Commented Oct 25, 2014 at 14:26
  • There's a root context which scans for configurations and components/services/repositories (see WebApplicationInitializer in my question). And there are 3 configuration classes: 2 from webapp-base (WebMvcConfiguration and TransactionConfiguration) and 1 from my webapp (MainConfiguration).
    – dtrunk
    Commented Oct 25, 2014 at 14:51
  • Should I perform different scans in root and servlet context?
    – dtrunk
    Commented Oct 28, 2014 at 15:09

4 Answers 4

8
+200

Looking at your log I can instantly tell that your transaction settings are wrongly set. That's because there's no TransactionInterceptor call in your stack trace.

The TransactionInterceptor is called by your Spring Service proxies when your web controllers call the actual Service methods.

  1. Make sure you use the Spring hibernate4 classes:

    org.springframework.orm.hibernate4.HibernateTransactionManager
    
  2. Don't override @Transactional methods, but use a template patterns instead.

  3. Try using JPATransactionManager instead so you can inject the current EntityManager with the @PersistenceContext annotation instead. This is much more elegant than calling sessionFactory.getCurrentSession() in every DAO method.

6
  • 1. Made sure: github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/… 2. I made a template pattern (didn't solved the problem) 3. I want to use Hibernate, not JPA.
    – dtrunk
    Commented Oct 30, 2014 at 18:51
  • Moving to JpaTransactionManager works.That didn't solve the problem actually, but I'll give you the +200. Do I need to use EntityManager.createQuery(String, Class) to get a list based on a foreign key or other fields? That's really ugly as Hibernate used the @Entity classes for this. So I don't need to write SQL querys.
    – dtrunk
    Commented Oct 30, 2014 at 21:56
  • You can use both the JPQL or the HQL syntax as long as you use Hibernate as your JPQ provider. You can also use native queries if you like, but createQuery is using JPQL/HQL so you don't have to use FK directly like in SQL queries. WIth entity queries you simply use the join directive on navigable associations. Commented Oct 30, 2014 at 22:05
  • So there's no way to create a query without using a query language? How would you realize this: github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/… ? .createQuery("count(*) from " + getType().getName())? How ugly is this?
    – dtrunk
    Commented Oct 31, 2014 at 7:48
  • 1
    You can use JPA 2.0 Criteria too and avoid string concatenation queries. Commented Oct 31, 2014 at 9:24
4

One

You must use @Transactional for @Service and @Repository. It lets Spring apply and create proxies with Transaction support.

In your code your @Service class has no the @Transacional either in class level or method level

Second

Where is the class that implements WebApplicationInitializer? I see you are extending a class.. Anyway My Point is, where is something like the following:

@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("/");

}

Where CentralServerConfigurationEntryPoint.class must only scan components that must work in the server side (@Service, @Repository, @Configuration for Transaction, Hibernate, DataSource etc)

Where CentralWebConfigurationEntryPoint must only scan components that must work in the client/web side (@Controller, @Configuration for Formatters, Tiles, Converters etc)

I dont understand your code about

@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();
}

My point is: you must have two AnnotationConfigWebApplicationContext one for the server and web side.

10
  • I've added @Transactional to all my @Repository and @Service classes without success. Pls take a look at the Spring JavaDocs: docs.spring.io/spring/docs/current/javadoc-api/org/…
    – dtrunk
    Commented Oct 30, 2014 at 18:56
  • My approach or configuration has been taken how reference by the Spring Samples offered on Github by the same Spring Developers. First time I see your approach, just curious, what is your blog/tutorial where you have taken your approach? Commented Oct 30, 2014 at 22:13
  • Example: saltnlight5.blogspot.de/2013/10/… There are several tutorials on the net using AbstractAnnotationConfigDispatcherServletInitializer as super class. I looked into the code to understand whats happening there and then I realized using AbstractDispatcherServletInitializer is way better for my approach as I want to scan packages instead of adding them directly.
    – dtrunk
    Commented Oct 31, 2014 at 7:45
  • Thanks by the link, his code only covers the web side, it is not including how reference or use the server side. Commented Oct 31, 2014 at 12:19
  • What's the meaning of "server side" and "web side"? There's a root and a servlet context: getRootConfigClasses/getServletConfigClasses.
    – dtrunk
    Commented Oct 31, 2014 at 13:10
2

Very short answer for this questions id, you just need to use @Transactional with your dao class, mark your configuration class as @EnableTransactionManagement and create a bean

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

Here, if you see the code example available in EnableTransactionManagement annotation, it suggest to use DataSourceTransactionManager instead of HibernateTransactionManager.

0
@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>
1
  • Welcome to SO, when answering a question don't just give the code. Add some info about the code and how you solved the problem. Commented Nov 24, 2020 at 7:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.