【Spring5】資料庫事務操作
Spring針對事務的操作
事務的概念:事務是資料庫最基本的單元,邏輯上的一組操作,要麼都成功,如果有一個操作失敗則都失敗。
事務的特性:ACID
原子性、一致性、隔離性、永續性
JavaEE環境三層結構
1.WEB檢視層
2.Service業務邏輯層
主要包含一些業務邏輯
3.資料訪問層
包含對資料庫操作的方法,不涉及業務的操作
(1)事務新增到JavaEE三層結構的Service業務邏輯層
(2)在Spring進行事務管理操作,有兩種方式:
①程式設計式事務管理:set autocommit = false
哪些操作會導致資料庫的自動提交?
1.DDL操作一旦執行,都會自動提交,set autocommit = false對DDL操作無效。
2.DML預設情況會自動提交,可以通過set autocommit = false的方式取消DML操作的自動提交。
DML(Data Manipulation Language)資料操縱語言:
適用範圍:對資料庫中的資料進行一些簡單操作,如insert,delete,update,select等.
DDL(Data Definition Language)資料定義語言:
適用範圍:對資料庫中的某些物件(例如,database,table)進行管理,如Create,Alter和Drop.
@Test public void updateTest() { Connection conn = null; try { conn = JDBCUtils.getConnection(); conn.setAutoCommit(false); // System.out.println(conn.getAutoCommit()); String sql1 = "update user set balance = balance - 100 where uer=?"; update(conn, sql1, "AA"); System.out.println(1/0); String sql2 = "update user set balance = balance + 100 where uer=?"; update(conn, sql2, "2B"); System.out.println("轉賬成功"); conn.commit(); } catch (SQLException throwables) { throwables.printStackTrace(); } finally { try { if(conn!=null) { conn.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } } }
②宣告式事務管理
基於註解方式(使用)
1.建立事務管理器 DataSourceTractionManager,並注入資料來源(資料庫連線池)
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
2.在配置檔案中引入名稱空間tx
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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
3.開啟事務註解
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
4.在Service中新增事務註解@Tranctional
在類上面新增此註解,則類中所有方法都新增到事務,若新增到方法上,則此方法新增到事務。
@Transactional
public void accountMoney(String id1, String id2, double money) {
accountDao.reduceMoney(id1, money);
System.out.println(1/0);
accountDao.addMoney(id2, money);
}
錢數不會發生變化
註解@Transactional的相關引數
1.propagation 事務傳播行為
事務的傳播行為,指的是多事務之間進行呼叫時,這個過程中的是如何管理的。
![](file://D:\資料\學習筆記\Java\Spring\2.png?msec=1649505122694)
事務的傳播行為由傳播屬性指定。Spring定義了7種傳播行為,重點記住前兩個。
傳播屬性 | 描述 |
---|---|
REQURED | 如果呼叫方法有事務在執行,當前方法就在這個事務中執行,否則,就啟動一個自己的新的事務,並在此事務中執行。(如上圖) |
REQURED_NEW | 當前方法必須啟動自己的事務,並在自己的事務中進行,如果呼叫方法中有事務在執行,則應該將其掛起 |
SUPPORTS | |
NOT_SUPPORTED | |
MANDATORY | |
NEVER | |
NESTED |
@Transactional(propagation = Propagation.REQUIRED)
2.isolation 事務隔離級別
事務的隔離性,是指多事務之間操作不會受彼此影響,不考慮隔離性會產生很多問題:
①髒讀:一個事務讀取到了另一個未提交事務的資料(即兩個事務都沒有提交,事務可能回滾導致另一個事務讀到髒資料)
②不可重複讀:一個未提交事務讀取到另一提交事務修改資料
![](file://D:\資料\學習筆記\Java\Spring\3.png?msec=1649505122695)
未提交事務的事務兩次讀到的資料不一致,正常情況下是不能讀取資料的
髒讀是一個問題,但是不可重複讀是一種現象,是允許發生的
③幻讀:一個未提交事務讀到另一個提交事務新增的資料,兩次讀到的行數不一致
通過設定事務隔離級別,解決讀問題
髒讀 | 不可重複讀 | 幻讀 | |
---|---|---|---|
Read Uncommitted (讀未提交) |
有 | 有 | 有 |
Read Committed (讀已提交) |
無 | 有 | 有 |
Repeatable Read (可重複讀) |
無 | 無 | 有 |
Serializable (可序列化) |
無 | 無 | 無 |
@Transactional(isolation = Isolation.SERIALIZABLE)
MySQL預設級別為REPEATABLE READ可重複讀
3.timeout 超時時間
事務需要在一定時間內進行提交,超過這個時間就會回滾
預設值為-1,表示不超時,值以s為單位
4.readOnly 是否只讀
讀:查詢操作
寫:新增修改刪除操作
5.rollbackFor 事務回滾
設定出現哪些異常進行事務的回滾
6.noRollbackFor 事務不回滾
設定出現哪些異常不進行事務的回滾
只需設定為異常的Class即可
基於XML配置檔案方式
(4)在Spring進行宣告式事務管理,底層使用AOP原理
(5)Spring事務管理API
完全註解開發
@Configuration //配置類
@ComponentScan(basePackages = "com.hikaru") //掃描元件
@EnableTransactionManagement //開啟事務
public class TxConfig {
@Bean
public DruidDataSource getDataSource() throws IOException {
DruidDataSource druidDataSource = new DruidDataSource();
InputStream is = TxConfig.class.getClassLoader().getResourceAsStream("JDBC.properties");
Properties properties = new Properties();
properties.load(is);
String driverClassName = properties.getProperty("driverClassName");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
druidDataSource.setDriverClassName(driverClassName);
druidDataSource.setUrl(url);
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
return druidDataSource;
}
@Bean
public DataSourceTransactionManager getTransactionManager(DruidDataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager() ;
transactionManager.setDataSource(dataSource);
return transactionManager;
}
@Bean
public JdbcTemplate getJdbcTemplate(DruidDataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
@Test
public void test() throws IOException, SQLException {
DruidDataSource druidDataSource = getDataSource();
System.out.println(druidDataSource.getConnection());
}
}
其中@Bean作用是將方法返回值注入IOC容器
配置類方法中的形參均在IOC容器中找到
測試
@Test
public void test() {
ApplicationContext context =
new AnnotationConfigApplicationContext(TxConfig.class);
AccountService accountService = context.getBean("accountService", AccountService.class);
accountService.accountMoney("1001", "1002", 500);
}