在浏览myBatis相关文档的时候,突然想到一个小问题,到目前为止,好像我还没有使用过事务,这个不太应该。
这里我们刚好来测试一下事务叭。
一:添加pom依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.23</version>
</dependency>
二:在启动类上边添加注解开启事务
在启动类加上启动事务注解:@EnableTransactionManagement
@ComponentScan(basePackages = "com")
@SpringBootApplication
// 添加这个注解扫描dao文件
@MapperScan(basePackages = "com.modules.dao")
// 开启定时任务
@EnableScheduling
// 开启mybatis数据库事务
@EnableTransactionManagement
public class EntryApplication
{
public static void main(String[] args)
{
SpringApplication.run(EntryApplication.class, args);
}
}
三:开启事务
开启事务其实很简单,就是在方法上添加注解@Transactional即可。
测试代码如下:
@Transactional // 开启事务
@GetMapping("java/testTrans")
public Map<String, Object> test()
{
String username = "Transactional";
String ip = "0.0.0.0";
Browse browse = new Browse();
browse.setUsername(username.toString());
browse.setArticleTitle("test:Transactional");
browse.setIp(ip);
browse.setIsWeixin((byte) '0');
articleDao.addBrowse(browse);
Map<String, Object> map = new HashMap<>(3);
map.put("code",1);
map.put("msg","数据写入成功!");
return map;
}
如果代码中发生错误,则当前方法中的所有数据库操作都会回滚。运行方法,发现数据库中多了一条数据。
正常是不会发生错误的,我这里手动制造一个小错误,代码如下:
@Transactional // 开启事务
@GetMapping("java/testTrans")
public Map<String, Object> test()
{
String username = "Transactional";
String ip = "0.0.0.0";
Browse browse = new Browse();
browse.setUsername(username.toString());
browse.setArticleTitle("test:Transactional");
browse.setIp(ip);
browse.setIsWeixin((byte) '0');
articleDao.addBrowse(browse);
// 添加一个除0错误
Integer res = 1 / 0;
Map<String, Object> map = new HashMap<>(3);
map.put("code",1);
map.put("msg","数据写入成功!");
return map;
}
因为我配置了全局异常捕获,因此访问上方代码,是直接被捕获返回:
{"msg":"/ by zero","code":-888}
查看数据库,事务生效,没有数据写入。
四:事务失效情况
1:在方法中使用try-catch会导致事务失效
@Transactional // 开启事务
@GetMapping("java/testTrans")
public Map<String, Object> test()
{
Map<String, Object> map = new HashMap<>(3);
try
{
String username = "Transactional";
String ip = "0.0.0.0";
Browse browse = new Browse();
browse.setUsername(username.toString());
browse.setArticleTitle("test:Transactional");
browse.setIp(ip);
browse.setIsWeixin((byte) '0');
articleDao.addBrowse(browse);
// 添加一个除0错误
Integer res = 1 / 0;
map.put("code",1);
map.put("msg","数据写入成功!");
return map;
}
catch(Exception e)
{
map.put("code",-1);
map.put("msg","出错了!");
return map;
}
}
上方的代码,事务就不会生效。
五:手动回滚事务
那这就有一个小问题,我需要使用try-catch来捕获代码中的异常,那事务和try-catch就不能共存了?
当然是不存在这个问题的,我们可以在catch中手动提交回滚。
需要对上边的代码做两个改动:
@Transactional(rollbackFor = Exception.class) // 开启事务
在注解中指定rollback是在Exception中。
在try-catch的catch中添加手动回滚代码:
// 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
全部代码如下所示:
@Transactional(rollbackFor = Exception.class) // 开启事务
@GetMapping("java/testTrans")
public Map<String, Object> test()
{
Map<String, Object> map = new HashMap<>(3);
try
{
String username = "Transactional";
String ip = "0.0.0.0";
Browse browse = new Browse();
browse.setUsername(username.toString());
browse.setArticleTitle("test:Transactional");
browse.setIp(ip);
browse.setIsWeixin((byte) '0');
articleDao.addBrowse(browse);
// 添加一个除0错误
Integer res = 1 / 0;
map.put("code",1);
map.put("msg","数据写入成功!");
return map;
}
catch(Exception e)
{
// 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
map.put("code",-1);
map.put("msg","出错了!");
return map;
}
}
访问代码,发现事务执行成功,数据也回滚成功。
六:@Transactional经常遇到的几个场景:
@Transactional 加于private方法, 无效
@Transactional 加于未加入接口的public方法, 再通过普通接口方法调用, 无效
@Transactional 加于接口方法, 无论下面调用的是private或public方法, 都有效
@Transactional 加于接口方法后, 被本类普通接口方法直接调用, 无效
@Transactional 加于接口方法后, 被本类普通接口方法通过接口调用, 有效
@Transactional 加于接口方法后, 被它类的接口方法调用, 有效
@Transactional 加于接口方法后, 被它类的私有方法调用后, 有效
七:使用DataSourceTransactionManager和TransactionDefinition实现事务
除了使用@Transactional注解来实现事务,使用DataSourceTransactionManager和TransactionDefinition也可以实现事务。
代码如下:
// 这两个必须要注入
@Resource
DataSourceTransactionManager dataSourceTransactionManager;
@Resource
TransactionDefinition transactionDefinition;
//@Transactional(rollbackFor = Exception.class) // 开启事务
@GetMapping("java/testTrans")
public Map<String, Object> test()
{
// 开启事务
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
Map<String, Object> map = new HashMap<>(3);
try
{
String username = "Transactional";
String ip = "0.0.0.0";
Browse browse = new Browse();
browse.setUsername(username.toString());
browse.setArticleTitle("test:Transactional");
browse.setIp(ip);
browse.setIsWeixin((byte) '0');
articleDao.addBrowse(browse);
// 添加一个除0错误
//Integer res = 1 / 0;
//提交事务
dataSourceTransactionManager.commit(transactionStatus);
map.put("code",1);
map.put("msg","数据写入成功!");
return map;
}
catch(Exception e)
{
// 回滚事务
dataSourceTransactionManager.rollback(transactionStatus);
// 手动回滚
//TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
map.put("code",-1);
map.put("msg","出错了!");
return map;
}
}
这个不太推荐使用,如果没有特殊要求,还是推荐使用@Transactional注解来实现事务。
以上大概就是Springboot实现数据库事务的两种方式。
有好的建议,请在下方输入你的评论。