1、事務的特性
事務的四種特性:
原子性:體現一個事務的操作的不可分割,要么權執行,要么全不執行。
一致性:事務的執行結果必須從一種一致性狀態變到另一種一致性狀態。最典型的就是轉賬,兩個賬戶A、B總金額為5000,不管A、B如何轉賬,轉幾次,當事務結束A、B賬戶總金額還為5000。
隔離型:即並發執行的事務操作同一張表時相互之間不能相互影響。舉例說明就是對於任意兩個並發的事務T1和T2,在事務T1看來,T2要么在T1開始之前就已經結束,要么在T1結束之后才開始,不能出現交叉執行。
持久性:指事務一旦被提交,對數據庫中數據的改變時永久性的。
2、事務的隔離性(解決臟讀、不可重讀和幻讀問題)
隔離級別所解決的問題:
臟讀(Dirty Reads):一個事務讀取了另一個事務還未提交的數據,這里主要指事務讀取了另一個事務未回滾前的數據。
不可重讀(Non-RepeatableReads):不可重復讀是指在對於數據庫中的某個數據,一個事務范圍內多次查詢卻返回了不同的數據值。例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據並且提交事務給數據庫,事務T1再次讀取該數據就得到了不同的結果,此時發生了不可重復讀。
幻讀:指事務A對符合條件的n條數據進行批量修改,在此過程中(事務A還未提交)事務B向數據庫中插入了一條符合事務A條件的數據,此時事務A再讀取時發現有未修改的數據,此時便產生幻讀。
注意:不可重讀和幻讀均讀取了另一個事務已提交的數據,這一點區別於臟讀,但不可重讀針對一條數據,而幻讀則針對批量數據。
四種隔離級別:
SERIALIZABLE:可避免臟讀、不可重復讀、幻讀的發生
REPEATABLE_READ:可避免臟讀、不可重復讀的發生
READ_COMMITTED:可避免臟讀的發生
READ_UNCOMMITTED:最低級別,任何情況都無法保證
3、事務的傳播性(解決事務創建時機的問題)
七種傳播級別:
PROPAGATION_REQUIRED:如果上下文中已經存在事務,那么就加入到事務中執行;如果當前上下文中不存在事務,則新建事務執行。
PROPAGATION_SUPPORTS:如果上下文存在事務,則支持事務加入事務,如果沒有事務,則使用非事務的方式執行
PROPAGATION_MANDATORY:該級別的事務要求上下文中必須要存在事務,否則就會拋出異常!配置該方式的傳播級別是有效的控制上下文調用代碼遺漏添加事務控制的保證手段。比如一段代碼不能單獨被調用執行,但是一旦被調用,就必須有事務包含的情況,就可以使用這個傳播級別
PROPAGATION_REQUIRES_NEW:每次都會新建一個事務,並且同時將上下文中的事務掛起,執行當前新建事務完成以后,上下文事務恢復再執行
PROPAGATION_NOT_SUPPORTED:若上下文中存在事務,則掛起事務,執行當前邏輯,結束后恢復上下文的事務
PROPAGATION_NEVER:上下文中不能存在事務,一旦有事務,就拋出runtime異常
PROPAGATION_NESTED:如果上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務
一、配置代碼
web.xml
<!--spring打開session及事務配置--> <filter> <filter-name>SpringOpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>SpringOpenSessionInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>
1、注解方式
applicationContext.xml
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--spring的配置文件 與springMVC的配置文件對包的重復掃描裝配會照成失效在主容器中(applicationContext.xml),將Controller的注解排除掉--> <context:component-scan base-package="example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 創建sessinFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan"> <list> <value>example.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hbm2ddl.auto">update</prop> </props> </property> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> </beans>
然后在service層的類或方法上加@Transactional即可。
注解方式需添加<tx:annotation-driven transaction-manager="transactionManager"/>,且@Transactional只能放在public修飾的方法或類,其它訪問權限前添加無效。
事務的隔離級別及傳播方式可通過下面方式配置(propagation配置傳播方式默認為REQUIRED,isolation配置隔離級別,默認為DEFAULT,用的為數據庫的默認隔離級別,大部分數據庫默認隔離級別為READ_COMMITTED,mysql為REPEATABLE_READ)
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
2、aop方式
applicationContext.xml
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--spring的配置文件 與springMVC的配置文件對包的重復掃描裝配會照成失效在主容器中(applicationContext.xml),將Controller的注解排除掉--> <context:component-scan base-package="example"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/spring_hibernate"/> <property name="username" value="root"/> <property name="password" value=""/> </bean> <!-- 創建sessinFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="packagesToScan"> <list> <value>example.entity</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hbm2ddl.auto">update</prop> </props> </property> </bean> <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut expression="execution(* example.service..*.*(..))" id="fooServiceOperation"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/> </aop:config> </beans>
事務的隔離級別及傳播方式可通過下面方式配置(propagation配置傳播方式,isolation配置隔離級別)
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
二、配置后事務不起作用原因
1、dao層需使用sessionFactory.getCurrentSession()方法獲得當前session,不能使用sessionFactory.openSession()
原因:
采用getCurrentSession()創建的Session會綁定到當前的線程中去、而采用OpenSession()則不會。
采用getCurrentSession()創建的Session在commit或rollback后會自動關閉,采用OpenSession()必須手動關閉。
2、applicationContext.xml中需添加下面配置
<!--spring的配置文件 與springMVC的配置文件對包的重復掃描裝配會照成失效在主容器中(applicationContext.xml),將Controller的注解排除掉-->
<context:component-scan base-package="example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>