溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring中事務(wù)機(jī)制的示例分析

發(fā)布時(shí)間:2021-07-21 14:12:42 來源:億速云 閱讀:186 作者:小新 欄目:編程語言

這篇文章將為大家詳細(xì)講解有關(guān)Spring中事務(wù)機(jī)制的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

具體如下。

JAVA EE傳統(tǒng)事務(wù)機(jī)制

通常有兩種事務(wù)策略:全局事務(wù)和局部事務(wù)。全局事務(wù)可以跨多個(gè)事務(wù)性資源(即數(shù)據(jù)源,典型的是數(shù)據(jù)庫和消息隊(duì)列),通常都需要J2EE應(yīng)用服務(wù)器的管理,其底層需要服務(wù)器的JTA支持。而局部事務(wù)則與底層采用的持久化技術(shù)有關(guān),如果底層直接使用JDBC,需要用Connection對象來操事務(wù)。如果采用Hibernate持久化技術(shù),則需要使用session對象來操作事務(wù)。

通常的,使用JTA事務(wù),JDBC事務(wù)及Hibernate事務(wù)的編程流程大致如下,

Spring中事務(wù)機(jī)制的示例分析

上圖也可以看出,采用傳統(tǒng)事務(wù)編程,程序代碼必須和具體的事務(wù)策略的API耦合,如果應(yīng)用需要切換一種策略,意味著需要大幅修改代碼。但是如果使用Spring事務(wù)的話,就不會有這個(gè)問題了。

Spring事務(wù)機(jī)制

Sring沒有提供任何事務(wù)支持,它只是負(fù)責(zé)包裝底層的事務(wù),而在Spring層面,對外提供統(tǒng)一的編程API。Spring事務(wù)的核心是PlatformTransactionManager接口,

PlatformTransactionManager代表與具體類型無關(guān)的事務(wù)接口,可以代表任何事務(wù),包括JDBC事務(wù),Hibernate事務(wù),甚至是JTA事務(wù)。

Springa事務(wù)機(jī)制是一種典型的策略模式,PlatformTransactionManager代表事務(wù)管理接口,但它并不知道到底如何管理事務(wù),它只要求事務(wù)管理提供開始事務(wù)getTransaction(),提交事務(wù)commit()和回滾事務(wù)rollback()這三個(gè)方法,但具體如何實(shí)現(xiàn)則交給其實(shí)現(xiàn)類完成。編程人員只需要在配置文件中根據(jù)具體需要使用的事務(wù)類型做配置,Spring底層就自動(dòng)會使用具體的事務(wù)實(shí)現(xiàn)類進(jìn)行事務(wù)操作,而對于程序員來說,完全不需要關(guān)心底層過程,只需要面向PlatformTransactionManager接口進(jìn)行編程即可。PlatformTransactionManager接口中提供了如下方法:getTransaction(..), commit(); rollback(); 這些都是與平臺無關(guān)的事務(wù)操作。

getTransaction()的完整寫法為 TransactionStatus getTransaction(TransactionDefinition definiton)

這個(gè)方法用來返回一個(gè)事務(wù)對象,其中的參數(shù)TransactionDefinition 則可以為事務(wù)對象指定各種屬性,通??梢灾付?事務(wù)的隔離屬性, 傳播屬性, 超時(shí),只讀 這幾個(gè)屬性。

Spring具體的事務(wù)管理需要在配置文件中配置好PlatformTransactionManager,下面是不同類型的事務(wù)對應(yīng)的Spring配置。

JDBC數(shù)據(jù)源的局部事務(wù)管理器的配置如下,

<!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn),并注入數(shù)據(jù)源的必要信息 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSrouce"
    destroy-method="close"
    p:driverClass="com.mysql.jdbc.Driver"
    p:jdbcUrl="jdbc:mysql://localhost/test"
    p:user="root"
    p:password=""
    p:maxPoolSize="40"
    p:minPoolSize="2"
    p:initialPoolSize="2"
    p:maxIdleTime="30" />
  <!-- 配置JDBC數(shù)據(jù)源的局部數(shù)據(jù)管理器,使用DataSourceTransactionManager類 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    p:dataSource-ref="dataSource" />

