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@Transactional
partout où j'utilise getSessionFactory().getCurrentSession()
@EnableTransactionManagement
est fourni@Transactional
partout où j'utilise getSessionFactory().getCurrentSession()
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.
Assurez-vous d'utiliser les classes Spring hibernate4:
org.springframework.orm.hibernate4.HibernateTransactionManager
Ne substituez pas les méthodes @Transactional
, mais utilisez plutôt un modèle de modèle.
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.
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("/");
}
Où CentralServerConfigurationEntryPoint.class
doit uniquement analyser les composants devant fonctionner côté serveur (@Service
, @Repository
, @Configuration
pour Transaction, Hibernate, DataSource, etc.)
Où 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.
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.