Spring學習筆記-事務管理

Spring學習筆記-事務管理

Spring支持兩種事務管理的方式:

編程式的事務管理,在實際應用中很少使用,通過TransactionTemplate手動管理事務。

聲明式的事務管理,使用XML配置聲明,開發中推薦使用(代碼侵入性最小),Spring的聲明式事務是通過AOP實現的。

什麼是事務

事務指的是邏輯上的一組操作,這組操作要麼全都成功,要麼全都失敗。最典型的例子就是銀行轉賬的問題。

事務的特性:

原子性:事務是不可分割的工作單位

一致性:事務前後數據的完整性必須保持一致

隔離性:多個併發事務數據要相互隔離

持久性:事務一旦提交,它對數據庫的改變是永久性的

Spring事務接口介紹

Spring事務管理高層抽象接口主要包括3個接口:

1、PlatformTransactionManager事務管理器

Spring爲不同的持久層框架提供了不同的PlatformTransactionManager接口實現。如JDBC、Mybatis使用DataSourceTransactionManager,Hibernate用HibernateTransactionManager等。

2、TransactionDefinition事務定義信息

包含(隔離級別、傳播行爲、超時、是否只讀)等一些信息。

若不考慮隔離性,將會造成髒讀、不可重複讀、幻讀等。

事務的傳播行爲主要解決service層裏方法的相互調用,解決事務是如何傳遞的。

3、TransactionStatus事務具體運行狀態


事務隔離級別:

事務的傳播行爲:

測試環境搭建

applicationContext.xml

<context:annotation-config/>

<context:component-scan base-package="com"></context:component-scan>

<!--引入配置文件-->
<!--1.配置數據庫相關參數&ndash;&gt;-->
<context:property-placeholder location="classpath:jdbc.properties"/>

<!--2.數據庫連接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <!--配置連接池屬性-->
    <property name="driverClass" value="${jdbc_driver}" />

    <!-- 基本屬性 urluserpassword -->
    <property name="jdbcUrl" value="${jdbc_url}" />
    <property name="user" value="${jdbc_username}" />
    <property name="password" value="${jdbc_password}" />

    <!--c3p0私有屬性-->
    <property name="maxPoolSize" value="30"/>
    <property name="minPoolSize" value="10"/>
    <!--關閉連接後不自動commit-->
    <property name="autoCommitOnClose" value="false"/>

    <!--獲取連接超時時間-->
    <property name="checkoutTimeout" value="1000"/>
    <!--當獲取連接失敗重試次數-->
    <property name="acquireRetryAttempts" value="2"/>
</bean>

 

Dao層

@Repository
public interface AccountDao {
    void outMoney(String out, Double money);

    void inMoney(String in, Double money);
}

 

@Repository("AccountDao")
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {

    @Autowired
    private ComboPooledDataSource dataSource;

    /**解決
     * java.lang.IllegalArgumentException:
     * 'dataSource' or 'jdbcTemplate' is required
     * 的問題。
     * 繼承了JdbcDaoSupport注入dataSource出現null的情況
     */
    @PostConstruct
    private void initialize() {
        setDataSource(dataSource);
    }

    public void outMoney(String out, Double money) {
        String sql = "update test set money = money - ? where name = ?";
        this.getJdbcTemplate().update(sql, money, out);
    }

    public void inMoney(String in, Double money) {
        String sql = "update test set money = money + ? where name = ?";
        this.getJdbcTemplate().update(sql, money, in);
    }
}

Service層

@Service
public interface AccountService {
    void transfer(String out, String in, Double money);
}

 

@Service("AccountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    public void transfer(String out, String in, Double money) {
        System.out.println(out+""+in+"轉賬"+money);
        accountDao.outMoney(out,money);
        //出現異常
        //若沒有事務處理,這時out賬戶錢轉出,但是in賬戶錢未轉入
        int i = 1/0;
        accountDao.inMoney(in,money);
    }
}

 

Main.java

在未進行事務處理時,會出現aaa中錢減少,bbb中錢未增加的情況。

public class Main {
    public static void main(String[] args){
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "applicationContext.xml");
        AccountService accountService = (AccountService) context.getBean("AccountService");
        accountService.transfer("aaa","bbb",100.0);
    }
}

 

編程式事務控制

Spring提供了事務管理的模板TransactionTemplate,在哪裏使用事務就在哪裏注入這個模板。

在applicationContext.xml文件中添加如下代碼,配置事務管理器和模板。

<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入數據庫連接池-->
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置事務管理器的模板:Spring爲了簡化事務管理的代碼而提供的類-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

 

編寫AccountServiceImpl2.java,進行編程式事務處理

@Service("AccountService2")
public class AccountServiceImpl2 implements AccountService {
    @Autowired
    private AccountDao accountDao;

