声明式事务管理
Spring 事务管理
1、事务添加到 Service 层
2、两种方式
(1)编程式事务管理:代码实现,不使用
(2)声明式事务管理:实际开发使用
3、声明式事务管理
(1)基于注解方式:实际开发使用
(2)基于 xml 配置文件方式
(3)底层使用 AOP
4、PlatformTransactionManager 接口:代表事务管理器,针对不同的框架提供不同的实现类
基于注解方式
1、配置
(1)xml:创建事务管理器,引入名称空间 tx,开启事务注解
<?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:context="http://www.springframework.org/schema/context"
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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启注解(组件)扫描 -->
<context:component-scan base-package="*"></context:component-scan>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="6000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
<!-- 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
(2)配置类 + 注解
@Configuration
//开启组件扫描
@ComponentScan(basePackages = {"包路径"})
//开启事务
@EnableTransactionManagement
public class ConfigTX {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://localhost:3306:druid");
druidDataSource.setUsername("用户名");
druidDataSource.setPassword("密码");
//其余配置略
return druidDataSource;
}
//创建JdbcTemplate对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到IOC容器中找到dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
//到IOC容器中找到dataSource
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
//注入dataSource
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
2、事务注解:@Transactional
(1)位置:类上 / 方法上
(2)如果把这个注解添加类上,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上,只有这个方法添加事务
@Service
@Transactional
class TestService {
@Autowired
private Dao dao;
public void transfer() {
dao.reduce();
dao.add();
}
}
interface BaseDao {
void add();
void reduce();
}
@Repository
class Dao implements BaseDao {
//注入JdbcTemplate对象
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void add() {
String sql = "update user set account=account+? where id=?";
jdbcTemplate.update(sql, 1000, "1");
}
@Override
public void reduce() {
String sql = "update user set account=account-? where id=?";
jdbcTemplate.update(sql, 1000, "2");
}
}
@Transactional 参数配置
1、Propagation:事务传播行为:当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行
传播属性 | 描述 |
Propagation.REQUIRED | 如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务,这是 Spring 默认的传播行为 |
Propagation.REQUIRES_NEW | 不管是否存在事务,该方法总会为自己发起一个新的事务,如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建 |
Propagation.SUPPORTS | 该方法在某个事务范围内被调用,则方法成为该事务的一部分,如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行 |
Propagation.NOT_SUPPORTED | 声明方法不需要事务,如果方法没有关联到一个事务,容器不会为它开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行 |
Propagation.MANDATORY | 该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务,如果在没有事务的环境下被调用,容器抛出异常 |
Propagation.NEVER | 该方法绝对不能在事务范围内执行,如果在就抛异常,只有该方法没有关联到任何事务,才正常执行 |
Propagation.NESTED | 如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按 REQUIRED 属性执行,它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点,内部事务的回滚不会对外部事务造成影响,它只对 DataSourceTransactionManager 事务管理器起效 |
2、Isolation:事务隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
读未提交:Isolation.READ UNCOMMITTED | 可能出现 | 可能出现 | 可能出现 | 不加锁 |
读已提交:Isolation.READ COMMITTED | 不会出现 | 可能出现 | 可能出现 | 不加锁 |
可重复读:Isolation.REPEATABLE READ | 不会出现 | 不会出现 | 可能出现 | 不加锁 |
可串行化:Isolation.SERIALIZABLE | 不会出现 | 不会出现 | 不会出现 | 不加锁 |
Spring 默认:DEFAULT:使用数据库本身使用的隔离级别,ORACLE(读已提交) MySQL(可重复读) |
3、timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交,则进行回滚
(2)默认值:-1,表示没有超时时间
(3)设置时间以秒单位进行计算
4、readOnly:是否只读
(1)false:默认值,表示可以查询、添加、修改、删除操作
(2)true:只能查询
5、rollbackFor:回滚
(1)设置出现哪些异常进行事务回滚
6、noRollbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚
基于 xml 配置文件方式
1、配置事务管理器
2、配置通知
3、配置切入点、切面
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解(组件)扫描 -->
<context:component-scan base-package="*"></context:component-scan>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="6000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
<!-- 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置通知 -->
<tx:advice id="txAdvice">
<!-- 配置事务参数 -->
<tx:attributes>
<!-- 指定哪种规则的方法添加事务 -->
<tx:method name="方法名" isolation="SERIALIZABLE"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pt" expression="execution(* *..*.*(..))"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战