spring深入學習(五)-----spring dao、事務管理
訪問數據庫基本是所有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、事務管理