在本系列文章的《MyBatis与Spring集成示例续》中,介绍了Spring的编程式事务管理。网友 dabing69221提出讲一下MyBatis与Spring整合的声明式事务管理,今天就根据这一知识点,对上文的示例进行改进。
Spring的声明式事务管理是采用AOP(Aspect-Oriented Programming,面向切面编程)实现的。在编程式事务管理中,各事务处理代码实际上是相似的,这就造成了代码重复;而且编程式事务管理会造成事务管理代码和被管理的代码耦合,不符合软件工程中“高内聚、低耦合”的要求。若采用AOP,则可以避免以上缺陷。
Spring AOP需要引入的jar包请参考:http://blog.csdn.net/a352193394/article/details/7588851。
首先写一个DAO类,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
com.abc.dao;
import
com.abc.mapper.StudentMapper;
import
com.abc.domain.Student;
public
class
StudentDao {
private
StudentMapper studentMapper;
//studentMapper的setter和getter方法
public
void
setStudentMapper(StudentMapper studentMapper)
{
this
.studentMapper = studentMapper;
}
public
StudentMapper getStudentMapper()
{
return
this
.studentMapper;
}
public
void
save(Student student)
{
this
.studentMapper.add(student);
int
a =
1
/
0
;
}
}
|
在此类中,有一个save方法用于保存学生,当然也可以添加其他方法。
接下来,我们要在Spring中应用AOP此类的方法提供事务管理,其配置如下(beans.xml):
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
<?
xml
version
=
"1.0"
encoding
=
"utf8"
?>
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop
=
"http://www.springframework.org/schema/aop"
xmlns:tx
=
"http://www.springframework.org/schema/tx"
xmlns:context
=
"http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-autowire
=
"byName"
default-lazy-init
=
"false"
>
<!--本示例采用DBCP连接池,应预先把DBCP的jar包复制到工程的lib目录下。
连接池配置如下-->
<
bean
id
=
"dataSource"
class
=
"org.apache.commons.dbcp.BasicDataSource"
>
<
property
name
=
"driverClassName"
value
=
"com.mysql.jdbc.Driver"
/>
<
property
name
=
"url"
value
=
"jdbc:mysql://localhost/courseman"
/>
<
property
name
=
"username"
value
=
"courseman"
/>
<
property
name
=
"password"
value
=
"abc123"
/>
</
bean
>
<
bean
id
=
"sqlSessionFactory"
class
=
"org.mybatis.spring.SqlSessionFactoryBean"
>
<!--dataSource属性指定要用到的连接池-->
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
<!--configLocation属性指定mybatis的核心配置文件-->
<
property
name
=
"configLocation"
value
=
"resources/configuration.xml"
/>
</
bean
>
<
bean
id
=
"transactionManager"
class
=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
</
bean
>
<
bean
id
=
"parentMapper"
class
=
"org.mybatis.spring.mapper.MapperFactoryBean"
abstract
=
"true"
>
<!--sqlSessionFactory属性指定要用到的SqlSessionFactory实例-->
<
property
name
=
"sqlSessionFactory"
ref
=
"sqlSessionFactory"
/>
</
bean
>
<
bean
id
=
"studentMapper"
parent
=
"parentMapper"
>
<!--mapperInterface属性指定映射器接口,用于实现此接口并生成映射器对象-->
<
property
name
=
"mapperInterface"
value
=
"com.abc.mapper.StudentMapper"
/>
</
bean
>
<!--为studentDao注入studentMapper-->
<
bean
id
=
"studentDao"
class
=
"com.abc.dao.StudentDao"
>
<
property
name
=
"studentMapper"
ref
=
"studentMapper"
/>
</
bean
>
<!--配置事务处理策略,transaction-manager属性指定事务管理器。
若事务管理器bean的id即为transactionManager,则
transaction-manager的属性可以不指定-->
<
tx:advice
id
=
"txAdvice"
transaction-manager
=
"transactionManager"
>
<
tx:attributes
>
<!--所有以find开头的方法都是只读的-->
<
tx:method
name
=
"find*"
read-only
=
"true"
/>
<
tx:method
name
=
"save*"
/>
<!--其他方法使用默认事务策略-->
<
tx:method
name
=
"*"
/>
</
tx:attributes
>
</
tx:advice
>
<!-- AOP配置-->
<
aop:config
>
<!--pointcut元素定义一个切入点,execution中的第一个星号
用以匹配方法的返回类型,这里星号表明匹配所有返回类型。
com.abc.dao.*.*(..)表明匹配com.abc.dao包下的所有类的所有
方法-->
<
aop:pointcut
id
=
"myPointcut"
expression
=
"execution(* com.abc.dao.*.*(..))"
/>
<!--将定义好的事务处理策略应用到上述的切入点-->
<
aop:advisor
advice-ref
=
"txAdvice"
pointcut-ref
=
"myPointcut"
/>
</
aop:config
>
</
beans
>
|
由以上可看出,事务策略的配置主要是<tx:method />标签的配置。其中name属性是必须的,用以指定事务策略作用的目标方法。除read-only属性外,还有propagation和rollback-for等属性,用以指定事务的传播属性和触发事务回滚的异常。这里没有指定rollback-for属性,则Spring默认情况下只在发生了RuntimeException或Error异常时,才回滚事务。
在<aop:pointcut/>配置中,出现了execution表达式。关于此表达式的写法,请参考:http://lavasoft.blog.51cto.com/62575/172292/。
在StudentDao的save方法中,故意添加了一行代码int a = 1 / 0。这行代码会抛出ArithmeticException(这是一个RuntimeException),按照以上的配置,Spring会回滚事务。执行结果如下图所示:
如图中所示,事务已回滚,到数据库查询,学生记录也没有被插入。
本示例源码下载:http://down.51cto.com/data/857171。