I have faced a real proglem and solved it, but couldn't figure out what has been happened.
I defined transactionManager
and sessionFactory
bean in root context and my dao class with @Transactional
methods in dispatcher context. And that's all. When I was trying to use getCurrentSession()
in dao, I was getting "could not obtain a current session".
But, as I can remember, dispatcher context is aware about root context and has access to all beans in root context.
Can somebody explain me, why do not transactions open before @Transactional
method if transactionManager
and sessionFactory
were defined in root context and class with @Transactional
in child context?
Database config class
@Configuration
@EnableTransactionManagement
public class DatabaseConfig {
@Bean
public LocalSessionFactoryBean sessionFactory() throws IOException {
LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
sessionFactoryBean.setDataSource(getDatabaseDataSource());
sessionFactoryBean.setPackagesToScan("com.varguss.domain");
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.connection.useUnicode", "true");
properties.setProperty("hibernate.connection.characterEncoding", "utf8");
properties.setProperty("hibernate.connection.charSet", "utf8");
sessionFactoryBean.setHibernateProperties(properties);
return sessionFactoryBean;
}
@Bean
@Autowired
public HibernateTransactionManager getTransactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
@Bean(name = "dataSource", destroyMethod = "close")
public BasicDataSource getDatabaseDataSource() throws IOException {
BasicDataSource databaseDataSource = new BasicDataSource();
Properties properties = new Properties();
ClassPathResource propertiesFileResource = new ClassPathResource("database.properties");
properties.load(propertiesFileResource.getInputStream());
databaseDataSource.setDriverClassName(properties.getProperty("driverClassName"));
databaseDataSource.setUrl(properties.getProperty("url"));
databaseDataSource.setUsername(properties.getProperty("username"));
databaseDataSource.setPassword(properties.getProperty("password"));
return databaseDataSource;
}
}
DAO class
@Repository
@Transactional
public class DbComputerPartDAO implements ComputerPartDAO {
private SessionFactory sessionFactory;
private Strategy strategy;
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
strategy = StrategyFactory.getStrategy(StrategyType.ALL, sessionFactory);
}
@Override
@Transactional(readOnly = true)
public List<ComputerPart> allParts() {
return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part ORDER BY part.count DESC", ComputerPart.class).getResultList();
}
@Override
@Transactional(readOnly = true)
public ComputerPart part(Long id) {
return sessionFactory.getCurrentSession().find(ComputerPart.class, id);
}
@Override
public void save(String name, boolean isImportant, Long count) {
sessionFactory.getCurrentSession().saveOrUpdate(new ComputerPart(name, isImportant, count));
}
@Override
public void remove(Long id) {
ComputerPart computerPart = part(id);
if (computerPart != null)
sessionFactory.getCurrentSession().delete(computerPart);
}
@Override
@Transactional(readOnly = true)
public List<ComputerPart> byImportance(boolean isImportant) {
return sessionFactory.getCurrentSession().createQuery("FROM ComputerPart part WHERE part.isImportant ORDER BY part.count DESC", ComputerPart.class).getResultList();
}
@Override
public void updateImportance(Long id, boolean isImportant) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setImportant(isImportant);
}
@Override
public void updateName(Long id, String name) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setName(name);
}
@Override
public void updateCount(Long id, Long count) {
ComputerPart computerPart = part(id);
if (computerPart != null)
computerPart.setCount(count);
}
@Override
@Transactional(readOnly = true)
public List<ComputerPart> page(int pageNumber) {
return strategy.page(pageNumber);
}
@Override
@Transactional(readOnly = true)
public List<ComputerPart> parts() {
return strategy.parts();
}
@Override
@Transactional(readOnly = true)
public Integer lastPageNumber() {
return strategy.lastPageNumber();
}
@Override
@Transactional(readOnly = true)
public List<ComputerPart> search(String partOfName) {
return strategy.search(partOfName);
}
@Override
public void changeStrategy(StrategyType strategyType) {
this.strategy = StrategyFactory.getStrategy(strategyType, sessionFactory);
}
}
Root context
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:annotation-config/>
<bean class="com.varguss.config.DatabaseConfig"/>
</beans>
Child context
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /resources/views/ directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/views/" p:suffix=".jsp" />
<context:component-scan base-package="com.varguss.dao" />
<context:component-scan base-package="com.varguss.controller" />
</beans:beans>
When using hierarchical application context (a parent and child) the child can see the beans from the parent. So it can detect the EntityManagerFactory
and the PlatformTransactionManager
.
However when using things like AOP that only applies to beans in the same application context as the AOP is defined in. So AOP defined in the parent context only applies to beans in the parent context, not to beans in the child contexts.
So in your case the @EnableTransactionManagement
is in the parent context but in there there aren't any beans with @Transactional
, those are in the child context. So either create an @Configuration
which enables transactions there or use <tx:annotation-driven />
in your XML configuration.