Spring MVC 应用程序使用 SpringDataJPA 和hibernate。生产模式利用 MySQL 但是对于单元测试我已经设置了H2DB 来运行,两者都使用单独的java配置。该应用程序还使用了Spring Security,但我在下面省略了它的配置。
应用程序以单元测试启动,但在第一次测试时(testOIC()见下面的代码)我得到了"TransactionRequiredException: no transaction is in progress"
。
testException = org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress, mergedContextConfiguration = [WebMergedContextConfiguration@1c581a0 testClass = OpenPositionsServiceTest, locations = '{}', classes = '{class our.dcollect.configuration.AppConfiguration, class our.dcollect.configuration.HibernateConfigurationTest}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]].
我使用Propagation运行的单元测试。 REQUIRES_NEW ,因此它应该有一个事务上下文。我担心在应用环境中搞砸了一些东西。我发现的与此主题相关的帖子没有多大帮助。
ApplicationContext包含以下bean: ApplicationContext中的Spring Bean定义名称(applicationContext.getBeanDefinitionNames()):
===================================================
Bean: appConfiguration
Bean: authenticationManagerBuilder
Bean: autowiredWebSecurityConfigurersIgnoreParents
Bean: beanNameHandlerMapping
Bean: dataSource
Bean: dataSourceTest
Bean: defaultServletHandlerMapping
Bean: delegatingApplicationListener
Bean: dtoConverter
Bean: emBeanDefinitionRegistrarPostProcessor
Bean: enableGlobalAuthenticationAutowiredConfigurer
Bean: entityManagerFactory
Bean: entityManagerFactoryTest
Bean: fileUploadController
Bean: fileUploadService
Bean: getInternalResourceViewResolverJsp
Bean: handlerExceptionResolver
Bean: hibernateConfiguration
Bean: hibernateConfigurationTest
Bean: hibernateProperties
Bean: httpRequestHandlerAdapter
Bean: initializeAuthenticationProviderBeanManagerConfigurer
Bean: initializeUserDetailsBeanManagerConfigurer
Bean: jpaContext
Bean: jpaMappingContext
Bean: jpaVendorAdapter
Bean: jpaVendorAdapterTest
Bean: multipartResolver
Bean: mvcContentNegotiationManager
Bean: mvcConversionService
Bean: mvcPathMatcher
Bean: mvcResourceUrlProvider
Bean: mvcUriComponentsContributor
Bean: mvcUrlPathHelper
Bean: mvcValidator
Bean: mvcViewResolver
Bean: objectPostProcessor
Bean: openIn....Repository
Bean: openPositionsService
Bean: org.springframework.aop.config.internalAutoProxyCreator
Bean: org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
Bean: org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
Bean: org.springframework.context.annotation.internalAutowiredAnnotationProcessor
Bean: org.springframework.context.annotation.internalCommonAnnotationProcessor
Bean: org.springframework.context.annotation.internalConfigurationAnnotationProcessor
Bean: org.springframework.context.annotation.internalPersistenceAnnotationProcessor
Bean: org.springframework.context.annotation.internalRequiredAnnotationProcessor
Bean: org.springframework.context.event.internalEventListenerFactory
Bean: org.springframework.context.event.internalEventListenerProcessor
Bean: org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension#0
Bean: org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport_Predictor
Bean: org.springframework.orm.jpa.SharedEntityManagerCreator#0
Bean: org.springframework.orm.jpa.SharedEntityManagerCreator#1
Bean: org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration
Bean: org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration
Bean: org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration
Bean: org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration
Bean: org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
Bean: org.springframework.transaction.config.internalTransactionAdvisor
Bean: org.springframework.transaction.config.internalTransactionalEventListenerFactory
Bean: org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration
Bean: privilegeEvaluator
Bean: push....Repository
Bean: requestDataValueProcessor
Bean: requestMappingHandlerAdapter
Bean: requestMappingHandlerMapping
Bean: resourceHandlerMapping
Bean: securityConfiguration
Bean: sessionFactory
Bean: sessionFactoryTest
Bean: simpleControllerHandlerAdapter
Bean: springSecurityFilterChain
Bean: transactionAttributeSource
Bean: transactionInterceptor
Bean: transactionManager
Bean: transactionManagerTest
Bean: viewControllerHandlerMapping
Bean: webSecurityExpressionHandler
===================================================
你可以帮忙吗?我在下面发布的相关代码部分。
MVC配置:
package our.dcollect.configuration;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "our.dcollect")
public class AppConfiguration {
private static final Logger log = Logger.getLogger(AppConfiguration.class);
@Bean
public InternalResourceViewResolver getInternalResourceViewResolverJsp(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/view/");
viewResolver.setSuffix(".jsp");
viewResolver.setOrder(0);
log.info("#### Internal view resolver 0 called...");
return viewResolver;
}
@Bean
public StandardServletMultipartResolver multipartResolver(){
log.info("#### Multipart resolver called...");
return new StandardServletMultipartResolver();
}
}
休眠配置:
package our.dcollect.configuration;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@ComponentScan(basePackages = "our.dcollect")
@EnableJpaRepositories(basePackages = {"our.dcollect.repository"},
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager")
@PropertySource(value = { "classpath:application.properties" })
@EnableTransactionManagement
//@PropertySource({ "/resources/hibernate.properties" })
public class HibernateConfiguration {
@Autowired
private Environment environment;
@Bean(name="sessionFactory")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "our.dcollect" });
sessionFactory.setHibernateProperties(this.hibernateProperties());
return sessionFactory;
}
@Bean(name="dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
@Bean(name="hibernateProperties")
public Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
}
@Bean(name="transactionManager")
@Autowired
public HibernateTransactionManager transactionManager(@Qualifier("sessionFactory") SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
@Bean(name="jpaVendorAdapter")
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.MYSQL);
return hibernateJpaVendorAdapter;
}
@Bean(name="entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSource());
lef.setJpaProperties(this.hibernateProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
lef.setPackagesToScan(new String[] { "our.dcollect.model"});
return lef;
}
}
单元测试的休眠配置:
package our.dcollect.configuration;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/***************************************************************************************
* The same as the real HibernateConfiguration but this works with H2 DB instead of MySQL.
* In addition, the properties are not read from a property file.
****************************************************************************************/
@Configuration
@ComponentScan(basePackages = "our.dcollect")
@EnableJpaRepositories(basePackages = {"our.dcollect.repository"},
entityManagerFactoryRef = "entityManagerFactoryTest",
transactionManagerRef = "transactionManagerTest")
@PropertySource(value = { "classpath:application.properties" })
@EnableTransactionManagement
public class HibernateConfigurationTest {
@Bean(name = "sessionFactoryTest")
public LocalSessionFactoryBean sessionFactoryTest() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSourceTest());
sessionFactory.setPackagesToScan(new String[] { "our.dcollect" });
sessionFactory.setHibernateProperties(hibernatePropertiesTest());
return sessionFactory;
}
@Bean(name = "dataSourceTest")
public DataSource dataSourceTest() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("...");
dataSource.setPassword("...");
return dataSource;
}
private Properties hibernatePropertiesTest() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
}
@Bean(name = "transactionManagerTest")
@Autowired
public HibernateTransactionManager transactionManagerTest(@Qualifier("sessionFactoryTest") SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
@Bean(name = "jpaVendorAdapterTest")
public JpaVendorAdapter jpaVendorAdapterTest() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.H2);
return hibernateJpaVendorAdapter;
}
@Bean(name = "entityManagerFactoryTest")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryTest() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSourceTest());
lef.setJpaProperties(this.hibernatePropertiesTest());
lef.setJpaVendorAdapter(this.jpaVendorAdapterTest());
lef.setPackagesToScan(new String[] { "our.dcollect.model"});
return lef;
}
}
单元测试课程具有易读性:
package our.dcollect.service;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import our.dcollect.configuration.AppConfiguration;
import our.dcollect.configuration.HibernateConfigurationTest;
import our.dcollect.model.OpenIC;
import our.dcollect.repository.OpenICRepository;
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {AppConfiguration.class, HibernateConfigurationTest.class})
public class OpenPositionsServiceTest {
@Autowired
private OpenICRepository OpenICDAO;
@PersistenceContext(unitName = "entityManagerFactoryTest")
private EntityManager entityManager;
@Test
@Transactional(value="transactionManagerTest", propagation = Propagation.REQUIRES_NEW)
public void testOIC() {
System.out.println("getOpenPositionsNotRegisteredInPushBanch");
OpenIC oic = new OpenIC();
oic.setBaId("111");
oic.setBezeichnung("abc");
// clear the persistence context so we don't return the previously cached location object
// this is a test only thing and normally doesn't need to be done in prod code
entityManager.clear();
OpenICDAO.saveAndFlush(oic);
List<OpenIC> list = OpenICDAO.findAll();
assertEquals(list.size(), 1);
OpenIC oicReadBack = list.get(0);
OpenICDAO.delete(oicReadBack.getOpenICId());
}
[...]
}
迈向解决方案的步骤:
Funtik的帖子帮了很多忙。我执行了以下设置:
为HibernateConfiguration的HibernateConfigurationTest和@Profile(&#34;!test&#34;)介绍@Profile(&#34; test&#34;)。在测试中 我把@ActiveProfiles(&#34; test&#34;)放在上面。这分开了豆子; 在测试过程中只装了一种味道:中的那种 HibernateConfigurationTest。然而这并没有解决 问题也是。
然后我在一个单独的分支中完全删除了一个配置文件,以查看数据库的两个配置是否会导致问题。我
与以前一样,在DB配置中遇到了同样的问题。
&#34; [...] 4671 [main] WARN org.springframework.test.context.TestContextManager - 在允许TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener@45c9b3]处理时捕获异常#39;后&#39;执行测试:方法[public void our.dcollect.service.OpenPositionsServiceTest.testGetOpenPositionsNotRegisteredInPushBanch()],instance [our.dcollect.service.OpenPositionsServiceTest@dc7b7d],exception [org.springframework.dao.InvalidDataAccessApiUsageException:没有事务正在进行中;嵌套异常是javax.persistence.TransactionRequiredException:没有正在进行的事务] org.springframework.transaction.UnexpectedRollbackException:事务已回滚,因为它已被标记为仅回滚[...]&#34;
Tests in error:
testOIC(our.dcollect.service.OpenPositionsServiceTest): no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
testOIC(our.dcollect.service.OpenPositionsServiceTest): Transaction rolled back because it has been marked as rollback-only
Tests run: 3, Failures: 0, Errors: 2, Skipped: 0
但是,我只有2种方法,而不是3种,两次报告testOIC。
解决方案:
transactionManager必须是 PlatformTransactionManager ,托管 JpaTransactionManager 。这解决了这个问题。感谢您的所有评论。我发现@Profile
注释非常有用,并且在应用程序上下文中列出了bean的想法。拥有正确的transactionManager后,注释DependsOn()
引起了循环引用,因此我删除了它;在这种情况下,@TestExecutionListeners
注释也不是必需的。我采用了一个简单的演示应用程序,我将其更改为我的配置并尝试重现原始故障。在能够重现之后,我检查了好坏状态之间的差异,这有助于找到差异。 (对于需要SessionFactoryTest对象的transactionManagerTest,也需要做同样的事情。)
// @Bean(name="transactionManager")
// @Autowired
// public HibernateTransactionManager transactionManager(@Qualifier("sessionFactory") SessionFactory s) {
// HibernateTransactionManager txManager = new HibernateTransactionManager();
// txManager.setSessionFactory(s);
// return txManager;
// }
@Bean(name="transactionManager")
@Autowired
public PlatformTransactionManager transactionManager(@Qualifier("sessionFactory") SessionFactory s) {
return new JpaTransactionManager( entityManagerFactory().getObject() );
}
答案 0 :(得分:1)
我遇到了同样的问题。根据我的经验,似乎在事务管理器bean之前加载了entityManager bean,因此spring根本不会检测到任何事务的存在。
@DependsOn
注释为我解决了这个问题。尝试在TransactionManager上设置显式依赖关系,如下所示:
@DependsOn("transactionManagerTest")
@Bean(name = "entityManagerFactoryTest")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryTest() {
答案 1 :(得分:0)
我将解决方案添加到底部的原始帖子中。