在上一篇《使用IntelliJ IDEA开发SpringMVC网站(四)集成MyBatis》文章中我们集成了MyBatis,本文在原来的基础上加入事务管理。
加入事务管理之前,我们先完善一下原来的代码增加更多的数据表的操作,并用Junit进行测试。
修改UserMapper.xml文件,增加对数据insert,update,delete的SQL,如下所示:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<delete id="deleteById" parameterType="java.lang.Integer">
DELETE FROM t_user WHERE user_id = #{userId}
</delete>
<insert id="insert" parameterType="User">
INSERT INTO t_user(user_id,user_name,user_age) VALUES(#{id},#{userName},#{age});
</insert>
<update id="update" parameterType="User">
UPDATE t_user
<set>
<if test="userName != null">
user_name = #{userName},
</if>
<if test="age != null">
user_age = #{age}
</if>
</set>
WHERE user_id = #{id}
</update>
<select id="getAll" resultMap="userMap">
SELECT user_id,user_name,user_age FROM t_user
</select>
parameterType=”User” 这里的User使用了简写,实际上是 parameterType=”com.springmvcdemo.model.User” 为了支持这种写法我们需要在sqlSessionFactory上配置typeAliasesPackage属性,如下所示:
1 | <!-- SqlSessionFactory --> |
修改UserMapper接口:1
2
3
4
5
6
7
8
9
10
11
12
public interface UserMapper {
User findById(@Param("userId") int userId);
int deleteById(int userId);
int insert(User user);
int update(User user);
List<User> getAll();
}
修改UserService:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class UserService {
private UserMapper userMapper;
public User findById(int userId) {
return userMapper.findById(userId);
}
public int deleteById(int userId) {
return userMapper.deleteById(userId);
}
public int insert(User user) {
return userMapper.insert(user);
}
public int update(User user) {
return userMapper.update(user);
}
public List<User> getAll() {
return userMapper.getAll();
}
}
使用Junit进行测试,首先引入依赖的spring-test jar包:1
2
3
4
5
6<!-- spring test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
因为我们使用了Maven,maven目录结构中要测试相关代码存放在test目录下,所以我们在现有的src目录下手动创建 test/java 目录,并设置为 Tests 如下所示:
在src/test/java目录下面新建一个名为TestMyBatis的测试类:
书写测试代码的过程中,报了一个错误,如下所示:
“Could not autowire. No beans of ‘UserService’ type found”,不能自动装配UserService,原来是我们开启Spring自动检测Bean的配置是在spring-mvc.xml文件里面的,如下所示:1
2<!-- Spring Auto scanning components -->
<context:component-scan base-package="com.springmvcdemo" />
而我们这里通过Junit测试,不需要依赖spring mvc 环境,我们只引入了spring.xml和spring-mybatis.xml两个文件1
"file:src/main/webapp/WEB-INF/spring.xml","file:src/main/webapp/WEB-INF/spring-mybatis.xml"}) (locations = {
所以对spring-mvc.xml里面的配置稍作修改:
1 | <context:component-scan base-package="com.springmvcdemo.controller" /> |
base-package 指定为com.springmvcdemo.controller表明只对controller包下面的类进行扫描。
在spring.xml文件里面增加配置:
1 | <context:component-scan base-package="com.springmvcdemo.mapper, com.springmvcdemo.service" /> |
表明对mapper和service包及其子包进行扫描,找到能够自动注册为Spring Bean的类。
修改完成后,我们来编写测试类TestMyBatis:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43// = extends SpringJUnit4ClassRunner (SpringJUnit4ClassRunner.class)
"file:src/main/webapp/WEB-INF/spring.xml","file:src/main/webapp/WEB-INF/spring-mybatis.xml"}) (locations = {
public class TestMyBatis {
private static final Logger logger = Logger.getLogger(TestMyBatis.class);
private UserService userService;
public void test1() {
User user = userService.findById(2);
logger.info("username:" + user.getUserName() + " , userAge=" + user.getAge());
}
public void test2() {
User user = new User();
user.setId(3);
user.setUserName("Toby");
user.setAge("28");
userService.insert(user);
}
public void test3() {
User user = userService.findById(3);
user.setAge("27");
userService.update(user);
}
public void test4() {
List<User> userList = userService.getAll();
for (User user : userList) {
logger.info("username:" + user.getUserName() + " , userAge=" + user.getAge());
}
}
public void test5() {
userService.deleteById(3);
}
}
依次执行 test1()到test5()方法,都可正常执行。
下面我们来对MyBatis加入事务管理。
参考MyBatis-Spring官方文档:http://www.mybatis.org/spring/zh/transactions.html
一个使用 MyBatis-Spring 的主要原因是它允许 MyBatis 参与到 Spring 的事务管理中。而 不是给 MyBatis 创建一个新的特定的事务管理器,MyBatis-Spring 利用了存在于 Spring 中的 DataSourceTransactionManager。
一旦 Spring 的 PlatformTransactionManager 配置好了,你可以在 Spring 中以你通常的做 法来配置事务。@Transactional 注解和 AOP(Aspect-Oriented Program,面向切面编程,译 者注)样式的配置都是支持的。在事务处理期间,一个单独的 SqlSession 对象将会被创建 和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。
- 注:Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现:
(图片摘自Spring实战 第3版 第6章)
一旦事务创建之后,MyBatis-Spring 将会透明的管理事务。在你的 DAO 类中就不需要额 外的代码了。
要 开 启 Spring 的 事 务 处 理 , 在 Spring 的 XML 配 置 文 件 中 简 单 创 建 一 个 DataSourceTransactionManager 对象,在spring-mybatis.xml文件中增加如下配置:1
2
3<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
我们使用声明式事务的方式,只需要在spring上下文中添加一行XML,即可声明事务,我们在spring-mybatis.xml文件中增加如下配置:1
<tx:annotation-driven />
就是这样!如果你期望更多的代码,很抱歉,就这么多了!为了让它更有趣一些,我们可以通过transaction-manager属性(默认值是transactionManager)来指定特定的事务管理器:1
<tx:annotation-driven transaction-manager="txManager" />
否则,它就那么多而已。这一行XML包含了强大的功能,它允许在最有意义的位置声明事务规则:在事务的方法上。
tx:annotation-driven 元素告诉Spring检查上下文中所有Bean并查找使用@Transactional注解的Bean,而不管这个注解时用在类级别上还是方法级别上。
我们修改UserService,增加事务:
1 | @Service |
在类级别上,UserService使用了@Transactional注解,表示所有的方法都支持事务并且是只读的。在方法级别上,insert、delete、update 方法通过注解来表示这个方法所需要的事务上下文。
至此,增加事务管理功能完成。接下来会继续完善框架。
(代码已提交到GitHub:https://github.com/jasonli822/MySpringMvcFrame)