在接触事务之前,先看一个例子。
在我们的生活中,会有转账的行为,转账可以大致分为如下几个步骤
- 修改自己的账户信息
- 在自己的账户上添加一条转账信息
- 修改对方的账户信息
- 在对方的账户上添加一条转账信息
如果在第二步执行完后,出现了异常,那么自己的钱就没了,这就会导致一些纠纷了,银行肯定不会允许这种事情发生;
为了避免这种事情的发生,我们把这所有的操作放在一个事务中,如果在执行这个事务时,发生异常,导致事务无法正常完成,此时就会触发事务的回滚机制,所有执行了的操作都不能生效,即没有提交数据;只有当所有的操作都执行了,事务正常结束,数据才会提交。
那在Spring中如何处理事务的呢?
Spring声明式事务AOP
在这里使用Spring+MyBatis集成的代码,在这个代码的基础上进行修改
代码的结构如下:
1.创建UserMapper接口
public interface UserMapper {
public void addUser(User user);
public void deleteUser(Integer id);
}
2.创建UserDao接口
public interface UserDao {
public void addUser(User user);
public void deleteUser(Integer id);
}
3.创建UserDao的实现类UserDaoImpl
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private UserMapper userMapper;
@Override
public void addUser(User user) {
userMapper.addUser(user);
}
@Override
public void deleteUser(Integer id) {
userMapper.deleteUser(id);
}
}
4.创建UserService接口
public interface UserService {
public void addUser(User user);
public void deleteUser(Integer id);
}
5.创建UseService的实现类UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public void addUser(User user) {
userDao.addUser(user);
}
@Override
public void deleteUser(Integer id) {
userDao.deleteUser(id);
}
}
6.创建UserAction类
在这个类中设置一个方法saveAndDel();在这个方法中执行两个操作,一个是插入用户的操作,一个是删除用户的操作。并在两个操作之间设置一个会出现异常的代码;
@Controller
public class UserAction {
@Autowired
private UserService userService;
public void saveAndDel(User user, Integer id) {
userService.addUser(user);
int a = 1 / 0;
userService.deleteUser(id);
}
}
7.测试
public class Test {
public static void main(String[] args) {
ApplicationContext app = new
ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserAction bean = app.getBean(UserAction.class);
User user = new User(0,"富贵",12);
bean.saveAndDel(user, 33);
}
}
测试后发现,只执行了插入用户的操作,发生异常后,删除用户操作并没有执行;也就是说这个事务执行未完成,就进行了提交修改了数据库;作为一个事务当然是不行的,那如何能够设置一个事务呢;?
Spring声明式事务有两种方法,一个是使用注解,一个是配置文件
使用事务需要设置xml文件的Namespace的选项;勾选了后,在它的右侧的Namespace Versions中全部选择第一个没有版本号的版本
使用注解声明事务
这里可以用到一个注解@Transactional,这个注解可以注释类,也可以注释方法,注释在方法时,表示这个方法是一个事务,作用在类上,表示每一个方法都是一个事务,
具体步骤如下:
1.修改UserAction,添加注解
@Controller
@Transactional(propagation=Propagation.REQUIRED) // == @Transactional
public class UserAction {
@Autowired
private UserService userService;
public void saveAndDel(User user, Integer id) {
userService.addUser(user);
int a = 1 / 0;
userService.deleteUser(id);
}
}
2. 修改application-action.xml文件
<context:component-scan base-package="com.young.action"></context:component-scan>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动注解事务 -->
<tx:annotation-driven/>
3.测试
public class Test {
public static void main(String[] args) {
ApplicationContext app = new
ClassPathXmlApplicationContext("classpath:applicationContext.xml");
UserAction bean = app.getBean(UserAction.class);
User user = new User(0,"富贵",12);
bean.saveAndDel(user, 33);
}
}
此次测试,遇到异常后,事务发生了回滚,数据库没有被修改
配置xml文件声明事务
1.修改UserAction,删除注解
@Controller
public class UserAction {
@Autowired
private UserService userService;
public void saveAndDel(User user, Integer id) {
userService.addUser(user);
int a = 1 / 0;
userService.deleteUser(id);
}
}
2. 修改application-action.xml文件
<context:component-scan base-package="com.young.action"></context:component-scan>
<!-- 1,声明事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2,声明事务的传播特性 也就是通知 -->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 3进行AOP织入 -->
<aop:config>
<!-- 声明切面 -->
<aop:pointcut expression="execution(* com.young.action.*.*(..))" id="pc"/>
<!-- 织入 -->
<aop:advisor advice-ref="advice" pointcut-ref="pc"/>
</aop:config>
相关属性说明
声明事务切面 告诉spring框架,将哪些方法放入到事务中,以及事务特征
- propagation:
- REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- NESTED: 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类 似的操作
- read-only=“true”,只读事务
如果把ready—only属性的值设置为true,则数据–只能读。
ready—only属性设置为true的事务,如果有更新数据的操作,则会报如下异常
< Connection is read-only. Queries leading to data modification are not allowed>