web-dev-qa-db-fra.com

HibernateException: Impossible d'obtenir une session synchronisée avec les transactions pour le thread actuel

L'exception suivante s'affiche lorsque j'essaie d'utiliser mes classes annotées @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]

La manière dont j'initialise mon application est compliquée et je dois donc fournir un lien vers le code de base complet pour obtenir des informations supplémentaires: https://github.com/dtrunk90/webapp-base . J'utilise cela comme une superposition maven.

Et voici le code nécessaire:

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

Initialiseur (depuis ma webapp):

public class WebApplicationInitializer extends AbstractWebApplicationInitializer {
}

@Configuration (de 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 (de mon application web):

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

Alors je suis autowiring PageViewService et utilise ses méthodes.

Je sais qu'il y a plusieurs questions avec le même problème ici mais j'ai déjà vérifié quelque chose:

Impossible d'obtenir une session synchronisée avec les transactions pour le thread actuel

  • @EnableTransactionManagement est fourni
  • Les services seront automatiquement connectés en tant qu'interfaces

HibernateException: impossible d'obtenir une session synchronisée avec les transactions pour le thread actuel

  • Vérifié pour @Transactional partout où j'utilise getSessionFactory().getCurrentSession()

Spring Hibernate - Impossible d'obtenir une session synchronisée avec les transactions pour le thread actuel

  • @EnableTransactionManagement est fourni
  • Vérifié pour @Transactional partout où j'utilise getSessionFactory().getCurrentSession()

org.hibernate.HibernateException: impossible d'obtenir une session synchronisée avec les transactions pour le thread actuel

  • Il n'y a pas de réponse utile. Je veux analyser tous les composants, pas seulement le contrôleur
10
dtrunk

En regardant votre journal, je peux dire instantanément que vos paramètres de transaction sont mal définis. En effet, votre trace de pile ne contient aucun appel TransactionInterceptor.

La TransactionInterceptor est appelée par vos mandataires Spring Service lorsque vos contrôleurs Web appellent les méthodes de service réelles. 

  1. Assurez-vous d'utiliser les classes Spring hibernate4:

    org.springframework.orm.hibernate4.HibernateTransactionManager
    
  2. Ne substituez pas les méthodes @Transactional, mais utilisez plutôt un modèle de modèle.

  3. Essayez d'utiliser JPATransactionManager à la place afin d'injecter la EntityManager actuelle avec l'annotation @PersistenceContext à la place. C'est beaucoup plus élégant que d'appeler sessionFactory.getCurrentSession() dans chaque méthode DAO.

7
Vlad Mihalcea

Un

Vous devez utiliser @Transactional pour @Service et @Repository. Il permet à Spring d’appliquer et de créer des mandataires avec prise en charge de Transaction. 

Dans votre code, votre classe @Service n'a pas le @Transacional ni au niveau de la classe ni au niveau de la méthode

Seconde

Où est la classe qui implémente WebApplicationInitializer? Je vois que vous étendez une classe .. Quoi qu'il en soit, voici où se trouve mon point suivant:

@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 doit uniquement analyser les composants devant fonctionner côté serveur (@Service, @Repository, @Configuration pour Transaction, Hibernate, DataSource, etc.) 

CentralWebConfigurationEntryPoint doit uniquement analyser les composants devant fonctionner sur le côté client/Web (@Controller, @Configuration pour les formats, les mosaïques, les convertisseurs, etc.) 

Je ne comprends pas votre code à propos de

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

Mon point est: vous devez avoir deux AnnotationConfigWebApplicationContext une pour le serveur et le côté Web.

2
Manuel Jordan

Très courte réponse pour cet identifiant de questions, il vous suffit d'utiliser @Transactional avec votre classe dao, de marquer votre classe de configuration en tant que @EnableTransactionManagement et de créer un bean

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

Ici, si vous voyez l'exemple de code disponible dans l'annotation EnableTransactionManagement, il est suggéré d'utiliser DataSourceTransactionManager au lieu de HibernateTransactionManager.

0
Kuldeep Singh