《榮譽勳章》《榮譽勳章:戰士》線上服務將於2023年關閉
spring 事務
介紹
事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗所有操作都失敗
事務四個特性(ACID)
(1)原子性
(2)一致性
(3)隔離性
(4)永續性
事務的四大特性詳解 https://blog.csdn.net/weixin_45730866/article/details/123654710
搭建事務操作環境
建立資料庫表,新增記錄,各有1000
引入 jar 包
建立 service,搭建 dao,完成物件建立和注入關係
dao介面
public interface UserDao { /** * 少錢 */ void reduceMoney(); /** * 多錢 */ void addMoney(); }
dao介面實現類
@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; /** * lucy 轉賬 100 給 mary * 少錢 */ @Override public void reduceMoney() { String sql = "update account set money = money - ? where user_name = ?"; jdbcTemplate.update(sql, 100, "lucy"); } /** * 多錢 */ @Override public void addMoney() { String sql = "update account set money = money + ? where user_name = ?"; jdbcTemplate.update(sql,100, "mary"); } }
service類
@Service
public class UserService {
@Autowired
private UserDao userDao;
/**
* 轉賬的方法
*/
public void accountMoney() {
// lucy少100
userDao.reduceMoney();
// mary多100
userDao.addMoney();
}
}
配置檔案
<!--直接配置連線池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/user_db"/> <property name="username" value="root"/> <property name="password" value="root"/> </bean> <!-- JdbcTemplate 物件 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--注入 dataSource--> <property name="dataSource" ref="dataSource"/> </bean> <!-- 元件掃描 --> <context:component-scan base-package="com.zjh.tx"/>
測試方法
@Test
public void txTest() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
結果
上面方法測試,是沒有問題的。但是如果程式碼執行過程中出現異常,就會有問題
新增異常程式碼
測試結果為
可以看到已經出現異常了,但是lucy 的錢卻扣減成功了,而mary的錢卻沒有變化這就是異常導致的問題。
這就需要使用到事務了,具體看下方內容
Spring 事務管理介紹
在 Spring 進行事務管理操作
(1)程式設計式事務管理
(2)和宣告式事務管理(使用)
宣告式事務管理
(1)基於註解方式(使用)
(2)基於 xml 配置檔案方式
在 Spring 進行宣告式事務管理,底層使用 AOP 原理
Spring 事務管理 API
(1)提供一個介面,代表事務管理器,這個介面針對不同的框架提供不同的實現類
註解宣告式事務管理
在 spring 配置檔案配置事務管理器
<!--建立事務管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入資料來源-->
<property name="dataSource" ref="dataSource"/>
</bean>
在 spring 配置檔案,開啟事務註解
(1)在 spring 配置檔案引入名稱空間 tx、aop
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
(2)開啟事務註解
<!--開啟事務註解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
在 service 類上面(或者 service 類裡面方法上面)新增事務註解
(1)@Transactional,這個註解新增到類上面,也可以新增方法上面
(2)如果把這個註解新增類上面,這個類裡面所有的方法都新增事務
(3)如果把這個註解新增方法上面,為這個方法新增事務
測試結果
雖然有異常但是結果是我們理想的,出現異常就回滾,不進行增減的操作。
宣告式事務管理引數配置
在 service 類上面添加註解@Transactional,在這個註解裡面可以配置事務相關引數
propagation:事務傳播行為
REQUIRE
:如果有事務在執行,當前的方法就在這個事務內執行,否則,就啟動一個新的事務,並在自己的事務內執行
REQUIRED NEW
:當前的方法必須啟動新事務,並在它自己的事務內執行。如果有事務正在執行,應該將它掛起
SUPPORTS
:如果有事務在執行,當前的方法就在這個事務內執行.否則它可以不執行在事務中
NOT SUPPORTE
:當前的方法不應該執行在事務中,如果有執行的事務,將它掛起
MANDATORY
:當前的方法必須執行在事務內部,如果沒有正在執行的事務,就丟擲異常
NEVER
:當前的方法不應該執行在事務中,如果有執行的事務,就丟擲異常
NESTED
:如果有事務在執行,當前的方法就應該在這個事務的巢狀事務內執行。否則,就啟動一個新的事務,並在它自己的事務內執行
設定方法
@Transactional(propagation = Propagation.REQUIRED)
ioslation:事務隔離級別
-
事務有特性稱為隔離性,多事務操作之間不會產生影響。不考慮隔離性產生很多問題
-
有三個讀問題:髒讀、不可重複讀、虛(幻)讀
-
髒讀:一個未提交事務讀取到另一個未提交事務的資料
-
不可重複讀:一個未提交事務讀取到另一提交事務修改資料
-
虛讀:一個未提交事務讀取到另一提交事務新增資料
-
通過設定事務隔離級別,解決讀問題
READ UNCOMMIT TED
:讀未提交,包含的問題:髒度、不可重複讀、幻讀
READ COMMITTED
:讀已提交,包含的問題:不可重複讀、幻讀
REPEATABLE READ
:可重複讀,包含的問題:幻讀
SERIALIZABLE
:序列化
設定方法
@Transactional(isolation = Isolation.REPEATABLE_READ)
timeout:超時時間
(1)事務需要在一定時間內進行提交,如果不提交進行回滾
(2)預設值是 -1 ,設定時間以秒單位進行計算
readOnly:是否只讀
(1)readOnly 預設值 false,表示可以查詢,可以新增修改刪除操作
(2)設定 readOnly 值是 true,設定成 true 之後,只能查詢
rollbackFor:回滾
設定出現哪些異常進行事務回滾
noRollbackFor:不回滾
設定出現哪些異常不進行事務回滾
XML 宣告式事務管理
使用 xml 檔案的方式就不用加上註解了
在 spring 配置檔案中進行配置
(1)配置事務管理器
<!--建立事務管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入資料來源-->
<property name="dataSource" ref="dataSource"/>
</bean>
(2)配置通知
<!-- 配置通知-->
<tx:advice id="txAdvice">
<!--配置事務引數-->
<tx:attributes>
<!--指定哪種規則的方法上面新增事務-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
(3)配置切入點和切面
<!-- 配置切入點和切面-->
<aop:config>
<!--配置切入點-->
<aop:pointcut id="pt" expression="execution(* com.zjh.spring.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
完全註解宣告式事務管理
建立配置類,使用配置類替代 xml 配置檔案
/**
* @author zjh
*
* EnableTransactionManagement:開啟事務
*/
@Configuration
@ComponentScan(basePackages = "com.zjh.tx")
@EnableTransactionManagement
public class TxConfig {
/**
* 建立資料庫連線池
* @return DruidDataSource
*/
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
/**
* 建立 JdbcTemplate 物件
* @param dataSource 資料來源
* @return JdbcTemplate
*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
//到 ioc 容器中根據型別找到 dataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入 dataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
/**
* 建立事務管理器
* @param dataSource 資料來源
* @return DataSourceTransactionManager
*/
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new
DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}