Spring v3.0.2 Learning Note 10 - Annotation-based Transaction Management

基於註解的事務管理

  • 配置數據源:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <context:property-placeholder location="classpath:jdbc.properties" /> // 佔位符的設置
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${driverClassName}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <!-- 連接池啓動時的初始值 -->
        <property name="initialSize" value="${initialSize}" />
        <!-- 連接池的最大值 -->
        <property name="maxActive" value="${maxActive}" />
        <!-- 最大空閒值.當經過一個高峯時間後,連接池可以慢慢將已經用不到的連接慢慢釋放一部分,一直減少到maxIdle爲止 -->
        <property name="maxIdle" value="${maxIdle}" />
        <!--  最小空閒值.當空閒的連接數少於閥值時,連接池就會預申請去一些連接,以免洪峯來時來不及申請 -->
        <property name="minIdle" value="${minIdle}" />
    </bean>

    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <tx:annotation-driven transaction-manager="txManager" />

    <bean id="personService" class="com.spring.test.manager.impl.PersonManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>

jdbc.properties內容:
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc/:mysql/://localhost/:3306/myproj?useUnicode/=true& characterEncoding/=UTF-8
username=root
password=123456
initialSize=1
maxActive=500
maxIdle=2
minIdle=1
 注意,如果直接在spring的XML配置文件中配置URL,其寫法爲(注意&的寫法)
 <property name="url" value="jdbc:mysql://localhost:3306/myproj?useUnicode=true&amp; characterEncoding=UTF-8"/>
  • 配置事務,需要在xml配置文件中引入聲明事務的tx命名空間,支持註解的方式和XML配置方式。

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        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-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

  • 註解方式用法

<bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
</bean>
<!-- 指定採用@Transactional 註解的方式使用事務 -->

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

注意:@Transactional只能用於public方法上。如果用在private、protected,系統不會抱錯,但是配置的事務設置將失效。如果一定要在非public方法上用這個註解,需要引入AspectJ.
  • 基本用法

@Service @Transactional
public class PersonDao implements IPersonDao {
//.......
}

  • Spring事務管理的傳播屬性

REQUESTED:如果方法運行時,已經處在一個事務中,那麼加入到該事務中來,否則爲自己創建一個新事務。
NOT_SUPPORTED:聲明方法不需要事務。如果方法沒有關聯到一個事務,容器不會爲它開啓事務。如果方法在一個事務中被調用,該事物會被掛起,在方法調用結束後,原先的事務恢復運行。
REQUEST_NEW:不管是否存在事務,業務方法總會爲自己發起一個新的事務。如果方法運行在一個事務中,則原有事務掛起,新的事務創建,直到方法執行結束,新事務纔算結束,原先的事務纔會恢復執行。
MANDATORY:指定業務方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。如果業務方法在沒有事務的環境下調用,容器就會拋出異常。
SUPPORTS:如果業務方法在某個事務範圍內被調用,則方法稱爲該事物的一部分。如果業務方法在事務範圍外被調用,則方法在沒有事務的環境下執行。
NEVER:指定業務方法不能再事務範圍內執行。如果業務方法在某個事務中執行,容器會拋出例外,如果業務方法沒有關聯到任何事務,才能正常執行。
NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中,如果沒有活動事務,則按REQUESTED屬性執行。它使用了一個獨立的事務,這個事務擁有多個可以回滾的保存點。內部事務的回滾不會影響到外部事務。

  • Spring容器在默認狀態下,碰到unchecked-exception會回滾,checked-exception則不會

public void delete(Integer personid) {
        jdbcTemplate.update("delete from person where id=?", new Object[]{personid},
                new int[]{java.sql.Types.INTEGER});






         throw new RuntimeException("This is unchecked-exception.");
}
運行此方法,默認狀態下,數據庫中的數據不會刪除,spring容器會回滾事務。


public void delete(Integer personid) throws Exception {
        jdbcTemplate.update("delete from person where id=?", new Object[]{personid},
                new int[]{java.sql.Types.INTEGER});

         throw new Exception("This is checked-exception.");
}
運行此方法,默認狀態下,數據庫中的數據會被刪除,spring容器不會回滾事務。

修改默認情況,指定事務中碰到某個checked-exception,spring容器會回滾事務。

 @Transactional(rollbackFor=Exception.class)

public void delete(Integer personid) throws Exception {
        jdbcTemplate.update("delete from person where id=?", new Object[]{personid},
                new int[]{java.sql.Types.INTEGER});

         throw new Exception("This is checked-exception.");
}運行此方法,spring容器會回滾事務。

同理,也可修改屬性,使得事務中碰到unchecked-exception不回滾事務。

 @Transactional(noRollbackFor=RuntimeException.class)

public void delete(Integer personid) {
        jdbcTemplate.update("delete from person where id=?", new Object[]{personid},
                new int[]{java.sql.Types.INTEGER});

         throw new RuntimeException("This is unchecked-exception.");
}

  • 註解中指定傳播屬性

默認狀態下,數據庫事務的傳播屬性爲REQUIRED。
可以按如下方法明確指定傳播屬性。
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public List<Person> getPersons() {
        return (List<Person>)jdbcTemplate.query("select * from person", new PersonRowMapper());
}

  • 隔離級別

數據庫系統提供了四種隔離級別,不同的隔離級別採用不同的鎖類型來實現。大多數據庫系統默認的隔離級別爲Read Committed。
Read Uncommitted:讀未提交的數據,會出現髒讀,不可重複讀和幻讀
Read Committed:讀已提交的數據,會出現不可重複讀和幻讀
Repeatable Read:可重複讀,會出現幻讀
Serializable:最高隔離級別,效率也最低,不會出現以上的髒讀,不可重複讀和幻讀。

髒讀:一個事務讀到另一個未提交事務的更新數據。
不可重複讀:在同一個事物中,多次讀取同一數據返回的結果不同。即後續讀取可以讀到另一個事務已提交的更新事務。
幻讀:一個事務讀到另一個事務已提交的insert數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章