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 的控制。
示例代码下载