一、本课目标
- 掌握SpringXML方式实现声明式事务处理
- 掌握使用注解实现声明式事务处理
二、为业务层添加声明式事务
2.1配置声明式事务
问题:如何在添加用户的业务流程中控制事务?
分析:
- 可以采用MyBatis控制事务
- 事务应该在业务逻辑层控制
- 硬编码方式,代码繁琐,且破坏分层,代码不易维护
解决方法:
- 可以采用AOP的方式实现
- Spring提供了声明式事务支持
声明式事务关注的核心问题是:对哪些方法,采用声明样的事务策略?
对哪些方法其实就是切入点,采取哪些事务策略其实就是增强。
配置步骤:
1、导入tx和aop命名空间
2、定义事务管理器Bean,为其注入数据源Bean
3、通过<tx:advice>配置事务增强,绑定事务管理器并针对不同方法定义事务规则
4、配置切面,将事务增强与方法切入点结合
配置文件:
<?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:p="http://www.springframework.org/schema/p"
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.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?
useUnicode=true&characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="41312019"></property>
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations">
<list>
<value>classpath:cn/smbms/dao/**/*.xml</value>
</list>
</property>
</bean>
<!-- 配置SqlSessionTemplate -->
<!-- <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
</bean> -->
<!-- <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="cn.smbms.dao.user.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean> -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.smbms.dao"></property>
</bean>
<!-- Service -->
<context:component-scan base-package="cn.smbms.service.user"></context:component-scan>
<!-- 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务增强 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" timeout="1000"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<aop:pointcut expression="execution(* cn.smbms.service..*.*(..))" id="myPoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint"/>
</aop:config>
</beans>
userMapper.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.smbms.dao.user.UserMapper">
<!-- 当数据库中的字段信息与对象的属性不一致时需要通过resultMap来映射 -->
<resultMap type="User" id="userList">
<result property="id" column="id" />
<result property="userCode" column="userCode" />
<result property="userName" column="userName" />
<result property="phone" column="phone" />
<result property="birthday" column="birthday" />
<result property="gender" column="gender" />
<result property="userRole" column="userRole" />
<result property="userRoleName" column="roleName" />
</resultMap>
<!-- 查询用户列表(参数:对象入参) -->
<select id="getUserList" resultMap="userList" parameterType="User">
select u.*,r.roleName from smbms_user u,smbms_role r
where u.userName like CONCAT ('%',#{userName},'%')
and u.userRole = #{userRole} and u.userRole = r.id
</select>
<insert id="add" parameterType="User">
insert into smbms_user (userCode,userName,userPassword,gender,
birthday,phone,address,userRole,createdBy,creationDate)
values (#{userCode},#{userName},#{userPassword},#{gender},
#{birthday},#{phone},#{address},#{userRole},
#{createdBy},#{creationDate})
</insert>
</mapper>
userMapper.java
package cn.smbms.dao.user;
import java.util.List;
import cn.smbms.pojo.User;
public interface UserMapper {
/**
* 查询用户列表(参数:对象入参)
*
* @return
*/
public List<User> getUserList(User user);
public int add(User user);
}
测试类:
@Test
public void testAdd() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
User user = new User();
user.setUserCode("test001");
user.setUserName("测试用户001");
user.setUserPassword("1234567");
Date birthday = null;
try {
birthday = new SimpleDateFormat("yyyy-MM-dd").parse("1984-12-12");
} catch (ParseException e) {
e.printStackTrace();
}
user.setBirthday(birthday);
user.setCreationDate(new Date());
user.setAddress("地址测试");
user.setGender(1);
user.setPhone("13688783697");
user.setUserRole(1);
user.setCreatedBy(1);
user.setCreationDate(new Date());
boolean result = userService.addNewUser(user);
logger.debug("testAdd result : " + result);
}
运行结果:
![](https://img.haomeiwen.com/i8886052/7697d2954331fc83.png)
这样单次的数据库的操作很难看出事务的作用,只有在一个业务逻辑里面同时多次去读取数据库的时候我们才可以发现具体在某一个数据操作发生了错误,具体的事务是怎么来控制的,我们才能够看出具体的事务控制的区别。基于这个考虑,添加一个addUsers()方法。
在service文件中增加方法:
package cn.smbms.service.user;
import java.util.List;
import cn.smbms.pojo.User;
public interface UserService {
public List<User> findUsersWithConditions(User user);
public boolean addNewUser(User user);
public void addUsers(List<User> users);
}
package cn.smbms.service.user;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import cn.smbms.dao.user.UserMapper;
import cn.smbms.pojo.User;
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userMapper")
private UserMapper userMapper;
@Override
public List<User> findUsersWithConditions(User user) {
try {
return userMapper.getUserList(user);
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public boolean addNewUser(User user) {
boolean result = false;
if (userMapper.add(user) == 1) {
result = true;
}
return result;
}
@Override
public void addUsers(List<User> users) {
for (int i = 0; i < users.size(); i++) {
userMapper.add(users.get(i));
throw new RuntimeException("test error");
}
}
}
测试类:
@Test
public void testAdd() {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"applicationContext.xml");
UserService userService = (UserService) ctx.getBean("userService");
User user = new User();
user.setUserCode("test001");
user.setUserName("测试用户001");
user.setUserPassword("1234567");
Date birthday = null;
try {
birthday = new SimpleDateFormat("yyyy-MM-dd").parse("1984-12-12");
} catch (ParseException e) {
e.printStackTrace();
}
user.setBirthday(birthday);
user.setCreationDate(new Date());
user.setAddress("地址测试");
user.setGender(1);
user.setPhone("13688783697");
user.setUserRole(1);
user.setCreatedBy(1);
user.setCreationDate(new Date());
// boolean result = userService.addNewUser(user);
List<User> users = new ArrayList<User>();
users.add(user);
users.add(user);
userService.addUsers(users);
// logger.debug("testAdd result : " + result);
}
此时的结果是数据库中不会添加进去任何数据,因为在添加第一条数据之后,抛出了自定义的异常,所以所有的数据都被回滚了。要么数据全部添加进去,要么全都添加不进去。
事务属性:
![](https://img.haomeiwen.com/i8886052/5e3fb6dee55f65dc.png)
![](https://img.haomeiwen.com/i8886052/7da2d757a313e575.png)
2.2使用注解实现事务处理
![](https://img.haomeiwen.com/i8886052/eb839f0d9db22e50.png)
配置文件:
<?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:p="http://www.springframework.org/schema/p"
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.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<!-- 配置数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/smbms?
useUnicode=true&characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="41312019"></property>
</bean>
<!-- 配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations">
<list>
<value>classpath:cn/smbms/dao/**/*.xml</value>
</list>
</property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.smbms.dao"></property>
</bean>
<!-- Service -->
<context:component-scan base-package="cn.smbms.service.user"></context:component-scan>
<!-- 事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 事务增强 -->
<!-- <tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" timeout="1000"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice> -->
<!-- 配置切面 -->
<!-- <aop:config>
<aop:pointcut expression="execution(* cn.smbms.service..*.*(..))" id="myPoint"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint"/>
</aop:config> -->
</beans>
serviceImpl:
package cn.smbms.service.user;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import cn.smbms.dao.user.UserMapper;
import cn.smbms.pojo.User;
@Transactional(propagation=Propagation.REQUIRED)
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userMapper")
private UserMapper userMapper;
@Override
public List<User> findUsersWithConditions(User user) {
try {
return userMapper.getUserList(user);
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}
}
public UserMapper getUserMapper() {
return userMapper;
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public boolean addNewUser(User user) {
boolean result = false;
if (userMapper.add(user) == 1) {
result = true;
}
return result;
}
@Override
public void addUsers(List<User> users) {
for (int i = 0; i < users.size(); i++) {
userMapper.add(users.get(i));
throw new RuntimeException("test error");
}
}
}
这样同样也可以实现事务管理。
使用注解的时候事务属性如下:
![](https://img.haomeiwen.com/i8886052/ba338bbd1b70b9a0.png)
![](https://img.haomeiwen.com/i8886052/299a41756105c92f.png)
三、小结
![](https://img.haomeiwen.com/i8886052/96394bf35d2f9541.png)
网友评论