Spring基础事务
Posted 烟锁迷城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring基础事务相关的知识,希望对你有一定的参考价值。
1、事务
事务是仅对数据库的一种操作定义,一个或多个增加,删除,修改语句都可以算作一个事务。事务具有四种基本特性,原子性,一致性,隔离性,持久性。
在spring中,事务是一种重要的机制,当一个请求中包含多个数据的修改操作时,就需要保证全部成功或全部失败,否则就会导致数据不一致。在不做管理的情况下,每一个修改数据库数据的SQL语句执行完成后都会自动提交,这让每一个语句都是独立的,想要将多个语句的成功与失败达成一致,就需要在所有的sql执行完成后统一提交。
在spring中,事务使用代理的方式实现,将需要的方法代理起来,然后在执行之前取消自动提交,执行之后进行统一提交,如果有错误就会导致整个方法回滚,这样就达成了所需要的事务一致性。
2、使用自定义注解实现事务
本案例使用自定义注解,AOP切面和JDBC数据库连接,实现事务。
数据库连接工具,建立数据库连接。
public class DBUtil {
private final static String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
private final static String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf-8";
private final static String USERNAME = "root";
private final static String PASSWORD = "root";
public static Connection getConnection() {
Connection connection = null;
try {
Class.forName(DRIVER_NAME);
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
}
使用自定义注解,设定四种切点。
事前切点,开启JDBC手动提交。
事后切点,手动提交事务。
抛错切点,事务回滚。
最终切点,事务关闭。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTransaction {
}
@Aspect
@Component
public class MyAspectJ {
@Before("@annotation(MyTransaction)")
public void transactionBefore() throws SQLException {
Connection connection = DBUtil.getConnection();
connection.setAutoCommit(false);
}
@AfterReturning("@annotation(MyTransaction)")
public void transactionAfterReturn() throws SQLException {
Connection connection = DBUtil.getConnection();
connection.commit();
}
@AfterThrowing("@annotation(MyTransaction)")
public void transactionAfterThrow() throws SQLException {
Connection connection = DBUtil.getConnection();
connection.rollback();
}
@After("@annotation(MyTransaction)")
public void transactionAfter() throws SQLException {
Connection connection = DBUtil.getConnection();
connection.close();
}
}
Dao层代码,插入数据方法。
public interface IUserDao {
void addUser(User user) throws SQLException;
}
@Repository
public class UserDaoImpl implements IUserDao {
public void addUser(User user) throws SQLException {
PreparedStatement preparedStatement = null;
String sql = "insert into user (name,age) values (?,?)";
preparedStatement = DBUtil.getConnection().prepareStatement(sql);
preparedStatement.setString(1, user.getName());
preparedStatement.setInt(2, user.getAge());
preparedStatement.executeUpdate();
}
}
service层,建立两个数据插入
public interface IUserService {
void business(User user1, User user2) throws SQLException;
}
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@MyTransaction
public void business(User user1, User user2) throws SQLException {
userDao.addUser(user1);
userDao.addUser(user2);
}
}
javaconfig,执行插入指令,检验注解切面是否生效。
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"com.lichong.service", "com.lichong.dao","com.lichong.aspect"})
public class JavaConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
IUserService bean = context.getBean(IUserService.class);
User user1 = new User();
user1.setName("lili");
user1.setAge(18);
User user2 = new User();
user2.setAge(20);
user2.setName("lilie");
try {
bean.business(user1, user2);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3、事务的传播行为
事务行为 | 说明 |
propagation_required | 支持当前事务,假设当前没有事务,就新建一个事务 |
propagation_supports | 支持当前事务,假设当前没有事务,就以非事务方式运行 |
propagation_mandatory | 支持当前事务,假设当前没有事务,就抛出异常 |
propagation_requires_new | 新建事务,假设当前存在事务,则把当前事务挂起 |
propagation_not_supported | 以非事务方式运行操作,假设当前存在事务,就把当前事务挂起 |
propagation_never | 以非事务方式运行操作,假设当前存在事务,就抛出异常 |
propagation_nested | 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与propagation_required一致的操作 |
- propagation_required:最常见的行为,事务A和事务B一起成功一起失败。
- propagation_supports:如果有两个事务,A和B将保持一致。如果没有父事务,那么下一个操作将没有事务
- propagation_mandatory:如果有两个事务,A和B将保持一致。如果没有父事务,那么下一个操作将抛出异常
- propagation_requires_new:如果有两个事务,A和B将互不影响,各自抛错和提交
- propagation_not_supported:非事务执行
- propagation_never:非事务执行,有事务则抛错
- propagation_nested:如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行与propagation_required一致的操作
4、事务的隔离级别
事务隔离级别是指在不同的事务访问同一批数据时的隔离程度。如果不采用数据隔离措施,会出现一些问题。
- 脏读:当事务A读取到事务B未提交的数据,就会发生脏读。假设B修改了某数据,还未提交,A此时读取到的数据就是错误的,这就是脏读。
- 幻读:当事务A批量修改某表数据时,事务B插入了一条新数据并提交,导致事务A未修改这条数据,就像发生了幻觉。
- 不可重复读:在一个事务中执行两次查询,查询间隔中事务B插入了一条数据并提交,导致两次结果不相同,这就是不可重复读。
为了解决这些问题,数据库提供了一些隔离级别
隔离级别 | 描述 |
default | 使用数据库本身使用的隔离级别 ORACLE:读已提交 MYSQL:可重复度 |
read_uncommited | 读未提交:避免不了任何风险 |
read_commitd | 读已提交:有幻读和不可重复读风险 |
repeatable_read | 可重复度:解决不可重复读的问题,有幻读风险 |
serlalizable | 串行化:解决幻读风险,保证所有事物串行执行。 |
以上是关于Spring基础事务的主要内容,如果未能解决你的问题,请参考以下文章