容器管理的JTA全局事務(wù)管理器的配置如下,

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" 
  p:jndiName="jdbc/jpetstore" />
<!-- 使用JtaTransactionManager類, 該類實(shí)現(xiàn)了PlatformTransactionManager接口 -->
<!-- 使用JTA全局事務(wù),Spring容器可以自行從Java EE服務(wù)器中獲取事務(wù)性資源,無需依賴注入 -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

對于JTA全局事務(wù),只需要指定事務(wù)管理器的實(shí)現(xiàn)類JtaTransactionManager即可,Spring容器會自行從J2EE服務(wù)器獲取數(shù)據(jù)源,無需顯式注入進(jìn)事務(wù)管理器。

基于Hibernate持久化技術(shù)的Spring局部事務(wù)配置如下,

<!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn),并注入數(shù)據(jù)源的必要信息 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSrouce"
    destroy-method="close"
    p:driverClass="com.mysql.jdbc.Driver"
    p:jdbcUrl="jdbc:mysql://localhost/test"
    p:user="root"
    p:password=""
    p:maxPoolSize="40"
    p:minPoolSize="2"
    p:initialPoolSize="2"
    p:maxIdleTime="30" />
  <!-- 定義Hibernate的SessionFactory, SessionFactory需要依賴數(shù)據(jù)源,注入dataSource -->
  <bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
    p:dataSource-ref="dataSource">
    <!-- annotatedClasses用來列出全部持久化類 -->  
    <property name="annotatedClasses">
      <list>
        <!-- 以下用來列出所有PO類 -->
        <value>com.entity.User</value>
      </list>
    </property>
    <!-- 定義Hibernate的sessionFactory屬性 -->
    <property name="hibernateProperties">
      <props>
        <!-- 指定Hibernate的連接方言 -->
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
        <!-- 是否根據(jù)Hibernate映射表創(chuàng)建數(shù)據(jù)表 -->
        <prop key="hibernate.hbm2ddl.auto">update</prop>
      </props>
    </property>
  </bean>
  <!-- 配置Hibernate的局部數(shù)據(jù)管理器,使用HibernateTransactionManager類 -->
  <!-- 該類是PlatformTransactionManager接口針對Hibernate的特定實(shí)現(xiàn) -->
  <!-- 配置HibernateTransactionManager需要注入sessionFactory -->
  <bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager"
    p:sessionFactory-ref="sessionFactory" />

Spring事務(wù)如果采用Hibernate策略,一般需要配置三點(diǎn):數(shù)據(jù)源, sessionFactory, 事務(wù)管理器。

如果底層采用Hibernate持久層技術(shù),而事務(wù)采用JTA全局事務(wù)時(shí),配置如下,

<!-- 配置JTA數(shù)據(jù)源-->
  <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" 
    p:jndiName="jdbc/jpetstore" />
  <!-- 定義Hibernate的SessionFactory, SessionFactory需要依賴數(shù)據(jù)源,注入dataSource -->
  <bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
    p:dataSource-ref="dataSource">
    <!-- annotatedClasses用來列出全部持久化類 -->  
    <property name="annotatedClasses">
      <list>
        <!-- 以下用來列出所有PO類 -->
        <value>com.entity.User</value>
      </list>
    </property>
    <!-- 定義Hibernate的sessionFactory屬性 -->
    <property name="hibernateProperties">
      <props>
        <!-- 指定Hibernate的連接方言 -->
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
        <!-- 是否根據(jù)Hibernate映射表創(chuàng)建數(shù)據(jù)表 -->
        <prop key="hibernate.hbm2ddl.auto">update</prop>
      </props>
    </property>
  </bean>
  <!-- 使用JtaTransactionManager類,該類是PlatformTransactionManager接口的實(shí)現(xiàn)類 -->
  <!-- 針對全局事務(wù)管理的特定實(shí)現(xiàn) -->
  <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

這與前面的基于Hibernate的Spring事務(wù)比起來,就是將數(shù)據(jù)源換成了JNDI數(shù)據(jù)源, 將事務(wù)管理器換成了JtaTransactionManager.

