项目中的读写、修改、删除数据的操作如果发生异常,常常会导致已写入、修改、删除的数据无法回退到最开始的情况。这种情况下,我们就要依赖Spring的事务机制来实现回退操作。
下面在ibatis项目的项目上简单讲一下如何使用事务:
1、新建Account类
import java.io.Serializable;
@Data
@ToString
public class Account implements Serializable {
private Integer id;//编号
private String username;//用户名
private float balance;//余额
}
2、新建AccountMapper接口
import com.batis.model.Account;
import org.springframework.stereotype.Repository;
@Repository
public interface AccountMapper {
Account findOne(Integer id);
void update(Account account);
}
3、在resources目录下的mapping目录新建AccountMapping.xml,实现AccountMapper接口中的sql
<?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="com.batis.mapper.AccountMapper">
<!-- 开启二级缓存 -->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"></cache>
<resultMap id="BaseResultMap" type="com.batis.model.Account">
<result column="id" jdbcType="INTEGER" property="id" />
<result column="username" jdbcType="VARCHAR" property="username" />
<result column="balance" jdbcType="FLOAT" property="balance" />
</resultMap>
<sql id="baseSql">
select id, username, balance from t_account
</sql>
<select id="findOne" resultMap="BaseResultMap" parameterType="java.lang.Integer">
<include refid="baseSql"/> where id = #{id}
</select>
<update id="update" parameterType="com.batis.model.Account">
UPDATE t_account SET username = #{username}, balance = #{balance}
WHERE id = #{id}
</update>
</mapper>
4、新建AccountService接口类和AccountServiceImpl实现类
AccountService接口类:
public interface AccountService {
/**
* 转账
* @param fromAccountId
* @param toAccountId
* @param money
*/
public void transferAccounts(int fromAccountId, int toAccountId, float money);
}
AccountServiceImpl实现类:
import com.batis.mapper.AccountMapper;
import com.batis.model.Account;
import com.batis.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Transactional(rollbackFor = Exception.class)
@Override
public void transferAccounts(int fromAccountId, int toAccountId, float money) {
Account fromAccount = accountMapper.findOne(fromAccountId);
fromAccount.setBalance(fromAccount.getBalance() - money);
accountMapper.update(fromAccount);
int a = 2 / 0;//报错处
Account toAccount = accountMapper.findOne(toAccountId);
toAccount.setBalance(toAccount.getBalance() + money);
accountMapper.update(toAccount);
}
}
@Transactional注解的属性:
value:可选的限定描述符,指定使用的事务管理器
propagation:可选的事务传播行为设置
isolation:可选的事务隔离级别设置
timeout:事务超时时间设置,默认值为-1表示永不超时
readOnly:读写或只读事务,默认读写,设置为true表示只读,false表示可读写,默认为false
rollbackFor:导致事务回滚的异常类数组
rollbackForClassName:导致事务回滚的异常类名字数组
noRollbackFor:不会导致事务回滚的异常类数组
noRollbackForClassName:不会导致事务回滚的异常类名字数组
5、新建AccountController控制器类
import com.batis.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/account")
public class AccountController {
@Autowired
private AccountService accountService;
@RequestMapping(value = "/transfer", method = RequestMethod.GET)
public String transferAccounts() {
try {
//1号zhangsan 给2号lisi 转账200元
accountService.transferAccounts(1, 2, 200);
return "ok";
} catch (Exception e) {
return "no";
}
}
}
6、在mysql的test库新建t_account表,并插入2条数据
CREATE TABLE `t_account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL COMMENT '用户名',
`balance` float(10,2) DEFAULT NULL COMMENT '余额',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `test`.`t_account`(`id`, `username`, `balance`) VALUES (1, 'zhangsan', 1000.00);
INSERT INTO `test`.`t_account`(`id`, `username`, `balance`) VALUES (2, 'lisi', 1000.00);
7、启动ibatis项目进行测试,在浏览器地址栏输入http://localhost:8080/account/transfer
1)先注释掉AccountServiceImpl实现类中的@Transactional,然后可以看到数据库zhangsan转出了200,但是lisi并没有增加金额,说明报错后数据没有回滚
2)去掉AccountServiceImpl实现类中注解@Transactional的注释,恢复张三的余额为1000,重新在浏览器输入http://localhost:8080/account/transfer进行测试,重新查看数据库中的结果
可以看到,结果张三和李四的金额都是1000,说明在执行转账的过程中,发生异常的情况下数据回滚了
至此Springboot集成Mybatis事务的讲解已经完成,测试也符合预期!
有可以改进的地方希望诸位同学不要吝惜笔墨,加以指正,万分感谢!