    //注入事務管理模板
    @Autowired
    private TransactionTemplate transactionTemplate;

    //使用匿名內部類,傳入的參數需要final
    public void transfer(final String out, final String in, final Double money) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                //使用匿名內部類實現編程式事務的處理
                System.out.println(out + "" + in + "轉賬" + money);
                accountDao.outMoney(out, money);
                //出現異常
                //若沒有事務處理,這時out賬戶錢轉出,但是in賬戶錢未轉入
                int i = 1 / 0;
                accountDao.inMoney(in, money);
            }
        });

    }
}

 

測試

AccountService accountService2 = (AccountService) context.getBean("AccountService2");
accountService2.transfer("aaa","bbb",100.0);

出現異常時不會出現aaa中錢減少而bbb中錢未增加的情況,出現異常進行了事務的一個回滾。

 

這種方式修改代碼較多,不推薦這種方式,推薦使用下面的聲明式事務的方式。

聲明式事務控制

基於AOP思想進行處理。

方法一,基於TransactionProxyFactoryBean

配置applicationContext.xml,添加TransactionProxyFactoryBean的相關配置。

這裏是通過代理實現功能的增強,基於AOP攔截規則。

這裏說明一下prop的格式 :

PROPAGATION,ISOLATION,readOnly,-Exception,+Exception分別代表

傳播行爲(7種),隔離級別,只讀事務,發生這些異常回滾,發生這些異常提交事務

<!--聲明式事務-->

<!--方法一:基於TransactionProxyFactoryBean-->
<!--配置業務層代理-->
<bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!--配置代理的目標對象-->
    <property name="target" ref="AccountService"/>
    <!--注入事務管理器-->
    <property name="transactionManager" ref="transactionManager"/>
    <!--注入事務屬性-->
    <property name="transactionAttributes">
        <props>
            <prop key="insert*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <!--這裏配置對應的方法進行AOP攔截,*攔截所有,這裏測試transfer方法-->
            <!--設置隔離級別-->
            <prop key="transfer">PROPAGATION_REQUIRED</prop>
            <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
        </props>
    </property>
</bean>

AccountServiceImpl不用修改任何代碼,在測試類中獲得的bean是代理類的對象。

AccountService accountService3 = (AccountService) context.getBean("accountServiceProxy");
accountService3.transfer("aaa","bbb",100.0);

方法二,使用XML配置聲明事務(原始方式)

配置applicationContext.xml,添加聲明事務。

<!--方法二:使用XML配置聲明事務(原始方式)-->
<!--配置事務的通知:(事務的增強)-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!--下面屬性分別爲傳播行爲,隔離級別,只讀,發生這些異常回滾,發生這些異常提交事務-->
        <tx:method name="transfer"
                   propagation="REQUIRED"
                   isolation="DEFAULT"
                   read-only="false"
                   rollback-for=""
                   no-rollback-for=""/>
    </tx:attributes>
</tx:advice>
<!--配置切面-->
<aop:config>
    <!--切入點配置-->
    <aop:pointcut id="pointcut1" expression="execution(* com.service.AccountService+.*(..))"/>
    <!--配置切面-->
    <!--pointcut1這個切入點上使用txAdvice這個增強-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>

也是基於AOP攔截規則,其他不用修改任何代碼,在測試時直接調用原來的方法。

AccountService accountService = (AccountService) context.getBean("AccountService");
accountService.transfer("aaa","bbb",100.0);

方法三,使用註解

配置applicationContext.xml,添加基於註解的聲明事務

<!--方法三:配置基於註解的聲明式事務-->
<tx:annotation-driven transaction-manager="transactionManager"/>

然後在需要進行事務處理的類或方法上使用@Transactional註解

//同樣有傳播行爲,隔離級別等屬性進行設置
@Transactional(propagation = Propagation.REQUIRED,
                isolation = Isolation.DEFAULT)
public void transfer(String out, String in, Double money) {
    System.out.println(out+""+in+"轉賬"+money);
    accountDao.outMoney(out,money);
    //出現異常
    //若沒有事務處理,這時out賬戶錢轉出,但是in賬戶錢未轉入
    int i = 1/0;
    accountDao.inMoney(in,money);
}

 

總結

編程式事務管理:手動編寫代碼,很少使用

聲明式事務管理:

l  方法一:基於TransactionProxyFactoryBean方式,需要爲每個進行事務管理的類配置一個TransactionProxyFactoryBean進行增強,很少使用。

l  方法二:基於AspectJ的XML方式,一旦配置好,類上不需要添加任何東西,經常使用。

l  方法三:基於註解方式,配置簡單,需要在業務層上添加@Transactional註解,經常使用。

發佈了75 篇原創文章 · 獲贊 49 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章