文章目录
一、编程式事务
1.1 什么是编程式事务
- 编程式事务是指通过手动编写程序来管理事务,即通过编码的方式直接控制事务的提交和回滚
- 在java 中,通常使用事务管理器(如Spring 中的PlatformTransactionManager)来实现编程式事务
1.2 编程式事务的优缺点
- 编程式事务的主要优点是灵活性高,可以按照自己的需求来控制事务的粒度、模式等
- 其缺点是需要编写大量的代码。可读性和可维护性不是很好,代码复用性不高
二、声明式事务
2.1 什么是声明式事务
- 声明式事务是指使用 注解 或 XML 配置的方式来控制事务的提交和回滚,只要告诉哪个方法需要事务即可,这就是声明式,程序员只需要写配置即可
2.2 声明式事务的优点
- 开发者只需配置即可,具体事务的控制实现由第三方框架实现,避免我们直接对事务进行操作
- 声明式事务可以将业务代码和事务逻辑分开来,提高代码的可读性和可维护性
2.3 Spring 事务管理器
- Spring 声明式事务对应的依赖
- spring-tx:包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等 )
- spring-jdbc: 包含DataSource 方式事务管理器实现类(DataSource) DataSourceTransactionManager
- spring-orm:包含其他持久层框架的事务管理器实现类 ,如Hibenate、jpa 等
- 如果持久层使用的是JDBC、JdbcTemplate、MyBatis,那么就用DataSourceTransactionManager,也就是导入spring-jdbc依赖
- 如果持久层使用的Hibenate,那么就用HibernateTransactionManager,也就是导入spring-orm 依赖
2.4 spring 声明式事务使用
- 持久层框架使用JdbcTemplate、事务管理器使用DataSourceTransactionManager
- 导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- jdbcTemplate-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
- jdbc.properties
datasource.url = jdbc:mysql://localhost:3306/数据库名称
datasource.driver = com.mysql.cj.jdbc.Driver
datasource.username = 账号
datasource.password = 密码
- 编写配置类,JavaConfig.java
@Configuration
@ComponentScan(basePackages = {"com.binbin"})
@PropertySource("classpath:jdbc.properties")
//@EnableAspectJAutoProxy
@EnableTransactionManagement
public class JavaConfig {
@Value("${datasource.driver}")
private String driver;
@Value("${datasource.url}")
private String url;
@Value("${datasource.username}")
private String username;
@Value("${datasource.password}")
private String password;
// 1 注入druid 连接池
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
// 2. 注入jdbcTemplate
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
// 3. 事务管理 注入
@Bean
public TransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
- UserService
public interface UserService {
public void updateById(Integer id,String name,Integer age) throws Exception;
}
- UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
@Transactional()
public void updateById(Integer id,String name,Integer age) throws Exception {
userDao.updateById(id, name, age);
}
}
- UserDao
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void updateById(Integer id,String name,Integer age) throws Exception {
String sql = "update users set name = ?,age = ? where id = ?;";
jdbcTemplate.update(sql,name,age,id);// 执行sql 语句
}
}
- 编写测试类
@SpringJUnitConfig(value = JavaConfig.class)
public class SpringTest {
@Autowired
UserService userService;
@Test
public void test() throws Exception {
userService.updateById(1,"李四",20);
}
}
- 也可以直接在类上加@Transactional,方法上的会覆盖类上的注解
- 只读模式设置,若执行非查询语句将会回滚
@Transactional(readOnly = true)
- 事务超时设置:在指定时间内开启事务的方法未执行完会自动回滚
@Transactional(timeout = 5)
- 指定触发回滚的异常,默认IO 异常 不触发
@Transactional(rollbackFor = Exception.class)
- 指定异常不回滚
@Transactional(noRollbackFor = FileNotFoundException.class)
- 指定四种隔离级别
未提交读:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
已提交读:@Transactional(isolation = Isolation.READ_COMMITTED)
可重复读:@Transactional(isolation = Isolation.REPEATABLE_READ)
串行化:@Transactional(isolation = Isolation.SERIALIZABLE)
2.5 事务的传播行为
-
什么是事务的传播行为?
一个方法调用另一个方法时,如何处理当前事务上下文的问题。这些方法通常都有 @Transactional 注解。在 Spring 中,传播行为定义了被调用方法应该如何参与调用者的事务。 -
同一个类的方法互相调用,传播失效,因为传播需要通过代理对象,而同一个类的方法互相调用不走代理。因此只有不同类的方法调用,才会传播事务
-
案例代码
@Service
public class TopServiceImpl implements TopService {
@Autowired
UserService userService;
@Override
@Transactional()
public void topMethod(){
userService.methodA();
userService.methodB();
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
@Transactional(propagation = Propagation.xxx)
public void methodA() {
userDao.updateById(1,"王五",25);
}
@Override
@Transactional(propagation = Propagation.xxx)
public void methodB() {
userDao.updateById(1,"李六",35);
}
}
-
propagation = Propagation.REQUIRED
- 加了这个的方法一定会有事务
- 如果调用方已经有事务,那么该方法就加入事务,如果没有,本方法就自己开启一个事务
- 是默认的传播行为,适用于增删改查
-
propagation = Propagation.REQUIRES_NEW
- 独立事务启动,同时挂起父方法的事务(暂停执行、提交事务)
方法被调用时,无论当前是否存在一个活动的事务,它总是会启动一个新的、独立的事务。这个事务的回滚不会影响其他事务的回滚,其他事务的回滚也不会影响本事务的回滚 - 调用方法和被调用方法不能在同一个类中
- 适用于内部事务和外部事务不存在关联的场景,如日志
- 独立事务启动,同时挂起父方法的事务(暂停执行、提交事务)