目錄
前言
一 什麼是事務
1.事務分類
2.事務四個原則
3.事務管理方式
二 程式設計式事務管理
1.TransactionDefinition
2.TransactionStatus
3.PlatformTransactionManager
(1)作用
(2)實作類
(3)執行個體
4.TransactionTemplate
5.簡單總結程式設計式事務管理
三 聲明式事務管理
1.xml配置
(1)通過在xml配置來定義事務通知
(2)增加切入點
(3)執行個體驗證
2.通過注解@Transactional
(1)在xml中配置
(2)在相應需要事務管理的方法上添加 @Transactional
四 總結
前言
在小白新手web開發簡單總結(十一)-資料庫連接配接的相關優化(資料源DataSource)主要總結了DataSource的一些概念,這次在總結下資料庫的事務管理。
一 什麼是事務
在小白新手web開發簡單總結(十)-資料庫HSQLDB執行個體問題總結中也簡單的提了下什麼是事務。所謂的事務就是一系列的動作,這些動作必須全部完成,若有一個失敗,則事務復原到初始狀态,那麼事務其實就是對應一系列SQL語句的操作,這些SQL語句要麼全都執行,要不全不執行。
1.事務分類
在Java EE中提供了兩種事務:
- (1)JDBC(Java DataBase Connectivity)事務:即本地事務,也就是一個普通事務,對應獨立的一個資料庫,通過Connection來控制管理;
是以在白新手web開發簡單總結(十一)-資料庫連接配接的相關優化(資料源DataSource)中的也可以看到我們配置了那麼多的DataSource,通過DataSource來擷取到Connection
- (2)JTA(Java Transcation API)事務:即分布式事務,涉及到兩個或多個資料源的事務,使事務可以跨越多個資料源,在Java EE中隻提供了事務管理接口,具體的實作是由應用程式的服務廠商(如WebSphere Application Server、Tomcat等)提供,JTA事務要比JDBC事務更強大。
另外還有一種對JDBC和JTA事務進行輕量封裝的Hibernate,Mybatis 等架構,最終連接配接資料庫還是用到Connection。
在Hibernate中,底層将JDBCTransaction或JTATransaction進行封裝,通過委托底層的JDBC或JTA來實作事務的處理。裡面有事務工廠類可以設定成JDBCTransactionFactory或者JTATransactionFactory,預設的事務是JDBC事務,該工廠類是線程安全對象,在web應用啟動的時候建立,一旦建立将不會輕易關閉,直到web應該關閉的時候,才會關閉。
遺留問題:Hibernate的一些簡單的内容後面繼續補充。
遺留問題:Mybatis使用的SqlSession對象的一些簡單的内容後面繼續補充。
2.事務四個原則
事務要遵循四個原則ACID:
- 原子性:事務開始的時候,要不全部做完,要不全部不做;若出現中止,必須全部復原到初始狀态;
- 一緻性:在事務執行的時候處于正确狀态,在事務執行完之後還是處于正确狀态,例如A向B轉錢,不可能出現A轉了錢,但是B沒有收到;
- 隔離性:并發事務執行互不影響,每次隻能一個事務對資料進行操作,例如A從一張銀行卡取錢的時候,不能向銀行卡裡面存錢;
- 持久性:事務完成後,所有的資料都要儲存到資料庫中
3.事務管理方式
在小白新手web開發簡單總結(八)-資料庫HSQLDB執行個體之前用到的JdbcTemplate來操作資料庫(其代碼對應com.wj.hsqldb.controller.BookManagerJdbcService#addBook())的時候,可以不關心Connection的建立和釋放,直接調用已有的方法來操作資料庫,但是預設的是AutoCommit模式,單純的依賴JdbcTemplate是無法保證事務的原子性、一緻性、隔離性和持久性。是以就有了事務管理的概念,而事務管理就是通過封裝出一些類來保證事務四個原則。
在Spring中提供了兩種事務管理的方式:
- (1)程式設計式事務管理:在業務邏輯需要的時候通過代碼的方式實作,粒度小;
- (2)聲明式事務管理:通過XML配置或注解實作,粒度比程式設計式事務管理大。
二 程式設計式事務管理
程式設計式事務管理主要就是在業務邏輯需要的時候加入相應的代碼來實作事務的管理。主要依賴于PlatformTransactionManager和模版類TransactionTemplate。
在Java EE中提供三個接口類來管理事務:
- org.springframework.transaction.PlatformTransactionManager:事務管理器,所有的事務都有該類負責;
- org.springframework.transaction.TransactionDefinition:事務的一些基礎資訊,如逾時時間、隔離級别、傳播屬性等;
- org.springframework.transaction.TransactionStatus:事務的一些狀态,如是否為一個新的事務,是否标記為復原。
1.TransactionDefinition
主要就是事務的一些基本基礎屬性。
(1)隔離級别
一個事務可能受其他并發事務影響的程度。
并發可能引起的問題:
- 髒讀(Dirty reads):一個事務讀取了另外一個事務改寫但尚未送出的資料時,如果另外一個事務復原,那麼第一個事務讀取的資料是無效的。
- 不可重複性(Nonrepeatable read):一個事務執行相同的查詢兩次或兩次以上,但每次都到的内容不同。通常是在另外一次查詢的時候進行了更新。重點在于修改
- 幻讀(Phantom read):于不可重複性類似,發生在一個事務讀取了幾行資料,另一個并發事務插入了一些資料,那麼在第一個事務就會多了一些不存在的記錄。重點在于增加或删除
在TransactionDefinition中提供了Spring的五大隔離級别:
- int ISOLATION_DEFAULT = -1; 預設的隔離級别;
- int ISOLATION_READ_UNCOMMITTED = 1;未送出讀:最低隔離級别,事務未送出前,可被其他事務讀取;可能會導緻髒讀、不可重複性、或幻讀;
- int ISOLATION_READ_COMMITTED = 2;送出讀:一個事務送出之後,才可被其他事務讀取;可阻止髒讀,但可能發生不可重複性或幻讀;
- int ISOLATION_REPEATABLE_READ = 4;可重複讀:多次讀取同一資料時,都和事務開始時候的内容是一緻的,禁止讀到其他事務未送出的資料;可阻止髒讀和不可重複性,但是可能發生幻讀;
- int ISOLATION_SERIALIZABLE = 8;序列化:代價最高,最靠譜的級别,可阻止髒讀、不可重複性、或幻讀,但也是最慢的事務隔離級别。
(2)傳播行為
當事務方法被另外一個事務調用時,必須指定事務如何傳播。也就是方法有可能在現有的事務中執行,也有可能開啟一個新事務,在自己的事務中運作。
在TransactionDefinition還提供了七大傳播行為:
- int PROPAGATION_REQUIRED = 0;目前方法必須運作在事務中。若目前事務存在,方法在目前事務中執行;若沒有目前事務,則建立一個事務;
- int PROPAGATION_SUPPORTS = 1;目前方法不需要事務上下文。若目前事務存在,方法則在目前事務中執行;若沒有目前事務,則以非事務性執行;
- int PROPAGATION_MANDATORY = 2;該方法必須在事務中運作。若沒有目前事務,則抛出異常;
- int PROPAGATION_REQUIRES_NEW = 3;目前方法必須運作在自己的事務中。始終建立一個新的事務,若原來有事務,則在執行該方法的時候,把目前事務挂起;
- int PROPAGATION_NOT_SUPPORTED = 4;該方法不運作在事務中,始終以非事務性方式執行,若有目前事務,挂起該事務;
- int PROPAGATION_NEVER = 5;該方法不運作事務中;若有目前事務存在,則引發異常;
- int PROPAGATION_NESTED = 6;若目前事務存在,則該方法将在嵌套事務(可以獨立于目前事務進行單獨的送出或復原)中執行。如果沒有目前事務,則建立一個
(3)隻讀
如果是隻讀事務,那麼資料庫就可以應用它認為合适的優化措施。
//傳回事務是否為隻讀
default boolean isReadOnly() {
return false;
}
(4)事務逾時
事務執行的定時器,如果逾時則自動復原,而不需要一直等待
//傳回事務逾時時間
default int getTimeout() {
return -1;
}
(5)復原規則
預設的隻會在運作異常的時候復原,也可以設定在遇到特定的檢查型異常的時候進行復原。
在Spring中預設實作類為 org.springframework.transaction.support.DefaultTransactionDefinition,裡面提供了一些預設的TransactionDefinition的配置項,當然也可以調用相應的設定方法進行設定。
2.TransactionStatus
事務的狀态資訊。提供簡單的事務執行和查詢事務狀态的方法。在復原或送出的時候需要應用對應的事務狀态。
在Spring中預設實作類為org.springframework.transaction.support.DefaultTransactionStatus。
3.PlatformTransactionManager
(1)作用
事務管理的核心。
為了相容不同架構在處理事務的時候用到的對象不同,特意定義了PlatformTransactionManager接口來統一标準,對不同的架構都要實作該類來進行事務管理。
在Spring中内置的事務管理都繼承了org.springframework.transaction.support.AbstractPlatformTransactionManager,而AbstractPlatformTransactionManager該抽象類實作了PlatformTransactionManager接口。
在PlatformTransactionManager中接口内容如下:
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
主要就是包含一個送出、復原、以及擷取事務狀态。
(2)實作類
在Spring中常用的内置事務管理器,也就是PlatformTransactionManager的實作類有下面三種:
- org.springframework.jdbc.datasource.DataSourceTransactionManager:主要用于處理JDBC事務;
- org.springframework.transaction.jta.JtaTransactionManager:主要用于處理JTA事務;
- org.springframework.orm.hibernate5.HibernateTransactionManager:主要用于對單個org.hibernate.SessionFactory事務支援,用于內建Hibernate架構時的事務管理
(3)執行個體
在優化之前執行個體代碼之前,先了解下在使用DataSourceTransactionManager來管理事務的一個基本流程就是:
- 1)定義一個事務TransactionDefinition,設定裡面的基本屬性
- 2)通過platformTransactionManager.getTransaction(transactionDefinition)來擷取到TransactionStatus,同時開啟事務
- 3)執行增删改查資料庫的代碼
- 4)如果正常執行完代碼,則将該事務通過platformTransactionManager.commit(transactionStatus)
- 5)如果在執行代碼的時候,抛出異常,則将該此事務通過platformTransactionManager.rollback(transactionStatus)進行復原
從這個基本流程中我們可以看到其實PlatformTransactionManager在控制着事務的送出或者復原來滿足事務的原子性。
那麼現在就使用DataSourceTransactionManager來優化在小白新手web開發簡單總結(八)-資料庫HSQLDB執行個體中的com.wj.hsqldb.controller.BookManagerJdbcService中的事務處理的邏輯,優化之後的代碼如下:
@Configuration
@PropertySource("classpath:/config/jdbc.properties")
public class BookManagerPlatformTransactionService {
@Value("${jdbc.table}")
private String jdbcTable = "book";
//1.從xml檔案中擷取DataSourceTransactionManager
@Resource
private DataSourceTransactionManager platformTransactionManager;
private TransactionStatus transactionStatus;
private DefaultTransactionDefinition transactionDefinition;
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void createTransactionManager() {
//2.定義事務的傳播行為以及隔離級别
transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//3.擷取到TransactionStatus,并開啟事務
transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
//4.執行業務邏輯代碼
try {
createBookTableSQL(platformTransactionManager.getDataSource());
//5.根據業務邏輯正常,則将該次事務進行送出
platformTransactionManager.commit(transactionStatus);
} catch (RuntimeException e) {
e.printStackTrace();
//6.出現了異常,則将該次事務進行復原
platformTransactionManager.rollback(transactionStatus);
}
}
/**
* 原建立table的業務邏輯
*
* @param source
*/
private void createBookTableSQL(DataSource source) {
jdbcTemplate = new JdbcTemplate(source);
System.out.println("BookManagerJdbcService 初始化 自動建立表!!!");
String sql = String.format("CREATE TABLE IF NOT EXISTS %s " +
"(id INTEGER PRIMARY KEY , name VARCHAR(50) NOT NULL, price DOUBLE, online DATE)", jdbcTable);
jdbcTemplate.execute(sql);
System.out.println(String.format("BookManagerJdbcService 建立 %s 表", jdbcTable));
}
}
在相比較于之前單純用 JdbcTemplate來進行SQL操作,現在多增加了DataSourceTransactionManager來進行事務管理,最後根據事務是否正常執行來決定是否送出或復原該次事務。在BookManagerJdbcService中的其他方法的優化,可檢視代碼com.wj.hsqldb.controller.service.BookManagerPlatformTransactionService中的相關邏輯,代碼已經更新到github,位址為https://github.com/wenjing-bonnie/sql-web.git,對應的代碼的tag為example12-1(因為項目在一直更新,是以通過打tag的方式來标記這次代碼)
注意一個問題:當對該次事務執行了commit()操作之後,在執行commit()操作會抛出下面的異常:
org.springframework.transaction.IllegalTransactionStateException: Transaction is already completed - do not call commit or rollback more than once per transaction
org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:804)
com.wj.hsqldb.controller.BookManagerPlatformTransactionService.getBook(BookManagerPlatformTransactionService.java:95)
com.wj.hsqldb.controller.BookListController.doGet(BookListController.java:34)
javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
是以目前的解決方案就是在執行一次新的事務的時候,在調用一下開啟事務的方法。
transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
遺留問題:目前不知道這種方式是否正确,後面參照一些像Hibernate,Mybatis等架構的源碼來看下該問題的解決方式。
解決方案:通過 去檢視TransactionTemplate的excute()的源碼,我發現這種處理方式應該是正确的。
4.TransactionTemplate
在3.PlatformTransactionManager中提到的DataSourceTransactionManager來管理事務的方式,比較複雜,需要開發者自行根據正常或異常執行來送出和復原事務,是以在Spring中提供了 org.springframework.transaction.support.TransactionTemplate繼承DefaultTransactionDefinition并實作TransactionDefinition接口,用于簡化事務管理。可以直接通過調用裡面的excute()方法來自動進行送出和復原事務。提供了兩個回調接口來處理業務代碼,而不需要在關系什麼時候送出事務,什麼時候回退事務:
- org.springframework.transaction.support.TransactionCallback:傳回業務方法的傳回值,那麼此時excute()的傳回值就是業務方法的傳回值。
- org.springframework.transaction.support.TransactionCallbackWithoutResult:實作TransactionCallback的抽象類,便于那些不需要傳回值的業務邏輯。
進到execute()的源碼中可以看到,其實就是将上述流程中DataSourceTransactionManager中來處理事務的5個步驟中的2)、4)、5)封裝到該方法中。
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager)this.transactionManager).execute(this, action);
} else {
//1.開啟事務
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result;
//2.在該事務中增加業務邏輯代碼,也就是我們的回調方法
try {
result = action.doInTransaction(status);
} catch (Error | RuntimeException var5) {
//3.異常之後,復原事務
this.rollbackOnException(status, var5);
throw var5;
} catch (Throwable var6) {
//3.異常之後,復原事務
this.rollbackOnException(status, var6);
throw new UndeclaredThrowableException(var6, "TransactionCallback threw undeclared checked exception");
}
//4.正常的話,送出事務
this.transactionManager.commit(status);
return result;
}
}
那麼我們上述的 BookManagerPlatformTransactionService類又可優化成為com.wj.hsqldb.controller.service.BookManageTransactionTemplateService的方式:
Configuration
@PropertySource("classpath:/config/jdbc.properties")
public class BookManageTransactionTemplateService {
@Value("${jdbc.table}")
private String jdbcTable = "book";
//1.從xml檔案中擷取DataSourceTransactionManager
@Resource
private DataSourceTransactionManager platformTransactionManager;
private TransactionTemplate transactionTemplate;
private JdbcTemplate jdbcTemplate;
@PostConstruct
public void createTransactionManager() {
//2.定義事務的傳播行為以及隔離級别
transactionTemplate = new TransactionTemplate(platformTransactionManager);
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//3.調用 transactionTemplate.execute執行業務邏輯
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
createBookTableSQL(platformTransactionManager.getDataSource());
}
});
}
}
相比較于使用DataSourceTransactionManager來管理事務,使用TransactionTemplate直接将DataSourceTransactionManager通過構造函數的方式傳入到TransactionTemplate,隻要執行transactionTemplate.execute()可輕松管理事務了。
在BookManagerPlatformTransactionService中的其他方法的優化,可檢視代碼com.wj.hsqldb.controller.service.BookManageTransactionTemplateService中的相關邏輯,代碼已經更新到github,位址為https://github.com/wenjing-bonnie/sql-web.git,對應的代碼的tag為example12-2(因為項目在一直更新,是以通過打tag的方式來标記這次代碼)
5.簡單總結程式設計式事務管理
通過上面的四個小标題學習了什麼是程式設計式管理。可以有兩種實作方式:
(1)通過PlatformTransactionManager來實作事務的管理,自行去根據業務代碼的執行成功和失敗來進行送出或復原事務;
(2)通過TransactionTemplate來将(1)的方式進行封裝,開發者不需要關心什麼時候送出事務,什麼時候復原事務,隻需要在處理業務邏輯代碼的回調方法來實作相關邏輯即可。要注意要将執行個體化的PlatformTransactionManager對象傳入到TransactionTemplate中。
另外我們看到我們在上述的這些xxxService類中,都會引入 JdbcTemplate 來操作資料庫的時候,其實在Spring中提供了org.springframework.jdbc.core.support.JdbcDaoSupport類來通路資料庫:
public abstract class JdbcDaoSupport extends DaoSupport {
@Nullable
private JdbcTemplate jdbcTemplate;
public JdbcDaoSupport() {
}
public final void setDataSource(DataSource dataSource) {
if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
this.jdbcTemplate = this.createJdbcTemplate(dataSource);
this.initTemplateConfig();
}
}
protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
public final void setJdbcTemplate(@Nullable JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.initTemplateConfig();
}
@Nullable
public final JdbcTemplate getJdbcTemplate() {
return this.jdbcTemplate;
}
。。。。。。
}
其實隻要子類繼承 JdbcDaoSupport就可以擁有JdbcTemplate。但是我們看到該JdbcDaoSupport中的jdbcTemplate并沒有用@Autowired進行标記,是以如果通過注解來加載執行個體的方式,需要對該類進行改造;而如果直接通過xml配置JavaBean的執行個體方式,則可以直接将子類注冊到xml即可。
三 聲明式事務管理
通過程式設計式事務管理每次事務都需要單獨實作,在實際的項目中,業務邏輯都會非常複雜,是以這種方式無疑會造成代碼臃腫,那麼就有了聲明式事務管理。對于聲明使事務管理主要有兩種方式:
- (1)通過xml配置<tx:advice>
- (2)通過注解@Transactional
1.xml配置<tx:advice>
需要經過兩個過程來完成通過xml來聲明事務:
(1)通過在xml配置<tx:advice>來定義事務通知
在這配置<tx:advice>之前,仍然要像程式設計式事務的一樣,在xml配置DataSource、PlatformTransactionManager。然後再就是通過<tx:advice>來配置事務屬性,其中transaction-manager為指定的事務管理器;
通過<tx:attributes>來指定需要攔截的方法(這些方法都是在項目中定義的需要進行加事務的方法)。
<tx:advice id="advice" transaction-manager="xmlTransactionManager">
<tx:attributes>
<!--攔截下面的方法,即我們在業務邏輯中的相關調用資料庫的方法-->
<tx:method name="createBookTable" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="*SQL" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="verifyTransaction" propagation="REQUIRED" isolation="READ_COMMITTED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<tx:method>裡面的每個屬性還是比較好了解的:
- name:就是直接我們在業務邏輯中操作資料庫的需要增加事務的相關方法,支援通配符*;
- propagation、isolation、timeout、read-only :就是和上面的TransactionDefinition提到的屬性的概念一樣;
- rollback-for:需要觸發復原的異常,可以定義多個,用","分開,預設為任何RuntimeException,而Checked Exception将不復原事務;
- no-rollback-for:不被觸發復原的異常,使用同rollback-for
(2)增加切入點
通過<aop:config> 定義哪些包或者哪些類需要切入事務。AOP本質是一種代理模式的實作。
<aop:config> <!--任意傳回類型 com.wj.hsqldb.controller.service下面類中的任意方法 方法可以有任意多的參數-->
<aop:pointcut id="pointcut"
expression="execution(* com.wj.hsqldb.controller.service.BookManagerXmlTransactionService.*(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
</aop:config>
重點說下expression的裡面的這個如下圖:
也就是說将 com.wj.hsqldb.controller.service.BookManagerXmlTransactionService下的所有方法都要切入事務。
注意在使用該裝配方式,需要在pom.xml添加AspectJ的依賴包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
通過上面兩步之後,就可以将 com.wj.hsqldb.controller.service.BookManagerXmlTransactionService下的所有方法添加了事務。
(3)執行個體驗證
通過在https://github.com/wenjing-bonnie/sql-web.git中的項目添加相應的代碼驗證這個裡面類裡面的方法已經添加了事務。對于事務有個很重要的準則就是要麼全部執行,要麼全不執行,那麼我們就可以借助這個特點來驗證事務是否添加成功。
首先需要将項目啟動,之後點選http://localhost:8080/booklist下清單中的任意一行,都會進入到顯示該書的詳細資訊的頁面,也就是進入的com.wj.hsqldb.controller.BookDetailController。在該頁面中會調用了com.wj.hsqldb.controller.service.BookManagerXmlTransactionService#verifyTransaction(Book, String)來驗證是否添加成功。
// @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void verifyTransaction(Book book, String id) {
//在插入之前先查詢下每個表中的資料
System.out.println("====異常資料之前====");
System.out.println(getTableRowCount(jdbcTable, getBookSQL(jdbcTable).size()));
System.out.println(getTableRowCount(xmlTransTable, getBookSQL(xmlTransTable).size()));
//将這個資料加入到原xbook表中,這個時候該操作會抛出異常
//将這個資料插入到hot表中,若增加了事務,則這個操作應該是操作失敗的.
addBookSQL(xmlTransTable, book);
System.out.println("====異常資料報錯之前插入hot表的資料====");
System.out.println(getTableRowCount(xmlTransTable, getBookSQL(xmlTransTable).size()));
System.out.println("====産生異常資料報錯====");
String insert = String.format("INSERT INTO %s \n" +
"(id, name, price, online)\n" +
"VALUES (%d , '%s', %f,'%s' )", jdbcTable, Integer.parseInt(id), book.name, book.price, book.online);
jdbcTemplate.update(insert);
}
代碼的具體邏輯其實通過該方法往兩種表book和hot裡面添加新資料:
- 在插入新資料之前先列印出book表和hot表中的資料數量
- 向hot表添加資料:通過調用 addBookSQL(xmlTransTable, book)來添加新資料;
- 向book表添加資料:因為該book對象是從book表中讀出來的,是以用原來的id插入到book表中的時候,會抛出“ ava.sql.SQLIntegrityConstraintViolationException: integrity constraint violation: unique constraint or index violation”
那麼對于現在這個現象就是:由于向book表添加資料引起該次操作終止,是以因為verifyTransaction()已經交給事務進行管理,那麼如果發生這次中斷之後,hot表的資料不會發生變化。
通過點選http://localhost:8080/booklist下清單中的任意一行,進入到下一個頁面,報錯,然後在傳回到http://localhost:8080/booklist,在點選一次,再次進入到該頁面,我們看下打出來的日志内容如下:
從日志中可以看出來,由于加入了事務管理,是以在插入book表的資料報錯的時候,會将hot表的資料復原。
2.通過注解@Transactional
通過注解@Transactional的方式要比xml配置的方式,xml配置的内容少點,代替配置<tx:advice>和<aop:config> ,隻需要下面的兩步内容:
(1)在xml中配置 <tx:annotation-driven>
<tx:annotation-driven transaction-manager="xmlTransactionManager"/>
(2)在相應需要事務管理的方法上添加 @Transactional
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public void verifyTransaction(Book book, String id) {
}
其中 @Transactional裡面的一些設定,同上的含義一緻。
具體的可參照https://github.com/wenjing-bonnie/sql-web.git下的相應代碼,對應的代碼的tag為example12-3(因為項目在一直更新,是以通過打tag的方式來标記這次代碼)。
四 總結
終于把事務管理的基本内容給看完了。當然這隻是基本的,後面還需要在去看下什麼是JTA事務,那些Hibernate,Mybatis等比較好的架構還需要繼續學習下。
1.所謂的事務簡單的說就是一些動作,要麼全部執行,要麼全部不執行,若執行了一半,被中止,那麼全部還原到初始狀态;
2.Java EE中提供了兩種事務:一種是JDBC事務,一種是JTA事務。針對這兩種事務有一些輕量封裝的架構如Hibernate,Mybatis等;
3.事務的四個原則:原子性、隔離性、一緻性和持久性;
4.事務管理方式有兩種:程式設計式事務管理和聲明式事務管理;
5.在Spring中為了相容不同架構對事務的處理,定義了PlatformTransactionManager來統一接口,不同的架構提供的事務管理需要實作該接口;
6.在Spring預設的有兩種實作類來處理JDBC事務和JTA事務;
7.在一個事務管理過程主要分為:
- 定義一個事務TransactionDefinition
- 通過PlatformTransactionManager擷取事務的狀态TransactionStatus,同時開啟事務
- 在該事務中執行業務代碼邏輯
- 如果該業務邏輯沒有異常,則送出事務;如果出現異常,則復原事務
8.當然Spring也提供了一個對事務管理的封裝類TransactionTemplate,通過該類隻需要将執行個體化的PlatformTransactionManager傳入即可,就可以直接通過execute()方法來執行業務邏輯代碼;
9.聲明式事務又分為兩種:一種通過xml配置;一種通過@Transactional;
10.使用xml配置事務的事務時,要添加AspectJ的依賴。
下一篇去總結一下 Hibernate,Mybatis的使用。加油!!!