SSM整合之事务管理(一)
1.核心准备工作
1.1 导入spring-tx依赖
<!-- 事务spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.22</version>
</dependency>
1.2 创建实体类
package com.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import tk.mybatis.mapper.annotation.KeySql;
import javax.persistence.Id;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
@Data
//属性必须是引用数据类型 数据库Null值 动态sql都是判断属性是否为空
public class Emp{
@Id
//导入的是javax.persistence下的
@KeySql(useGeneratedKeys = true)//主键回填
private Integer empno;
private String ename;
private String mgr;
private String job;
private String hiredate;
private Double sal;
private Double comm;
private Integer deptno;
}
1.3 创建spring-tx.xml
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--创建事务管理器 通过Jdbc方式-->
<bean id="tm" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启注解事务 选tx结尾的 报错后就回滚-->
<tx:annotation-driven transaction-manager="tm"></tx:annotation-driven>
</beans>
2 创建通用业务 BaseService(给实现子类提供方法)
package com.service;
import java.io.Serializable;
import java.util.List;
public interface BaseService<T> {
//mapper是单表的增删改查,impl业务实现层
List<T> findAll();
List<T> findAll(T t);
//实现序列化的类都可以传 number类 String
T findById(Serializable id);
T findById(T t);
boolean insert(T t);
boolean delete(Serializable id);
boolean update(T t);
}
3 创建通用业务实现子类BaseServiceImpl
package com.service.impl;
import com.service.BaseService;
import org.springframework.beans.factory.annotation.Autowired;
import tk.mybatis.mapper.common.Mapper;
import java.io.Serializable;
import java.util.List;
//该类仅为子类提供公共的增删改查业务方法
public abstract class BaseServiceImpl<T> implements BaseService<T> {
//浏览器-->控制器->业务层-->数据访问层(调用关系)
//必须要有mapper接口,无就会注入失败
@Autowired
Mapper<T> mapper;
@Override
public List<T> findAll() {
return mapper.selectAll();
}
@Override
public List<T> findAll(T t) {
return mapper.select(t);
}
@Override
public T findById(Serializable id) {
return mapper.selectByPrimaryKey(id);
}
@Override
public T findById(T t) {
//查询单个对象
return mapper.selectOne(t);
}
@Override
public boolean insert(T t) {
//受影响行数
return mapper.insertSelective(t)>0;
}
@Override
public boolean delete(Serializable id) {
return mapper.deleteByPrimaryKey(id)>0;
}
@Override
public boolean update(T t) {
return mapper.updateByPrimaryKeySelective(t)>0;
}
}
4 写员工业务接口EmpService
package com.service;
import com.entity.Emp;
//接口继承借口,类继承类
public interface EmpService extends BaseService<Emp>{
//属于员工转账业务
void transfer(int account1, int account2, int money);
}
5 创建员工业务的实现类EmpServiceImpl
package com.service.impl;
import com.entity.Emp;
import com.service.BaseService;
import com.service.EmpService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
@Service
public class EmpServiceImpl extends BaseServiceImpl<Emp> implements EmpService {
}
6 事务管理
6.1 理由
因为mysql数据库默认是自动提交事务的,程序出现错误并不会自动回滚
而在一个事务中,当程序发生错误的时候,我们希望回滚到最初的状态,
所以就需要进行事务管理
6.2 如何用
6.2.1 逻辑错误
a 出现原因
当你查询不到任何结果时,返回的是null,对一个为null值的对象调用方法,必然会出现空指针异常
b 错误示例代码
b.1 转账方法
@Override
public void transfer(int account1, int account2, int money) {
Emp emp1 = findById(account1);
Emp emp2 = findById(account2);
//转账
emp1.setSal(emp1.getSal()-money);
emp2.setSal(emp2.getSal()+money);
//修改数据库
mapper.updateByPrimaryKeySelective(emp1);
mapper.updateByPrimaryKeySelective(emp2);
}
b.2 测试代码
@Test
public void t99(){
//需要保证一致性
es.zz(99980,7499,100);
}
b.3 运行截图
b.4 解决方案(执行后面逻辑前加判断)
if(emp1==null) throw new RuntimeException(account1+"账号不存在");
if(emp2==null) throw new RuntimeException(account2+"账号不存在");
6.2.2 运行时错误
a 解决方案
在类上面加上@Transactional注解
b 示例代码
b.1 转账方法
@Override
public void transfer(int account1, int account2, int money) {
Emp emp1 = findById(account1);
Emp emp2 = findById(account2);
if(emp1==null) throw new RuntimeException(account1+"账号不存在");
if(emp2==null) throw new RuntimeException(account2+"账号不存在");
//转账
emp1.setSal(emp1.getSal()-money);
emp2.setSal(emp2.getSal()+money);
//修改数据库
mapper.updateByPrimaryKeySelective(emp1);
System.out.println(1/0);
mapper.updateByPrimaryKeySelective(emp2);
}
b.2 测试方法
@Test
public void t(){
es.transfer(7369,7499,100);
}