對于JTA全局事務(wù),因?yàn)樾枰讓討?yīng)用服務(wù)器的支持,而不同應(yīng)用服務(wù)器所提供的JTA全局事務(wù)可能存在細(xì)節(jié)上的差異,因此實(shí)際配置全局事務(wù)管理器時(shí)可能需要使用JtaTransactionManager的子類,例如Oracle的JavaEE應(yīng)用服務(wù)器提供的OC4JJtaTransactionManager,Oracle為WebLogic提供的WebLogicJtaTransactionManager, IBM為WebSphere提供的WebSphereUowTransactionManager等。

從上面各種事務(wù)類型的Spring配置可以看出,當(dāng)應(yīng)用程序采用Spring事務(wù)管理時(shí),應(yīng)用程序無需與具體的事務(wù)API耦合,應(yīng)用程序只需要面向PlatormTransactionManager接口編程即可,ApplicationContext會根據(jù)配置文件選擇合適的事務(wù)策略實(shí)現(xiàn)類(即PlatormTransactionManager的實(shí)現(xiàn)類)。

那么在具體在Spring中如何進(jìn)行事務(wù)控制編程呢,通常有兩種方式,

編程式事務(wù)管理:就是直接在代碼中使用PlatormTransactionManager提供的三個(gè)抽象方法進(jìn)行事務(wù)流程控制。也可以在Spring容器中獲取PlatormTransactionManager類型的Bean,該Bean總是PlatormTransactionManager的具體實(shí)現(xiàn)類的實(shí)例,具體的實(shí)現(xiàn)類則由ApplicationContext按照策略模式進(jìn)行選擇,編程人員無需關(guān)心,只需要面向接口編程即可。

聲明式事務(wù)管理:這種方式不需要講事務(wù)控制流程寫入代碼中,而是通過AOP的方式,完全由配置文件完成事務(wù)的織入。即XML配置文件可以為業(yè)務(wù)組件配置事務(wù)代理,事務(wù)代理為業(yè)務(wù)組件提供事務(wù)控制。現(xiàn)在這種方式是最好的,源碼侵入性最低。

使用聲明式事務(wù)管理-使用XML Schema配置事務(wù)策略

當(dāng)使用聲明式事務(wù)時(shí),只需要寫好配置文件,配置需要事務(wù)控制的組件種類,業(yè)務(wù)組件就會在AOP機(jī)制下被織入事務(wù)控制,而編程人員不需要寫任何事務(wù)管理代碼,可以專注于業(yè)務(wù)組件的開發(fā)。因此通常都推薦使用聲明式事務(wù)管理。

Spring的XML Schema方式提供了簡潔的事務(wù)配置策略,通過命名空間 <tx:advice> 來配置一個(gè)事務(wù)增強(qiáng)處理,其中可以指定事務(wù)的各種屬性(例如隔離屬性, 傳播屬性, 超時(shí),只讀屬性等等),然后通過<aop:config>標(biāo)簽可以將事務(wù)的增強(qiáng)與AOP的切入點(diǎn)(即Bean的執(zhí)行方法)進(jìn)行綁定,從而實(shí)現(xiàn)對Bean的方法織入事務(wù)操作。下面是一個(gè)簡單的例子,配置一個(gè)NewsDaoImpl bean進(jìn)行數(shù)據(jù)操作,使用c3p0數(shù)據(jù)源,Spring的JDBC事務(wù)管理器,在<tx:advice對事務(wù)設(shè)置屬性。

