15

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
Community
  • 1
  • 1
dtrunk
  • 4,685
  • 17
  • 65
  • 109
  • 1
    I don't see any Transactional annotation on your `saveOrUpdate()` method. – JB Nizet Oct 25 '14 at 13:59
  • Cause `super.saveOrUpdate()` has it. But adding the annotation there as well is still giving the same exception. – dtrunk Oct 25 '14 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 Oct 25 '14 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 Oct 25 '14 at 14:51
  • Should I perform different scans in root and servlet context? – dtrunk Oct 28 '14 at 15:09
  • You **must** use `@Transactional` for `@Service` and `@Repository`. In your code your `@Service` class has no the `@Transacional` either in class level or method level – Manuel Jordan Oct 30 '14 at 01:57
  • @ManuelJordan I did. But without success. – dtrunk Oct 30 '14 at 17:52
  • Possible duplicate of [Spring Hibernate - Could not obtain transaction-synchronized Session for current thread](http://stackoverflow.com/questions/26203446/spring-hibernate-could-not-obtain-transaction-synchronized-session-for-current) – Vadzim May 18 '17 at 13:47
  • I am not seeing the same code now in github, can you please share the code in github , i m having the same problem now and want to know how to fix that with JPATransaction manager – rinilnath Aug 14 '20 at 12:18

4 Answers4

8

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.

AdrieanKhisbe
  • 3,899
  • 8
  • 37
  • 45
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • 1. Made sure: https://github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/webapp/base/config/TransactionConfiguration.java#L12 2. I made a template pattern (didn't solved the problem) 3. I want to use Hibernate, not JPA. – dtrunk Oct 30 '14 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 Oct 30 '14 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. – Vlad Mihalcea Oct 30 '14 at 22:05
  • So there's no way to create a query without using a query language? How would you realize this: https://github.com/dtrunk90/webapp-base/blob/master/base/src/main/java/webapp/base/repository/GenericDaoImpl.java#L45 ? `.createQuery("count(*) from " + getType().getName())`? How ugly is this? – dtrunk Oct 31 '14 at 07:48
  • 1
    You can use JPA 2.0 Criteria too and avoid string concatenation queries. – Vlad Mihalcea Oct 31 '14 at 09:24
  • Thanks for the information about needing to see the TransactionInterceptor in the stack trace. It's at least pointing me on the right track for a similar problem I'm having. – AHungerArtist Jan 12 '15 at 21:43
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.

Manuel Jordan
  • 15,253
  • 21
  • 95
  • 158
  • I've added `@Transactional` to all my `@Repository` and `@Service` classes without success. Pls take a look at the Spring JavaDocs: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/support/AbstractDispatcherServletInitializer.html – dtrunk Oct 30 '14 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? – Manuel Jordan Oct 30 '14 at 22:13
  • Example: http://saltnlight5.blogspot.de/2013/10/getting-started-with-annotation-based.html 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 Oct 31 '14 at 07:45
  • Thanks by the link, his code only covers the web side, it is not including how reference or use the server side. – Manuel Jordan Oct 31 '14 at 12:19
  • What's the meaning of "server side" and "web side"? There's a root and a servlet context: `getRootConfigClasses`/`getServletConfigClasses`. – dtrunk Oct 31 '14 at 13:10
  • It is on the reply `server side (@Service, @Repository, @Configuration for Transaction, Hibernate, DataSource etc)` and `client/web side (@Controller, @Configuration for Formatters, Tiles, Converters etc)`. About your second question/sentence, My two `AnnotationConfigWebApplicationContext` are using the `register` method. – Manuel Jordan Oct 31 '14 at 13:31
  • I guess server side and web side is the same as root and servlet context. You can register all controller and components in the root context. You can register them in the servlet context or both as well. As you like/need. – dtrunk Oct 31 '14 at 13:37
  • Is not a wise practice include both root and servlet together. I am using two how you can see in my code. I have read that approach many times in the web. Even in some training courses. – Manuel Jordan Oct 31 '14 at 14:17
  • For your consideration about our two approaches to configure Spring MVC through JavaConfig: http://stackoverflow.com/questions/26676782/when-use-abstractannotationconfigdispatcherservletinitializer-and-webapplication – Manuel Jordan Oct 31 '14 at 14:17
  • I know it's suggested to separate the configurations, but it's not necessary. Nevermind. With JpaTransactionManager it works. – dtrunk Oct 31 '14 at 14:27
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.

Kuldeep Singh
  • 199
  • 1
  • 11
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>
Albin
  • 1
  • 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. – theWellHopeErr Nov 24 '20 at 07:43