使用场景
在实际开发中,其实很少会用到事务,一般情况下事务用的比较多的是在金钱计算方面,或购买下单扣库存的过程(过程中一个购买操作包含多个执行过程:查询库存、下单、更新库存,实际操作时,由于高并发存在,可能到下单结束时,更新库存出错,那本次购买操作就是失败的,其下单结果应该被回滚)。如果在一些对一致性要求不高的情况下,可以通过最终一致来解决这个问题。
实现起来很简单
MyBatis与Spring集成后,其事务该怎么做?其实很简单,直接在上一节代码的基础上增加Service层,并在Service类上增加@Transactional
(通常是在Service层注解)注解即可。
注意事项
默认会情况是抛出RuntimeException
运行时异常时,才会回滚事务。可以通过修改 @Transactional(rollbackFor = Exception.class)
来改变默认行为。
Service
package com.gemantic.simulate.service;
import com.gemantic.simulate.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* Created by Yezhiwei on 17/10/31.
*/
@Transactional(rollbackFor = Exception.class)
@Service("us") // 给service取个名字,因为项目中的其他测试中已经存在
public class UserService {
@Autowired
private UserDao userDao;
public boolean addUser() throws Exception {
int result = userDao.insertUser("Yezhwi4", "123", "12345678901");
// 故意抛出异常,进行事务测试
if (1 == result) {
System.out.println("result 1");
// throw new RuntimeException();
throw new Exception();
}
return true;
}
}
Test
package com.gemantic.simulate.service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
/**
* Created by Yezhiwei on 17/10/31.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class UserServiceTest {
@Resource(name = "us")
private UserService userService;
@Test
public void testAddUser() throws Exception {
userService.addUser();
}
}
通过几次测试,查询数据库并没有产生脏数据。
实践
- 如果UserService中有读数据的方法,可以把事务修改为只读
@Transactional(readOnly = true)
增加到相应的方法上。 - 如果方法抛出的异常类型为
checked
异常,可以通过try-catch
块处理,在catch中抛出unchecked异常,实现回滚。 - 建议在具体的类(或类的方法)上使用
@Transactional
注解,而不要使用在类所要实现的任何接口上。 - 查了很多资料,为了添加事务,很多人会在
MyBatisConfig
这个类中增加@EnableTransactionManagement
注解,但是实际测试中不加这句@Transactional
注解事务依然有用。 - 在
MyBatisConfig
类中添加了获取事务管理器的方法,添加下面代码段的作用是在使用@Transactional
注解的地方使用方法中的事务管理器进行事务管理。
/**
* 配置事务管理器
* @param dataSource
* @return
* @throws Exception
*/
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}