wordpress 禁用縮略圖電腦優(yōu)化大師官方免費(fèi)下載
目錄
一、JdbcTemplate(持久化技術(shù))
1. 簡(jiǎn)介
2. 準(zhǔn)備工作
2.1. 引入依賴坐標(biāo)
2.2. 創(chuàng)建jdbc.properties
2.3. 配置Spring的配置文件
3. 測(cè)試
3.1. 在測(cè)試類裝配 JdbcTemplate
3.2. 測(cè)試增刪改功能
查詢一條數(shù)據(jù)為實(shí)體類對(duì)象
查詢多條數(shù)據(jù)為一個(gè)list集合
查詢單行單列的值
二、SpringTransactional(事務(wù)管理)
1. 事務(wù)類型
1.1. 編程式事務(wù)
缺陷
1.2. 聲明式事務(wù)
好處
1.3. 總結(jié)
2. 基于注解的聲明式事務(wù)
2.1. 準(zhǔn)備工作
加入依賴
創(chuàng)建jdbc.properties
配置Spring的配置文件
創(chuàng)建表
創(chuàng)建組件
2.2. 測(cè)試無事務(wù)情況
創(chuàng)建測(cè)試類
模擬場(chǎng)景
觀察結(jié)果
2.3. 加入事務(wù)
添加事務(wù)配置
添加事務(wù)注解
觀察結(jié)果
2.4. @Transactional注解標(biāo)識(shí)的位置
2.5. 事務(wù)屬性
1、只讀:readOnly
介紹
使用方式
注意
2、超時(shí):timeout
介紹
使用方式
觀察結(jié)果
3、回滾策略:四種
介紹
使用方式
觀察結(jié)果
4、事務(wù)隔離級(jí)別:Isolation
介紹
四種隔離級(jí)別
使用方式
5、事務(wù)傳播行為:propagation
介紹
測(cè)試
觀察結(jié)果
6、總結(jié)
3. 基于XML的Transactional
3.1. 場(chǎng)景模擬
3.2. 修改 Spring 配置文件
一、JdbcTemplate(持久化技術(shù))
1. 簡(jiǎn)介
Spring 框架對(duì) JDBC 進(jìn)行了封裝,使用 JdbcTemplate 可以讓我們非常方便的實(shí)現(xiàn)對(duì)數(shù)據(jù)庫的操作。
2. 準(zhǔn)備工作
在使用JdbcTemplate之前,我們需要進(jìn)行一些依賴配置,加入mysql-connector-java數(shù)據(jù)庫連接驅(qū)動(dòng),
配置druid連接池技術(shù),引入spring-orm包,在執(zhí)行持久化層操作,與持久化層技術(shù)進(jìn)行整合過程中,
需要使用orm、jdbc、tx三個(gè)jar包,導(dǎo)入·orm 包就可以通過 Maven 的依賴傳遞性把其他兩個(gè)也jar包一起導(dǎo)入
2.1. 引入依賴坐標(biāo)
<!-- Spring 持久化層支持jar包 --><!-- Spring 在執(zhí)行持久化層操作、與持久化層技術(shù)進(jìn)行整合過程中,需要使用orm、jdbc、tx三個(gè)jar包 --><!-- 導(dǎo)入 orm 包就可以通過 Maven 的依賴傳遞性把其他兩個(gè)也導(dǎo)入 --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.1</version></dependency>
整體:
<dependencies><!-- 基于Maven依賴傳遞性,導(dǎo)入spring-context依賴即可導(dǎo)入當(dāng)前所需所有jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.1</version></dependency><!-- Spring 持久化層支持jar包 --><!-- Spring 在執(zhí)行持久化層操作、與持久化層技術(shù)進(jìn)行整合過程中,需要使用orm、jdbc、tx三個(gè)jar包 --><!-- 導(dǎo)入 orm 包就可以通過 Maven 的依賴傳遞性把其他兩個(gè)也導(dǎo)入 --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.1</version></dependency><!-- Spring 測(cè)試相關(guān) --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.1</version></dependency><!-- junit測(cè)試 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- MySQL驅(qū)動(dòng) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><!-- 數(shù)據(jù)源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.1</version></dependency></dependencies>
2.2. 創(chuàng)建jdbc.properties
有數(shù)據(jù)源環(huán)境,必定需要配置數(shù)據(jù)源文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
2.3. 配置Spring的配置文件
再通過配置Spring的配置文件引入數(shù)據(jù)源:
<!--引入jdbc.properties--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><bean class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
3. 測(cè)試
3.1. 在測(cè)試類裝配 JdbcTemplate
環(huán)境搭建完畢:開始測(cè)試環(huán)境是否搭建成功
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;}
3.2. 測(cè)試增刪改功能
環(huán)境搭建成功后:開始測(cè)試 JdbcTemplate 的增刪改功能
@Test
//測(cè)試增刪改功能
public void testUpdate(){ String sql = "insert into t_emp values(null,?,?,?)"; int result = jdbcTemplate.update(sql, "張三", 23, "男"); System.out.println(result);
}
增刪改測(cè)試完畢:開始測(cè)試 JdbcTemplate 的查詢功能
查詢功能主要有查詢一條數(shù)據(jù)為實(shí)體類對(duì)象,查詢多條數(shù)據(jù)為一個(gè)list集合,查詢單行單列的值
查詢一條數(shù)據(jù)為實(shí)體類對(duì)象
@Test
//查詢一條數(shù)據(jù)為一個(gè)實(shí)體類對(duì)象
public void testSelectEmpById(){ String sql = "select * from t_emp where id = ?"; Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 1); System.out.println(emp);
}
查詢多條數(shù)據(jù)為一個(gè)list集合
@Test
//查詢多條數(shù)據(jù)為一個(gè)list集合
public void testSelectList(){ String sql = "select * from t_emp"; List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class)); list.forEach(emp -> System.out.println(emp));
}
查詢單行單列的值
1、測(cè)試操作
@Test
//查詢單行單列的值
public void selectCount(){ String sql = "select count(id) from t_emp"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(count);
}
2、創(chuàng)建jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
3、配置Spring的配置文件
<!--引入jdbc.properties--><context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"></property><property name="url" value="${jdbc.url}"></property><property name="username" value="${jdbc.username}"></property><property name="password" value="${jdbc.password}"></property></bean><bean class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"></property></bean>
4、測(cè)試
1、在測(cè)試類裝配 JdbcTemplate
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-jdbc.xml")
public class JdbcTemplateTest {@Autowiredprivate JdbcTemplate jdbcTemplate;}
2、測(cè)試增刪改功能
@Test
//測(cè)試增刪改功能
public void testUpdate(){ String sql = "insert into t_emp values(null,?,?,?)"; int result = jdbcTemplate.update(sql, "張三", 23, "男"); System.out.println(result);
}
3、查詢一條數(shù)據(jù)為實(shí)體類對(duì)象
@Test
//查詢一條數(shù)據(jù)為一個(gè)實(shí)體類對(duì)象
public void testSelectEmpById(){ String sql = "select * from t_emp where id = ?"; Emp emp = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Emp.class), 1); System.out.println(emp);
}
4、查詢多條數(shù)據(jù)為一個(gè) list 集合
@Test
//查詢多條數(shù)據(jù)為一個(gè)list集合
public void testSelectList(){ String sql = "select * from t_emp"; List<Emp> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Emp.class)); list.forEach(emp -> System.out.println(emp));
}
5、查詢單行單列的值
@Test
//查詢單行單列的值
public void selectCount(){ String sql = "select count(id) from t_emp"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); System.out.println(count);
}
二、SpringTransactional(事務(wù)管理)
1. 事務(wù)類型
1.1. 編程式事務(wù)
事務(wù)功能的相關(guān)操作全部通過自己編寫代碼來實(shí)現(xiàn):
Connection conn = ...; try { // 開啟事務(wù):關(guān)閉事務(wù)的自動(dòng)提交 conn.setAutoCommit(false); // 核心操作 // 提交事務(wù) conn.commit(); }catch(Exception e){ // 回滾事務(wù) conn.rollBack(); }finally
{ // 釋放數(shù)據(jù)庫連接 conn.close(); }
缺陷
- 細(xì)節(jié)沒有被屏蔽:具體操作過程中,所有細(xì)節(jié)都需要程序員自己來完成,比較繁瑣
- 代碼復(fù)用性不高: 如果沒有有效抽取出來,每次實(shí)現(xiàn)功能都需要自己編寫代碼,代碼就沒有得到復(fù)用。
1.2. 聲明式事務(wù)
既然事務(wù)控制的代碼有規(guī)律可循,代碼的結(jié)構(gòu)基本是確定的,所以框架就可以將固定模式的代碼抽取出來,進(jìn)行
相關(guān)的封裝封裝起來后,我們只需要在配置文件中進(jìn)行簡(jiǎn)單的配置即可完成操作
好處
- 好處1:提高開發(fā)效率
- 好處2:消除了冗余的代碼
- 好處3:框架會(huì)綜合考慮相關(guān)領(lǐng)域中在實(shí)際開發(fā)環(huán)境下有可能遇到的各種問題,進(jìn)行了健壯性、性能等各個(gè)方
面的優(yōu)化
1.3. 總結(jié)
所以,我們可以總結(jié)下面兩個(gè)概念:
- 編程式:自己寫代碼實(shí)現(xiàn)功能
- 聲明式:通過配置讓框架實(shí)現(xiàn)功能
2. 基于注解的聲明式事務(wù)
2.1. 準(zhǔn)備工作
加入依賴
<dependencies><!-- 基于Maven依賴傳遞性,導(dǎo)入spring-context依賴即可導(dǎo)入當(dāng)前所需所有jar包 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.1</version></dependency><!-- Spring 持久化層支持jar包 --><!-- Spring 在執(zhí)行持久化層操作、與持久化層技術(shù)進(jìn)行整合過程中,需要使用orm、jdbc、tx三個(gè)jar包 --><!-- 導(dǎo)入 orm 包就可以通過 Maven 的依賴傳遞性把其他兩個(gè)也導(dǎo)入 --><dependency><groupId>org.springframework</groupId><artifactId>spring-orm</artifactId><version>5.3.1</version></dependency><!-- Spring 測(cè)試相關(guān) --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.1</version></dependency><!-- junit測(cè)試 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- MySQL驅(qū)動(dòng) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.16</version></dependency><!-- 數(shù)據(jù)源 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.31</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.1</version></dependency></dependencies>
創(chuàng)建jdbc.properties
jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
jdbc.username=root
jdbc.password=root
配置Spring的配置文件
<!--掃描組件-->
<context:component-scan base-package="com.zheng.travel.spring.tx.annotation"></context:component-scan><!-- 導(dǎo)入外部屬性文件 -->
<context:property-placeholder location="classpath:jdbc.properties" /> <!-- 配置數(shù)據(jù)源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${jdbc.url}"/> <property name="driverClassName" value="${jdbc.driver}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/>
</bean><!-- 配置 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 裝配數(shù)據(jù)源 --> <property name="dataSource" ref="druidDataSource"/>
</bean>
創(chuàng)建表
CREATE TABLE t_book ( book_id int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', book_name varchar(20) DEFAULT NULL COMMENT '圖書名稱', price int(11) DEFAULT NULL COMMENT '價(jià)格', stock int(10) unsigned DEFAULT NULL COMMENT '庫存(無符號(hào))', PRIMARY KEY (book_id)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;insert into t_book(book_id,book_name,price,stock) values (1,'斗破蒼穹',80,100),(2,'斗羅大陸',50,100);CREATE TABLE t_user ( user_id int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', username varchar(20) DEFAULT NULL COMMENT '用戶名', balance int(10) unsigned DEFAULT NULL COMMENT '余額(無符號(hào))', PRIMARY KEY (user_id)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
insert into t_user(user_id,username,balance) values (1,'admin',50);
創(chuàng)建組件
1、創(chuàng)建BookController
@Controller
public class BookController { @Autowired private BookService bookService; public void buyBook(Integer bookId, Integer userId){ bookService.buyBook(bookId, userId); }}
2、創(chuàng)建接口BookService
public interface BookService { void buyBook(Integer bookId, Integer userId);
}
3、創(chuàng)建實(shí)現(xiàn)類BookServiceImpl
@Service
public class BookServiceImpl implements BookService { @Autowired private BookDao bookDao; @Override public void buyBook(Integer bookId, Integer userId) { //查詢圖書的價(jià)格 Integer price = bookDao.getPriceByBookId(bookId); //更新圖書的庫存 bookDao.updateStock(bookId); //更新用戶的余額 bookDao.updateBalance(userId, price); }}
4、創(chuàng)建接口BookDao:
public interface BookDao { Integer getPriceByBookId(Integer bookId); void updateStock(Integer bookId); void updateBalance(Integer userId, Integer price);
}
5、創(chuàng)建實(shí)現(xiàn)類BookDaoImpl
@Repository
public class BookDaoImpl implements BookDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public Integer getPriceByBookId(Integer bookId) { String sql = "select price from t_book where book_id = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, bookId); } @Override public void updateStock(Integer bookId) { String sql = "update t_book set stock = stock - 1 where book_id = ?"; jdbcTemplate.update(sql, bookId); } @Override public void updateBalance(Integer userId, Integer price) { String sql = "update t_user set balance = balance - ? where user_id = ?"; jdbcTemplate.update(sql, price, userId); }}
2.2. 測(cè)試無事務(wù)情況
創(chuàng)建測(cè)試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:tx-annotation.xml")
public class TxByAnnotationTest { @Autowired private BookController bookController; @Test public void testBuyBook(){ bookController.buyBook(1, 1); }}
模擬場(chǎng)景
用戶購(gòu)買圖書,先查詢圖書的價(jià)格,再更新圖書的庫存和用戶的余額
假設(shè)用戶id為1的用戶,購(gòu)買id為1的圖書用戶余額為50,而圖書價(jià)格為80
購(gòu)買圖書之后,用戶的余額為-30,數(shù)據(jù)庫中余額字段設(shè)置了無符號(hào),因此無法將-30插入到余額字段
此時(shí)執(zhí)行sql語句會(huì)拋出SQLException
觀察結(jié)果
因?yàn)闆]有添加事務(wù),圖書的庫存更新了,但是用戶的余額沒有更新
顯然這樣的結(jié)果是錯(cuò)誤的,購(gòu)買圖書是一個(gè)完整的功能,更新庫存和更新余額要么都成功要么都失敗
2.3. 加入事務(wù)
添加事務(wù)配置
在Spring的配置文件中添加配置:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> ? ?<property name="dataSource" ref="dataSource"></property>
</bean><!-- ? ?開啟事務(wù)的注解驅(qū)動(dòng) ? ?通過注解@Transactional所標(biāo)識(shí)的方法或標(biāo)識(shí)的類中所有的方法,都會(huì)被事務(wù)管理器管理事務(wù)--><!-- transaction-manager屬性的默認(rèn)值是transactionManager,如果事務(wù)管理器bean的id正好就是這個(gè)默認(rèn)值,則可以省略這個(gè)屬性 --><tx:annotation-driven transaction-manager="transactionManager" />
注意:導(dǎo)入的名稱空間需要 tx 結(jié)尾的那個(gè)
添加事務(wù)注解
因?yàn)閟ervice層表示業(yè)務(wù)邏輯層,一個(gè)方法表示一個(gè)完成的功能,因此處理事務(wù)一般在service層處理
在BookServiceImpl的buybook()添加注解@Transactional
觀察結(jié)果
由于使用了Spring的聲明式事務(wù),更新庫存和更新余額都沒有執(zhí)行
現(xiàn)在環(huán)境搭建完畢,增刪改查了解完畢,就需要正式開始了解事務(wù)操做方案:
首先,我們要明確編程式事務(wù)和聲明式事務(wù)兩種概念
2.4. @Transactional注解標(biāo)識(shí)的位置
@Transactional標(biāo)識(shí)在方法上,則只會(huì)影響該方法
@Transactional標(biāo)識(shí)的類上,則會(huì)影響類中所有的方法
2.5. 事務(wù)屬性
例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行,也可能開啟一個(gè)新事務(wù),并在自己的事務(wù)中運(yùn)行
- REQUIRED:如果當(dāng)前沒有事務(wù),就新建一個(gè)事務(wù),如果已經(jīng)存在一個(gè)事務(wù)中,加入到這個(gè)事務(wù)中,一般的選擇(默認(rèn)值)
- SUPPORTS:支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),就以非事務(wù)方式執(zhí)行(沒有事務(wù))
- MANDATORY:使用當(dāng)前的事務(wù),如果當(dāng)前沒有事務(wù),就拋出異常
- REQUERS_NEW:新建事務(wù),如果當(dāng)前在事務(wù)中,把當(dāng)前事務(wù)掛起
- NOT_SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起
- NEVER:以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),拋出異常
- NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行 REQUIRED 類似的操作
- 超時(shí)時(shí)間:默認(rèn)值是-1,沒有超時(shí)限制。如果有,以秒為單位進(jìn)行設(shè)置
- 是否只讀:建議查詢時(shí)設(shè)置為只讀
1、只讀:readOnly
介紹
對(duì)一個(gè)查詢操作來說,如果我們把它設(shè)置成只讀,就能夠明確告訴數(shù)據(jù)庫,
這個(gè)操作不涉及寫操作,這樣數(shù)據(jù)庫就能夠針對(duì)查詢操作來進(jìn)行優(yōu)化
使用方式
@Transactional(readOnly = true)
public void buyBook(Integer bookId, Integer userId) { //查詢圖書的價(jià)格 Integer price = bookDao.getPriceByBookId(bookId); //更新圖書的庫存 bookDao.updateStock(bookId); //更新用戶的余額 bookDao.updateBalance(userId, price); //System.out.println(1/0);
}
注意
對(duì)增刪改操作設(shè)置只讀會(huì)拋出下面異常:
Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modificationare not allowed
2、超時(shí):timeout
介紹
事務(wù)在執(zhí)行過程中,有可能因?yàn)橛龅侥承﹩栴},導(dǎo)致程序卡住,從而長(zhǎng)時(shí)間占用數(shù)據(jù)庫資源
而長(zhǎng)時(shí)間占用資源,大概率是因?yàn)槌绦蜻\(yùn)行出現(xiàn)了問題(可能是Java程序或MySQL數(shù)據(jù)庫或網(wǎng)絡(luò)連接等等)
此時(shí)這個(gè)很可能出問題的程序應(yīng)該被回滾,撤銷它已做的操作,事務(wù)結(jié)束,把資源讓出來,讓其他正常程序可以
執(zhí)行
簡(jiǎn)單來說就是:超時(shí)回滾,釋放資源
使用方式
@Transactional(timeout = 3)
public void buyBook(Integer bookId, Integer userId) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } //查詢圖書的價(jià)格 Integer price = bookDao.getPriceByBookId(bookId); //更新圖書的庫存 bookDao.updateStock(bookId); //更新用戶的余額 bookDao.updateBalance(userId, price); //System.out.println(1/0);
}
觀察結(jié)果
執(zhí)行過程中拋出異常:
org.springframework.transaction.TransactionTimedOutException: Transaction timed out:deadline was Fri Jun 04 16:25:39 CST 2022
3、回滾策略:四種
介紹
聲明式事務(wù)默認(rèn)只針對(duì)運(yùn)行時(shí)異常回滾,編譯時(shí)異常不回滾
可以通過@Transactional中相關(guān)屬性設(shè)置回滾策略
- rollbackFor:需要設(shè)置一個(gè)Class類型的對(duì)象
- rollbackForClassName:需要設(shè)置一個(gè)字符串類型的全類名
- noRollbackFor:需要設(shè)置一個(gè)Class類型的對(duì)象
- rollbackFor:需要設(shè)置一個(gè)字符串類型的全類名
使用方式
@Transactional(noRollbackFor = ArithmeticException.class)
//@Transactional(noRollbackForClassName = "java.lang.ArithmeticException")
public void buyBook(Integer bookId, Integer userId) { //查詢圖書的價(jià)格 Integer price = bookDao.getPriceByBookId(bookId); //更新圖書的庫存 bookDao.updateStock(bookId); //更新用戶的余額 bookDao.updateBalance(userId, price); System.out.println(1/0);
}
觀察結(jié)果
雖然購(gòu)買圖書功能中出現(xiàn)了數(shù)學(xué)運(yùn)算異常(ArithmeticException),但是我們?cè)O(shè)置的回滾策略是
當(dāng)出現(xiàn)ArithmeticException不發(fā)生回滾,因此購(gòu)買圖書的操作正常執(zhí)行
4、事務(wù)隔離級(jí)別:Isolation
介紹
數(shù)據(jù)庫系統(tǒng)必須具有隔離并發(fā)運(yùn)行各個(gè)事務(wù)的能力,使它們不會(huì)相互影響,避免各種并發(fā)問題,
一個(gè)事務(wù)與其他事務(wù)隔離的程度稱為隔離級(jí)別,SQL標(biāo)準(zhǔn)中規(guī)定了多種事務(wù)隔離級(jí)別,
不同隔離級(jí)別對(duì)應(yīng)不同的干擾程度,隔離級(jí)別越高,數(shù)據(jù)一致性就越好,但并發(fā)性越弱
四種隔離級(jí)別
- 讀未提交:READ UNCOMMITTED允許Transaction01讀取Transaction02未提交的修改。
- 讀已提交:READ COMMITTED\要求Transaction01只能讀取Transaction02已提交的修改。
- 可重復(fù)讀:REPEATABLE READ確保Transaction01可以多次從一個(gè)字段中讀取到相同的值,即Transaction01執(zhí)行期間禁止其它事務(wù)對(duì)這個(gè)字段進(jìn)行更新。
- 串行化:SERIALIZABLE確保Transaction01可以多次從一個(gè)表中讀取到相同的行,在Transaction01執(zhí)行期間,禁止其它事務(wù)對(duì)這個(gè)表進(jìn)行添加、更新、刪除操作??梢员苊馊魏尾l(fā)問題,但性能十分低下。
各個(gè)隔離級(jí)別解決并發(fā)問題的能力見下表:
隔離級(jí)別 | 臟讀 | 不可重復(fù)讀 | 幻讀 |
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 無 | 有 | 有 |
REPEATABLE READ | 無 | 無 | 有 |
SERIALIZABLE | 無 | 無 | 無 |
各種數(shù)據(jù)庫產(chǎn)品對(duì)事務(wù)隔離級(jí)別的支持程度:
隔離級(jí)別 | Oracle | MySQL |
READ UNCOMMITTED | × | √ |
READ COMMITTED | √(默認(rèn)) | √ |
REPEATABLE READ | × | √(默認(rèn)) |
SERIALIZABLE | √ | √ |
使用方式
@Transactional(isolation = Isolation.DEFAULT)//使用數(shù)據(jù)庫默認(rèn)的隔離級(jí)別
@Transactional(isolation = Isolation.READ_UNCOMMITTED)//讀未提交
@Transactional(isolation = Isolation.READ_COMMITTED)//讀已提交
@Transactional(isolation = Isolation.REPEATABLE_READ)//可重復(fù)讀
@Transactional(isolation = Isolation.SERIALIZABLE)//串行化
5、事務(wù)傳播行為:propagation
介紹
當(dāng)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),必須指定事務(wù)應(yīng)該如何傳播,
測(cè)試
1、創(chuàng)建接口CheckoutService
public interface CheckoutService { void checkout(Integer[] bookIds, Integer userId);
}
2、創(chuàng)建實(shí)現(xiàn)類CheckoutServiceImpl
@Service
public class CheckoutServiceImpl implements CheckoutService { @Autowired private BookService bookService; @Override @Transactional //一次購(gòu)買多本圖書 public void checkout(Integer[] bookIds, Integer userId) { for (Integer bookId : bookIds) { bookService.buyBook(bookId, userId); } }
}
3、在BookController中添加方法
@Autowired
private CheckoutService checkoutService;public void checkout(Integer[] bookIds, Integer userId){ checkoutService.checkout(bookIds, userId);
}
在數(shù)據(jù)庫中將用戶的余額修改為100元
觀察結(jié)果
可以通過@Transactional中的propagation屬性設(shè)置事務(wù)傳播行為
修改BookServiceImpl中buyBook()上,注解@Transactional的propagation屬性
@Transactional(propagation = Propagation.REQUIRED)
默認(rèn)情況,表示如果當(dāng)前線程上有已經(jīng)開啟的事務(wù)可用,那么就在這個(gè)事務(wù)中運(yùn)行
經(jīng)過觀察,購(gòu)買圖書的方法buyBook()在checkout()中被調(diào)用,checkout()上有事務(wù)注解,因此在此事務(wù)中執(zhí)行
所購(gòu)買的兩本圖書的價(jià)格為80和50,而用戶的余額為100,因此在購(gòu)買第二本圖書時(shí)余額不足失敗,導(dǎo)致整個(gè)
checkout()回滾,即只要有一本書買不了,就都買不了
@Transactional(propagation = Propagation.REQUIRES_NEW)
表示不管當(dāng)前線程上是否有已經(jīng)開啟的事務(wù),都要開啟新事務(wù)
同樣的場(chǎng)景,每次購(gòu)買圖書都是在buyBook()的事務(wù)中執(zhí)行,因此第一本圖書購(gòu)買成功,事務(wù)結(jié)束,
第二本圖書購(gòu)買失敗,只在第二次的buyBook()中回滾,購(gòu)買第一本圖書不受影響,即能買幾本就買幾本
6、總結(jié)
基于注解的聲明式配置
依賴導(dǎo)入,在導(dǎo)入引入spring-orm包的時(shí)候就已經(jīng)導(dǎo)入相關(guān)配置,現(xiàn)在我們其它的數(shù)據(jù)源環(huán)境搭建完畢后,就可
以進(jìn)行事務(wù)配置,我們?cè)谠赟pring的配置文件中添加配置。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property></bean><tx:annotation-driven transaction-manager="transactionManager" />
transaction-manager屬性的默認(rèn)值是transactionManager,
如果事務(wù)管理器bean的id正好就是這個(gè)默認(rèn)值,則可以省略這個(gè)屬性,
我們需要導(dǎo)入名稱空間為 tx 結(jié)尾的那個(gè),開啟事務(wù)的注解驅(qū)動(dòng),
從而通過注解@Transactional所標(biāo)識(shí)的方法或標(biāo)識(shí)的類中所有的方法,都會(huì)被事務(wù)管理器管理事務(wù)。
@Transactional注解標(biāo)識(shí)的位置,
如果@Transactional標(biāo)識(shí)在方法上,則只會(huì)影響該方法
如果@Transactional標(biāo)識(shí)的類上,則會(huì)影響類中所有的方法
3. 基于XML的Transactional
3.1. 場(chǎng)景模擬
參考基于注解的聲明式事務(wù)
3.2. 修改 Spring 配置文件
將Spring配置文件中去掉tx:annotation-driven 標(biāo)簽,并添加配置:
<aop:config> <!-- 配置事務(wù)通知和切入點(diǎn)表達(dá)式 --> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.yjxz.spring.tx.xml.service.impl.*.*(..))"></aop:advisor></aop:config><!-- tx:advice標(biāo)簽:配置事務(wù)通知 -->
<!-- id屬性:給事務(wù)通知標(biāo)簽設(shè)置唯一標(biāo)識(shí),便于引用 -->
<!-- transaction-manager屬性:關(guān)聯(lián)事務(wù)管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- tx:method標(biāo)簽:配置具體的事務(wù)方法 --> <!-- name屬性:指定方法名,可以使用星號(hào)代表多個(gè)字符 --> <tx:method name="get*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="find*" read-only="true"/> <!-- read-only屬性:設(shè)置只讀屬性 --> <!-- rollback-for屬性:設(shè)置回滾的異常 --> <!-- no-rollback-for屬性:設(shè)置不回滾的異常 --> <!-- isolation屬性:設(shè)置事務(wù)的隔離級(jí)別 --> <!-- timeout屬性:設(shè)置事務(wù)的超時(shí)屬性 --> <!-- propagation屬性:設(shè)置事務(wù)的傳播行為 --> <tx:method name="save*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/> <tx:method name="update*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/> <tx:method name="delete*" read-only="false" rollback-for="java.lang.Exception" propagation="REQUIRES_NEW"/> </tx:attributes></tx:advice>
注意:基于xml實(shí)現(xiàn)的聲明式事務(wù),必須引入aspectJ的依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.1</version></dependency>