spring 學習(五):spring 事務
事務概要
一個數據庫事務一般包含了一個序列的對數據庫的讀/寫操做。它的存在包含有如下兩個目的:java
- 爲數據庫操做序列提供了一個從失敗中恢復到正常狀態的方法,同時提供了數據庫即便在異常狀態下仍能保持一致性的方法。
- 當多個應用程序在併發訪問數據庫時,能夠在這些應用程序之間提供一個隔離方法,以防止彼此的操做互相干擾。
因此事務是用來處理異常和併發問題的。mysql
spring 事務管理
spring 的事務管理咱們通常採用聲明式事務管理這種方式,有兩種方式實現:spring
- 基於 xml 配置文件實現
- 基於註解實現
通常使用 PlatformTransactionManager
這個事務接口來實現 spring 事務管理。sql
spring 針對不一樣的 dao 層框架,提供了接口不一樣的實現類。數據庫
因爲如今通常使用 spring JDBC 和 iBatis ,因此咱們使用express
org.springframework.jdbc.datasource.DataSourceTransactionManager
這個類來實現數據的持久化操做。併發
spring 實例
咱們仍是舉個栗子來進行 spring 事務操做,以轉帳操做爲例:一人少錢,一人多錢。app
1 建立數據庫表 account,添加數據,以下圖所示:框架
2 建立 OrdersDao.java 和 OrdersService.java ,實現數據操做:less
OrdersDao.java:
package cn.itcast.dao; import org.springframework.jdbc.core.JdbcTemplate; public class OrdersDao { private JdbcTemplate jdbcTemplate; public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /* 作對數據庫操做的方法,不寫業務操做 */ //少錢的方法 public void lessMoney(){ String sql = "update account set salary=salary+? where username=?"; jdbcTemplate.update(sql, -1000, "小王"); } //多錢的方法 public void moreMoney(){ String sql = "update account set salary=salary+? where username=?"; jdbcTemplate.update(sql, 1000, "小馬"); } }
OrdersService:
package cn.itcast.service; import cn.itcast.dao.OrdersDao; import org.springframework.transaction.annotation.Transactional; // @Transactional 使用註解時的操做 public class OrdersService { private OrdersDao ordersDao; public void setOrdersDao(OrdersDao ordersDao) { this.ordersDao = ordersDao; } /* 業務邏輯層,寫轉帳業務 調用 dao 方法 */ public void accountMoney(){ //小王少1000 ordersDao.lessMoney(); int i = 10/0; //拋出異常操做 //小馬多1000 ordersDao.moreMoney(); } }
- service 層又叫業務邏輯層
- dao 層,單純對數據庫操做層,在 dao 層不添加業務
咱們在編寫程序的時候最好按照這種規範操做。
3 建立 applicationContext.xml 文件,完成注入關係,並添加事務模塊,出現異常進行回滾操做,配置文件使用 aop 思想配置:
<?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: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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置c3p0鏈接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 注入屬性值 --> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///sampledb"></property> <property name="user" value="root"></property> <property name="password" value=""></property> </bean> <!-- 第一步 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入 dataSource --> <property name="dataSource" ref="dataSource"> </property> </bean> <!-- 第二步 配置事務加強 --> <tx:advice id="txadvice" transaction-manager="transactionManager"> <!-- 作事務操做 --> <tx:attributes> <tx:method name="account*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 第三步 配置切面 --> <aop:config> <!-- 切入點 --> <aop:pointcut id="pointcut1" expression="execution(* cn.itcast.service.OrdersService.*(..))"/> <!-- 切面 --> <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/> </aop:config> <bean id="ordersService" class="cn.itcast.service.OrdersService"> <property name="ordersDao" ref="ordersDao"></property> </bean> <bean id="ordersDao" class="cn.itcast.dao.OrdersDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
固然咱們還能夠採用註解的方式進行事務管理,只需修改配置文件:
<?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: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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置c3p0鏈接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 注入屬性值 --> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///sampledb"></property> <property name="user" value="root"></property> <property name="password" value=""></property> </bean> <!-- 第一步 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 第二步 開啓事務註解 --> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="ordersService" class="cn.itcast.service.OrdersService"> <property name="ordersDao" ref="ordersDao"></property> </bean> <bean id="ordersDao" class="cn.itcast.dao.OrdersDao"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
而後在要使用事務的方法所在類上面添加註解 @tTransactional
。
4 咱們編寫一個測試類來查看當拋出異常時數據庫能不能進行回滾操做。
建立 TestService.java:
package cn.itcast.service; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestService { @Test public void testDemo(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); OrdersService ordersService = (OrdersService) context.getBean("ordersService"); ordersService.accountMoney(); } }
運行測試文件,能夠看到在控制檯拋出異常:
是由於咱們在 OrdersService.java 的 accountMoney() 方法下加入了下面這行:
int i = 10/0;
這是不容許的。查看咱們數據庫中的數據,能夠看到內容沒有變,因此是進行了回滾操做的,否則按業務邏輯會出現一方少1000,而另外一方內容不變。