1. 程式人生 > >spring深入學習(五)-----spring dao、事務管理

spring深入學習(五)-----spring dao、事務管理

manager ade 擴展 註解 have requires pda 包括 getname

訪問數據庫基本是所有java web項目必備的,不論是oracle、mysql,或者是nosql,肯定需要和數據庫打交道。一開始學java的時候,肯定是以jdbc為基礎,如下:

private static int insert(Student student) {
    String driver = "com.mysql.jdbc.Driver";
    String url = "jdbc:mysql://localhost:3306/samp_db";
    String username = "root";
    String password = "";
    Connection conn 
= null; int i = 0; String sql = "insert into students (Name,Sex,Age) values(?,?,?)"; PreparedStatement pstmt; try { Class.forName(driver); //classLoader,加載對應驅動 conn = (Connection) DriverManager.getConnection(url, username, password); pstmt = (PreparedStatement) conn.prepareStatement(sql); pstmt.setString(
1, student.getName()); pstmt.setString(2, student.getSex()); pstmt.setString(3, student.getAge()); i = pstmt.executeUpdate(); pstmt.close(); conn.close(); } catch (Exception e) { e.printStackTrace(); } return i; }

spring對dao層提供了不同的模板類,主要如下;

技術分享圖片

主要機制如下:

技術分享圖片

數據源

在spring中,數據連接是通過數據源獲得的。數據源一般是由web應用服務器提供,spring中,通過jndi的方式獲取,也可以直接在spring容器中配置,當然也可以通過代碼的方式創建一個數據源。

1、dbcp數據源

jar包依賴:

commons-dbcp2-2.1.1.jar
commons-logging-1.2.jar
commons-pool2-2.4.2.jar

配置如下:

技術分享圖片

2、c3p0數據源

jar包依賴:

mchange-commons-java-0.2.11.jar
c3p0-0.9.5.2.jar

配置如下:

技術分享圖片

3、jndi數據源

如果應用配置在高性能的應用服務器上,那麽可能希望使用應用服務器本身提供的數據源。應用服務器的數據源使用JNDI開放調用者使用,spring專門提供了引用jndi數據源的JndiObjectFactoryBean,配置如下:

技術分享圖片

另外spring為獲取java ee資源提供了一個jee命名空間,通過jee可以有效的簡化java ee資源的引用。利用jee命名空間引用jndi數據源的配置如下:

技術分享圖片

4、DriverManagerDataSource(spring提供)

技術分享圖片

事務

針對事務,本人準備在後續數據庫章節專門進行闡述,這裏只要知道事務是為了保證一系列的操作要麽同時成功,要麽同時失敗的機制。

jdbc事務操作:

技術分享圖片

在jdbc3.0之後,引入了savepoint的概念,流程如下;

技術分享圖片

技術分享圖片

spring對事務的支持

spring事務管理的亮點在於聲明式事務管理,主要包括如下幾個元素:

1、TransactionDefinition
定義了spring兼容的事務屬性,包括事務隔離、事務傳播、事務超時、只讀狀態等等。
2、TransactionStatus
代表一個事務的具體運行狀態
3、PlatformTransactionManager
spring提供的事務管理的對象

技術分享圖片

spring可以支持很多orm框架,例如mybatis、jpa、hibernate等,spring提供的事務管理類如下:

技術分享圖片

技術分享圖片

spring事務管理器實現類

1、spring jdbc、mybatis

基於數據源的connection訪問數據庫,所以可以使用DataSourceTransactionManager,配置如下:

技術分享圖片

DataSourceTransactionManager可以使用DataSource的connection對象的commit()、rollback()等方法來管理事務。

2、jpa
jpa通過javax.persistence.EntityTransaction來管理jpa事務。
其中EntityTransaction需要通過javax.persistence.EntityManager#getTransaction()獲得,EntityManager則是由javax.persistence.EntityManagerFactory#createEntityManager()獲取。

技術分享圖片

事務同步管理器

在spring中,jdbc的connection,hibernate的session等訪問數據庫的連接或會話對象統稱為資源,這些資源在同一時刻是不能多線程共享的。那麽如何解決呢?spring使用事務同步管理器(org.springframework.transaction.support.TransactionSynchronizationManager)使用ThreadLocal為不同事務線程提供了獨立的資源副本,同時維護事務配置的屬性和運行狀態信息。

技術分享圖片

spring事務配置

1、xml配置方式

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
    <!-- 事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 傳播行為 -->
            <tx:method name="save*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
    <!-- 切面 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice"
            pointcut="execution(* com.jeenotes.ssm.service.*.*(..))" />
    </aop:config>
</beans>

不過這種方式一看就不方便,下面說說註解方式

2、基於註解配置(@Transactional)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
    <!-- 事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 開啟事務控制的註解支持,配置 Annotation 驅動,掃描@Transactional註解的類定義事務。proxy-target-class為true代表使用CGLIB類代理;false則代表使用的是jdk動態代理 -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
</beans>
public class DefaultFooService implements FooService {
 
  public Foo getFoo(String fooName) {
    // do something
  }
 
  //方法上註解屬性會覆蓋類註解上的相同屬性
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

@Transactional註解:

a、屬性

技術分享圖片

b、作用範圍

@Transactional註解可以應用於接口定義接口方法類定義類的public方法上。
但是不建議使用在接口中,主要是因為當proxy-target-class="true"時,接口的實現類將會工作在非事務環境下,所以spring建議在具體的類上使用@Transactional。

c、方法中使用@Transactional

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
 
  public Foo getFoo(String fooName) {
    // do something
  }
 
  // these settings have precedence for this method
  //方法上註解屬性會覆蓋類註解上的相同屬性
  @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
  public void updateFoo(Foo foo) {
    // do something
  }
}

spring事務易錯點

1、關於spring事務,經常會有一種錯誤的說法,就是一個事務方法不應該調用另一個事務方法,否則將產生兩個事務。

事務的傳播行為

技術分享圖片

spring中的默認事務傳播行為是PROPAGATION_REQUIRED,因此當一個方法使用@Transactional修飾,調用另一個使用@Transactional修飾的方法,則這兩個方法其實是工作在同一個事務當中。

2、多線程下的事務安全

web容器基本都是多線程的,即使開發者認為自己沒有開啟多線程,但其實整個web容器為了能夠處理更多的請求,都是使用共享線程池的方式來處理請求。在spring應用中,大部分的bean都是單例的,因此倒不會出現線程安全問題。

但是dao層持有的都是connection對象,這個連接對象肯定是狀態化的。spring的解決方式就是通過ThreadLocal將有狀態的變量(connection等)本地線程化,實現線程安全。

結論:在相同線程中進行相互嵌套調用的事務方法工作在相同的事務中;如果這些相互嵌套調用的方法工作在不同的線程中,則不同線程下的事務方法工作在獨立的事務中。

3、哪些方法實現不了spring aop事務

大家都知道aop的實現原理主要有jdk動態代理或者cglib,那麽對於spring aop事務的實現,還是有一定的要求的:

  • 基於jdk動態代理的aop事務基於接口,所以要求方法必須是public修飾,並且不能使用static修飾;
  • 而基於cglib字節碼動態代理的方案是通過擴展被增強類,動態創建其子類的方式進行aop增強,但是final、static、private修飾的方法都不能被子類覆蓋,所以這些方法無法實施aop增強。

spring深入學習(五)-----spring dao、事務管理