Spring 事務配置說明java
Spring 若是沒有特殊說明,通常指是跟數據存儲有關的數據操做事務操做;對於數據持久操做的事務配置,通常有三個對象,數據源,事務管理器,以及事務代理機制;mysql
Spring 提供了多種的底層數據源實現,以及多種類型的事務管理器;全部的管理器都基於 PlatformTransactionManager 接口實現各自的事務策略;正則表達式
Spring 事務管理採用 AOP 切面代理技術實現,AOP 用於分隔關注點,保證事務的原子性,採用必定的技術 把該關注點 (weaving) 織入到 待完善的關注點上,實現單獨組件沒法實現的功能,以解決面向對象編程在某些方式下難於實現的操做,更好的支持面向對象的開關原則(擴展開放,修改關閉)。spring
底層數據源配置sql
首選加載 數據源配置 .properties 文件;數據庫
<bean id="loadProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:META-INF/mybatis/mysql.properties</value> <value>classpath:META-INF/spring/hibernate.properties</value> </list> </property> </bean>
mysql.properties:express
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/springdb
username=root
password=xxxxx
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
hibernate.properties:apache
# hibernate.X hibernate.connection.driverClass=org.gjt.mm.mysql.Driver hibernate.connection.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf-8 hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.connection.username=root hibernate.connection.password=xxxxx hibernate.show_sql=true hibernate.hbm2ddl.auto=create-drop
1. JDBC 方式:編程
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${driver}"></property> <property name="url" value="${url}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> </bean>
2. c3p0 方式:服務器
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <!-- 指定鏈接數據庫的驅動 --> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <!-- 指定鏈接數據庫的URL --> <property name="jdbcUrl" value="jdbc:mysql://localhost/springdb"/> <!-- 指定鏈接數據庫的用戶名 --> <property name="user" value="root"/> <!-- 指定鏈接數據庫的密碼 --> <property name="password" value="xxxxx"/> <!-- 指定鏈接數據庫鏈接池的最大鏈接數 --> <property name="maxPoolSize" value="40"/> <!-- 指定鏈接數據庫鏈接池的最小鏈接數 --> <property name="minPoolSize" value="1"/> <!-- 指定鏈接數據庫鏈接池的初始化鏈接數 --> <property name="initialPoolSize" value="1"/> <!-- 指定鏈接數據庫鏈接池的鏈接的最大空閒時間 --> <property name="maxIdleTime" value="20"/> </bean>
3. dbcp 方式:
<beans> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${driver}"></property> <property name="url" value="${url}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> </bean>
4. Alibaba Druid 方式:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close" init-method="init" > <property name="url" value="${url}?useUnicode=true&characterEncoding=utf-8"></property> <property name="driverClassName" value="${driver}"></property> <property name="username" value="${username}"></property> <property name="password" value="${password}"></property> <property name="filters" value="${filters}"></property> <property name="maxActive" value="${maxActive}"></property> <property name="maxWait" value="${maxWait}"></property> <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"></property> <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"></property> <property name="validationQuery" value="${validationQuery}"></property> <property name="testWhileIdle" value="${testWhileIdle}"></property> <property name="testOnBorrow" value="${testOnBorrow}"></property> <property name="testOnReturn" value="${testOnReturn}"></property> <property name="poolPreparedStatements" value="${poolPreparedStatements}"></property> <property name="maxPoolPreparedStatementPerConnectionSize" value="${maxPoolPreparedStatementPerConnectionSize}"></property> </bean>
5. JNDI 全局配置方式:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${jndiName}"></property> </bean>
6. 自定義 DataSource:
<bean id="dataSource" class="me.study.hnmapper.utils.CustomDataSource"> <property name="driverClass" value="oracle.jdbc.driver.OracleDriver"></property> <property name="driverUrl" value="jdbc:oracle:thin:@10.30.2.204:1527:sec"></property> <property name="username" value="apps"></property> <property name="password" value="secapp29"></property> </bean>
CustomDataSource:
package me.study.hnmapper.utils; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.DataSource; public class CustomDataSource implements DataSource { private String driverClass; private String driverUrl; private String username; private String password; @Override public PrintWriter getLogWriter() throws SQLException { // TODO Auto-generated method stub return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { // TODO Auto-generated method stub } @Override public void setLoginTimeout(int seconds) throws SQLException { // TODO Auto-generated method stub } @Override public int getLoginTimeout() throws SQLException { // TODO Auto-generated method stub return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { // TODO Auto-generated method stub return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { // TODO Auto-generated method stub return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { // TODO Auto-generated method stub return false; } @Override public Connection getConnection() throws SQLException { // TODO Auto-generated method stub return null; } @Override public Connection getConnection(String username, String password) throws SQLException { // TODO Auto-generated method stub // TODO Auto-generated method stub Connection conn = null; try { Class.forName(driverClass); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } conn = DriverManager.getConnection(driverUrl, username, password); return conn; } public String getDriverClass() { return driverClass; } public void setDriverClass(String driverClass) { this.driverClass = driverClass; } public String getDriverUrl() { return driverUrl; } public void setDriverUrl(String driverUrl) { this.driverUrl = driverUrl; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
6. Hibernate方式,利用的是SessionFactory做爲數據源操做;
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingLocations" > <list> <value>classpath*:model/*.hbm.xml</value> </list> </property> <!-- packagesToScan能夠自動搜索某個package的所有標記@Entity class --> <!-- <property name="packagesToScan"> <list> <value>hibernatelibs.model*</value> </list> </property> --> <property name="hibernateProperties"> <props> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> </props> </property> </bean>
TransactionManager 事務管理器配置
1. JDBC TransactionManager 配置;
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 配置DataSourceTransactionManager時須要依注入DataSource的引用 --> <property name="dataSource" ref="dataSource"/> </bean>
2. hibernate TransactionManager 配置:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
3. Jta TransactionManager 配置:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Jta 在應用服務器依賴於 JNDI 查找,也可基於 (ObjectWeb)JOTM 或 Atomikos 這樣的 userTransaction 來實現。
PlatformTransactionManager 是 Spring 事務策略核心的接口,Spring 並不支持事務的實現,而是負責包裝底層的事務,負責分離各類基於PlatformTransaction接口的具體實現。
應用底層支持什麼樣的事務策略,Spring 就支持什麼樣的事務策略。
Spring 聲明式事務管理是基本 AOP 切面代理技術實現,是基本基於XML 或 Annotation 配置的一種實現,PlatformTransaction 爲各類管理機制提供統一操做接口,各類類型的管理機制都得基於 PlatformTransaction 接口實現具體的事務處理,所以,Spring 事務配置能夠很容易的在各類類型的事務管理機制間切換。
PlatformTransaction 提供的接口以下:
public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
比較經常使用的事務機制還有:(Jdo)JdoTransactionManager,(Jpa)JpaTransactionManager。此外還有:WebSphereUowTransactionManager、WebLogicJtaTransactionManager等類型;
事務代理機制
1. 使用 pointcut 通知方式 (最多見的方式):
首先配置 tx 命名空間:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
配置通知項 <tx:advice
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" /> <!-- 匹配到類下方法的切入點, 好比 query* 表示匹配全部以 query開頭的方法 , 這裏能夠配置事務行爲 傳播行爲propagation, 隔離級別isolation, 超時(秒)timeout_xx, 自讀read-only 等 -->
</tx:attributes> </tx:advice>
<tx:method 配置項:
name: 用於匹配類中的方法,spring 只支持 類下的方法的形式,對於模式匹配,能夠支持 perl 的正則表達式,也支持 aspectj ,默認支持 AspectJ;
<tx:method name="*" ... /> <!-- 匹配全部方法 --> <tx:method name="get*" ... /> <!-- 匹配全部以 get 開頭的方法名 --> <tx:method name="save*" ... /> <!-- 匹配全部以 save 開頭的方法名 --> ...
isolation:
一、Serializable:最嚴格的級別,事務串行執行,資源消耗最大;
二、REPEATABLE_READ:保證了一個事務不會修改已經由另外一個事務讀取但未提交(回滾)的數據。避免了「髒讀取」和「不可重複讀取」的狀況,可是帶來了更多的性能損失。
三、READ_COMMITTED:大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另外一個並行事務已修改但未提交的數據,避免了「髒讀取」。該級別適用於大多數系統。
四、Read_Uncommitted:保證了讀取過程當中不會讀取到非法數據。隔離級別在於處理多事務的併發問題。
五、Default 默認。
propagation: Spring 支持七種傳播行爲,比 EJB 的 CMT 還多一種;
1. Required=>PROPAGATION_REQUIRED: 有邏輯事務就加入執行,沒有就建立一個新的;
2. RequiresNew=>PROPAGATION_REQUIRES_NEW: 表示每次都會建立一個新事務,事務間不互相影響;
3. Supports=>PROPAGATION_SUPPORTS: 有邏輯事務就加入,沒有就以非事務方式執行;
4. NotSupported=>PROPAGATION_NOT_SUPPORTED: 不支持事務,有事務時,暫停,以非事務方式執行;
5. Mandatory=>PROPAGATION_MANDATORY: 以事務方式執行,沒有事務時,拋出異常;
6. Never=>PROPAGATION_NEVER: 不支持事務,有事務時,拋出異常;
7. Nested=>PROPAGATION_NESTED: 有事務時,嵌入事務內執行,沒有時建立新的事務;(內部事務異常不影響外部事務,外部事務異常會影響到內部的事務)
timeout="5" 或 timeout_xx: 好比: timeout_5; 表示超時 5 秒;
完整的一個 tx:method:
<tx:method propagation="REQUIRED" isolation="REPEATABLE_READ" timeout="5" read-only="true" />
另一種配置方式,是配置在具體bean內的:
<bean id="xxxxDao"> <!-- ... --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED,REPEATABLE_READ,timeout_5,readOnly</prop> </props> </property> </bean>
配置 aop:config :
<aop:config> <!-- 表示在全部切入點加註事務管理 --> <aop:pointcut expression="execution(* springlibs.service.*.*(..))" id="puintCutid" /> <!-- 須要配置 component-scan --> <aop:advisor advice-ref="txAdvice" pointcut-ref="puintCutid" /> </aop:config>
expression表達式 : execution(* springlibs.service.*.*(..))
第一個 * : 表示全部的返回值類型;
第二個 * : 表示全部的類;
第三個 * : 表示全部的方法;
切入點表達式例子:
1>. execution(* springlibs.service.*.*(..)) 表示 springlibs.service 下全部的類跟全部的方法;
2>. execution(* springlibs.service.*.save*(..)) 表示 springlibs.service 下全部的以 save 開頭的方法;
3>. execution(public * springlibs.service.*.*(..)) 表示 springlibs.service 下全部的公共方法;
pointcut 表示的是一些鏈接點的集合,使用 expression 來表達這種集合;advisor 這裏就當作鏈接 pointcut切入點 與 advice通知 的做用;
2. 一個 bean 對應一個事務;
配置 dao 實現 bean:
<bean id="xxxDaotarget" class="springlibs.dao.xxxDaoImpl"> <!-- property name="sessionFactory" ref="sessionFactory" /> --> </bean>
在 Dao 上配置事務:
<bean id="xxxDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="xxxDaotarget" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
3. 全部 bean 共享一個基類:
<bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
在 Dao 上配置事務:
<bean id="xxxDao" parent="transactionBase"> <property name="target"> <bean id="xxxDaoImpl" class="springlibs.dao.xxxDaoImpl"></bean> </property> </bean>
4. 使用攔截器:
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean>
在 Dao 上配置事務;
<!-- 配置DAO實現 --> <bean id="xxxDao" class="springlibs.dao.xxxDaoImpl"> <!-- <property name="sessionFactory" ref="sessionFactory" /> --> </bean>
5. 使用 Annotation 方式:
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
proxy-target-class 爲 true: 使用 CGLib 建立增長的代理;爲 false 就是使用 標準 JDK 將會被建立;
能夠在 類級別或方法 上使用 @Transaction 注入;
加註 @Transaction 的類或方法,自己並無事務行爲,能被識別爲事務的,實際上是 tx:annotation-driven 的配置開啓了事務;