完整的Spring配置如下,

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
  http://www.springframework.org/schema/tx
  http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
  <!-- 定義數(shù)據(jù)源Bean,使用C3P0數(shù)據(jù)源實(shí)現(xiàn),并注入數(shù)據(jù)源的必要信息 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close"
    p:driverClass="com.mysql.jdbc.Driver"
    p:jdbcUrl="jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8"
    p:user="root"
    p:password=""
    p:maxPoolSize="40"
    p:minPoolSize="2"
    p:initialPoolSize="2"
    p:maxIdleTime="30" />
  <!-- 配置JDBC數(shù)據(jù)源的局部數(shù)據(jù)管理器,使用DataSourceTransactionManager類 -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    p:dataSource-ref="dataSource" />
  
  <!-- 配置一個(gè)業(yè)務(wù)邏輯Bean -->
  <bean id="newsDao" class="com.dao.impl.NewsDaoImpl" p:ds-ref="dataSource" />
  <!-- 配置事務(wù)增強(qiáng)處理, 指定事務(wù)管理器 -->
  <tx:advice id="txAdvice"
    transaction-manager="transactionManager">
    <!-- 用于配置詳細(xì)的事務(wù)定義 -->
    <tx:attributes>
      <!-- 所有以get開頭的方法都是只讀的 -->
      <tx:method name="get*" read-only="true" />
      <!-- 其他方法默認(rèn)都適用事務(wù),指定超時(shí)5秒 -->
      <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5" />
    </tx:attributes>
  </tx:advice>
  <aop:config>
    <!-- 配置一個(gè)切入點(diǎn),匹配impl包下所有以impl結(jié)尾的類里的所有方法的執(zhí)行 -->
    <aop:pointcut expression="execution(* com.dao.impl.*Impl.*(..))" id="myPointcut" />
    <!-- 將切入點(diǎn)myPointcut和增強(qiáng)txAdvice綁定-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" />
    <!-- 再配置一個(gè)切入點(diǎn),匹配impl包下所有以abc開頭類里的所有方法的執(zhí)行 -->
  </aop:config>
</beans>

NewsDaoImpl代碼中,則是插入重復(fù)數(shù)據(jù)到表中,

package com.dao.impl;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import com.dao.NewsDao;
public class NewsDaoImpl implements NewsDao {
	private DataSource ds;
	public void setDs(DataSource ds) {
		this.ds = ds;
	}
	@Override
	  public void insert(String title, String content) {
		//c3p0數(shù)據(jù)池的用法
		JdbcTemplate jt = new JdbcTemplate(ds);
		jt.update("insert into news_inf" + " values(100,?,?)", title, content);
		jt.update("insert into news_inf" + " values(100,?,?)", title, content);
		//如果沒有事務(wù)控制,則第一條記錄可以被插入
		//如果增加事務(wù)控制,將發(fā)現(xiàn)第一條記錄也插不進(jìn)去
	}
}

下面是測試方法,

public static void test3() {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans4JDBC.xml");
    //獲取事務(wù)代理Bean
    NewsDao dao = (NewsDao)ctx.getBean("newsDao", NewsDao.class);
    dao.insert("java編程核心思想", "輕量級Java EE開發(fā)");
    System.out.println("執(zhí)行完畢");
  }

執(zhí)行測試方法會發(fā)現(xiàn)拋出異常(因?yàn)橛兄貜?fù)數(shù)據(jù)),而又因?yàn)槭聞?wù)控制,數(shù)據(jù)庫中講不會有數(shù)據(jù)插入。

可以看到上面例子中,通常對于XML Schema的配置中,其實(shí)就是對一個(gè)普通的Bean做了AOP配置,織入一個(gè)advice增強(qiáng),而advice增強(qiáng)中則配置一個(gè)事務(wù)管理器,事務(wù)管理器又依賴數(shù)據(jù)源。

對于<aop:advisor>中,將advice和切入點(diǎn)的綁定,而在Spring底層是由Bean后處理器完成(例如BeanNameAutoProxyCreator, DefaultAdvisorAutoProxyCreator),其本質(zhì)就是動(dòng)態(tài)代理。

另外,在<tx:advice>配置增強(qiáng)中,還可以為事務(wù)指定再遇到特定異常時(shí),進(jìn)行強(qiáng)制rollback和強(qiáng)制不rollback,即rollback-for="xxxException", no-rollback-for="xxxException"

使用@Transactionl

除了使用XML Schema的方法之外,也可以直接在方法上添加@Transaction注解,使這個(gè)方法具有事務(wù)屬性。 在@Transaction中可以為事務(wù)配置各種屬性(例如隔離屬性, 傳播屬性, 超時(shí),只讀屬性等等),此外,還需要在在XML配置中加入<tx:annotation-triven配置表明Spring會根據(jù)注解來配置事務(wù)代理,這樣,事務(wù)的屬性配置和AOP切入配置就可以只通過一步(直接通過注解配置在方法名上)完成了。

<tx:annotation-driven transaction-manager="transactionManager" />

NewsDaoImpl.

@Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.DEFAULT, timeout=5)
@Override
public void insert(String title, String content) {

關(guān)于“Spring中事務(wù)機(jī)制的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI