Spring Test Transactional @Rollback
来源:原创 发布时间:2015-04-19 归档:spring-test
开发环境 :
JDK 7
Maven 3
Junit 4.11
Spring 4.1.5
MySQL 5.5
Eclipse Luna
pom.xml
<project> <properties> <spring.version>4.1.5.RELEASE</spring.version> <mysql.version>5.1.17</mysql.version> <junit.version>4.11</junit.version> <hamcrest.version>1.3</hamcrest.version> <aspectj.version>1.6.8</aspectj.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>${hamcrest.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </project>beans.xml
<?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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:annotation-config /> <context:component-scan base-package="org.lychie" /> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <property name="driverClassName" value="${jdbc.driverClassName}" /> </bean> <bean class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" rollback-for="Throwable" /> <tx:method name="insert*" propagation="REQUIRED" rollback-for="Throwable" /> <tx:method name="update*" propagation="REQUIRED" rollback-for="Throwable" /> <tx:method name="delete*" propagation="REQUIRED" rollback-for="Throwable" /> <tx:method name="*" read-only="true" /> </tx:attributes> </tx:advice> <aop:config> <aop:advisor pointcut="execution(* org.lychie.dao.*.*(..))" advice-ref="txAdvice" /> </aop:config> </beans>LoggerRule.java
public class LoggerRule implements TestRule { @Override public Statement apply(final Statement base, final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { String method = description.getDisplayName(); System.out.println("---> " + method + " is ready to execute"); base.evaluate(); System.out.println("---> " + method + " has been executed"); } }; } }EmployeeDaoImpl.java
@Repository public class EmployeeDaoImpl implements EmployeeDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public List<Employee> getAll() { String sql = "SELECT * FROM Employee"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class)); } @Override public boolean insert(Employee employee) { String sql = "INSERT INTO Employee(id, name, age, mail) VALUES(?, ?, ?, ?)"; int result = jdbcTemplate.update(sql, null, employee.getName(), employee.getAge(), employee.getMail()); return result > 0; } }EmployeeDaoImplTest.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/beans.xml") @Transactional public class EmployeeDaoImplTest { @Autowired private EmployeeDao employeeDao; @Rule public TestRule loggerRule = new LoggerRule(); @Test @Rollback public void testInsert() { Employee employee = new Employee(); employee.setAge(20); employee.setName("店小四"); employee.setMail("dianxiaosi@yeah.net"); boolean successful = employeeDao.insert(employee); assertThat(successful, is(true)); } }
前面是测试环境的准备, 没有什么特别的。这里才是焦点的开始。
@Rollback 默认值是 true, 如果想设为 false, 可使用 @Rollback(false) 标注。执行单元测试的结果, 如下 :
@Rollback 默认值是 true, 如果想设为 false, 可使用 @Rollback(false) 标注。执行单元测试的结果, 如下 :
---> testInsert(org.lychie.dao.impl.EmployeeDaoImplTest) is ready to execute
四月 20, 2015 12:01:19 上午 org.springframework.test.context.transaction.TransactionContext startTransaction
信息: Began transaction (1) for test context [DefaultTestContext@3f9b4e74 testClass = EmployeeDaoImplTest,
testInstance = org.lychie.dao.impl.EmployeeDaoImplTest@6d3bc24b, testMethod = testInsert@EmployeeDaoImplTest,
testException = [null], mergedContextConfiguration = [MergedContextConfiguration@6e5101f3 testClass =
EmployeeDaoImplTest, locations = '{classpath:/beans.xml}', classes = '{}', contextInitializerClasses = '[]',
activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader =
'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager
[org.springframework.jdbc.datasource.DataSourceTransactionManager@857311a]; rollback [true]
四月 20, 2015 12:01:19 上午 org.springframework.test.context.transaction.TransactionContext endTransaction
信息: Rolled back transaction for test context [DefaultTestContext@3f9b4e74 testClass = EmployeeDaoImplTest,
testInstance = org.lychie.dao.impl.EmployeeDaoImplTest@6d3bc24b, testMethod = testInsert@EmployeeDaoImplTest,
testException = [null], mergedContextConfiguration = [MergedContextConfiguration@6e5101f3 testClass =
EmployeeDaoImplTest, locations = '{classpath:/beans.xml}', classes = '{}', contextInitializerClasses = '[]',
activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader =
'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
---> testInsert(org.lychie.dao.impl.EmployeeDaoImplTest) has been executed
四月 20, 2015 12:01:19 上午 org.springframework.test.context.transaction.TransactionContext startTransaction
信息: Began transaction (1) for test context [DefaultTestContext@3f9b4e74 testClass = EmployeeDaoImplTest,
testInstance = org.lychie.dao.impl.EmployeeDaoImplTest@6d3bc24b, testMethod = testInsert@EmployeeDaoImplTest,
testException = [null], mergedContextConfiguration = [MergedContextConfiguration@6e5101f3 testClass =
EmployeeDaoImplTest, locations = '{classpath:/beans.xml}', classes = '{}', contextInitializerClasses = '[]',
activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader =
'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager
[org.springframework.jdbc.datasource.DataSourceTransactionManager@857311a]; rollback [true]
四月 20, 2015 12:01:19 上午 org.springframework.test.context.transaction.TransactionContext endTransaction
信息: Rolled back transaction for test context [DefaultTestContext@3f9b4e74 testClass = EmployeeDaoImplTest,
testInstance = org.lychie.dao.impl.EmployeeDaoImplTest@6d3bc24b, testMethod = testInsert@EmployeeDaoImplTest,
testException = [null], mergedContextConfiguration = [MergedContextConfiguration@6e5101f3 testClass =
EmployeeDaoImplTest, locations = '{classpath:/beans.xml}', classes = '{}', contextInitializerClasses = '[]',
activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader =
'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
---> testInsert(org.lychie.dao.impl.EmployeeDaoImplTest) has been executed
测试执行结果的信息已经用不同的颜色标识了出来, 仔细阅读, 你可以获得 testInsert 在执行期间的详细信息 :方法开始 --> 事务开始 --> 事务回滚 --> 事务结束 --> 方法结束。
使用 @Rollback 的好处是, 测试数据不会对数据库造成污染, 这一点是很重要的。但 @Rollback 其实也不是真正意义上的数据零污染, 如果数据库表的主键是自增长类型, 虽然发生了事务回滚, 但是主键的索引还是会递增的。
执行这个测试, 数据库是不会插入记录的, 如果把 @Rollback 改成 @Rollback(false), 数据库就会插入一条数据。
@Rollback 需要 @Transactional 的支持 ( 我们知道, @Transactional 默认是会自动提交事务的 ), 如果没有 @Transactional 标注, 则事务就不会受 @Rollback 的控制。
使用 @Rollback 的好处是, 测试数据不会对数据库造成污染, 这一点是很重要的。但 @Rollback 其实也不是真正意义上的数据零污染, 如果数据库表的主键是自增长类型, 虽然发生了事务回滚, 但是主键的索引还是会递增的。
执行这个测试, 数据库是不会插入记录的, 如果把 @Rollback 改成 @Rollback(false), 数据库就会插入一条数据。
@Rollback 需要 @Transactional 的支持 ( 我们知道, @Transactional 默认是会自动提交事务的 ), 如果没有 @Transactional 标注, 则事务就不会受 @Rollback 的控制。
示例代码下载