Spring 在根上下文中定义的 MVC 事务管理器不在子上下文中定义的 dao 中打开事务
Spring MVC transaction manager defined in root context doesn't open transactions in dao defined in child context
我遇到了一个真正的问题并解决了它,但无法弄清楚发生了什么。
我在根上下文中定义了 transactionManager
和 sessionFactory
bean,在调度程序上下文中使用 @Transactional
方法定义了我的 dao class。就这样。当我试图在 dao 中使用 getCurrentSession()
时,我得到 "could not obtain a current session".
但是,正如我所记得的,调度程序上下文知道根上下文并且可以访问根上下文中的所有 bean。
有人能解释一下,如果 transactionManager
和 sessionFactory
是在根上下文中定义的,而 class 是 [=17= 的,那么为什么不在 @Transactional
方法之前打开事务] 在子上下文中?
数据库配置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);
}
}
根上下文
<?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>
子上下文
<?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>
当使用分层应用程序上下文(父和子)时,子可以看到来自父的 bean。所以它可以检测到 EntityManagerFactory
和 PlatformTransactionManager
。
然而,当使用像 AOP 这样的东西时,它只适用于与定义 AOP 相同的应用程序上下文中的 beans。所以在父上下文中定义的 AOP 只适用于父上下文中的 beans,而不适用于子上下文中的 beans上下文。
因此,在您的情况下,@EnableTransactionManagement
在父上下文中,但其中没有任何带有 @Transactional
的 bean,它们在子上下文中。因此,要么创建一个 @Configuration
以在那里启用交易,要么在您的 XML 配置中使用 <tx:annotation-driven />
。
我遇到了一个真正的问题并解决了它,但无法弄清楚发生了什么。
我在根上下文中定义了 transactionManager
和 sessionFactory
bean,在调度程序上下文中使用 @Transactional
方法定义了我的 dao class。就这样。当我试图在 dao 中使用 getCurrentSession()
时,我得到 "could not obtain a current session".
但是,正如我所记得的,调度程序上下文知道根上下文并且可以访问根上下文中的所有 bean。
有人能解释一下,如果 transactionManager
和 sessionFactory
是在根上下文中定义的,而 class 是 [=17= 的,那么为什么不在 @Transactional
方法之前打开事务] 在子上下文中?
数据库配置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);
}
}
根上下文
<?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>
子上下文
<?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>
当使用分层应用程序上下文(父和子)时,子可以看到来自父的 bean。所以它可以检测到 EntityManagerFactory
和 PlatformTransactionManager
。
然而,当使用像 AOP 这样的东西时,它只适用于与定义 AOP 相同的应用程序上下文中的 beans。所以在父上下文中定义的 AOP 只适用于父上下文中的 beans,而不适用于子上下文中的 beans上下文。
因此,在您的情况下,@EnableTransactionManagement
在父上下文中,但其中没有任何带有 @Transactional
的 bean,它们在子上下文中。因此,要么创建一个 @Configuration
以在那里启用交易,要么在您的 XML 配置中使用 <tx:annotation-driven />
。