本節要點:java
事務的定義:程序員
數據庫系統爲了保證數據操做的完整性和一致性,引入了事務這個重要的概念,所謂事務,就是將一系列的數據庫操做做爲一個總體來執行。如對數據庫存取,就是一組SQL指令,這一組SQL指令必須所有執行成功;若是由於某個緣由(例如其中一行SQL有錯誤),則先前所執行過的SQL指令撤銷。spring
Jdbc事務管理sql
在JDBC中,能夠用Connection的setAutoCommit()方法,給定它false參數。在一連串的SQL語句後面,調用Connection的commit()來送出變動。若是中間發生錯誤,則調用rollback()來撤銷全部的執行,例如:數據庫
try{express
connection.setAutoCommit(false);編程
…//一連串SQL操做併發
connection.commit(); //執行成功,提交全部變動oracle
}catch(SQLException e){app
connection.rollback(); //發生錯誤,撤銷全部變動
}
案例:操做用戶信息
在數據庫新建一個表t_user 幷包含id和name兩個屬性。
User
public class User { private int id; private String name; get/set…… }
UserDao
public interface UserDao { public String getUser(int id); public void updUser(int id); public void addUser(User user); public void delUser(int id); }
UserDaoImpl
/** * JDBC Connection類的事務控制方法: setAutoCommit(boolean autoCommit) 設置是否自動提交事務,默認自動 commit() 提交事務 rollback() 撤銷事務 * @author Administrator * */ public class UserDaoImpl implements UserDao { public DataSource dataSource;// 數據源 private Connection conn;// 數據庫鏈接 /** * 設置數據源並根據數據源獲取數據庫的鏈接 */ public void setDataSource (DataSource dataSource){ this.dataSource = dataSource; try{ this.conn = dataSource.getConnection(); }catch(SQLException e){ e.printStackTrace(); } } @Override public String getUser(int id) { String name = null; try{ PreparedStatement ps = conn.prepareStatement("select name from " + "t_user where id=?"); ps.setInt(1, id); ResultSet rs = ps.executeQuery(); if(rs.next()){ name = rs.getString(1); } }catch(SQLException e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ PreparedStatement ps = conn.prepareStatement("update t_user set name=? where id=? "); ps.setString(1, "25342"); ps.setInt(2, id); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } @Override public void addUser(User user) { try{ PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values(" +user.getId()+","+user.getName()+")"); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } @Override public void delUser(int id) { try{ PreparedStatement ps = conn.prepareStatement("delete from t_user where id=? "); ps.setInt(1, id); ps.executeUpdate(); }catch(SQLException e){ e.printStackTrace(); } } }
UserService
public interface UserService { public String getUser(int id); public void updUser(int id); public void addUser(User user); public void delUser(int id); }
UserServiceImpl
public class UserServiceImpl implements UserService{ private UserDao userDao; public UserDao getUserDao() { return userDao; } public void setUserDao(UserDao userDao) { this.userDao = userDao; } @Override public String getUser(int id) { return userDao.getUser(id); } @Override public void updUser(int id) { userDao.updUser(id); } @Override public void addUser(User user) { userDao.addUser(user); } @Override public void delUser(int id) { userDao.delUser(id); } }
Bean.xml
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> <!--數據源配置 DriverManagerDataSource是spring提供的一個簡單的數據源實現類 這個類實現了javax.sql.DataSource接口,可是它沒有提供池化鏈接的機制, 每次調用getConnection()獲取新鏈接時,只是簡單地建立一個新的鏈接。 所以,這個數據源簡單應用或測試,由於它不須要額外的依賴類 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
Test
public class Test { public static void main(String[] args) throws Exception{ ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans.xml"); UserService userService=(UserService)applicationContext.getBean("userService"); User user = new User(); // user.setId(1); // user.setName("1231"); // userService.addUser(user); userService.delUser(1); } }
UserDaoImpl中的新增方法修改爲以下,體驗事務的功能:若是事務過程當中出現異常,捕獲異常後對數據進行回滾,能夠保證數據的完整性。
public void addUser(User user) { try{ conn.setAutoCommit(false); PreparedStatement ps = conn.prepareStatement("insert into t_user(id,name) values(" +user.getId()+","+user.getName()+")"); ps.executeUpdate(); Integer.parseInt("asfdas"); conn.commit(); }catch(Exception e){ e.printStackTrace(); try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } }
Spring事務概述
Spring框架提供了極其強大而簡便的事務處理功能,其核心即是PlatformTransactionManager抽象接口。Spring將全部的事務管理都抽象爲PlatformTransactionManager、TransactionStatus和TransactionDefinition這3個接口,而不管其底層關聯的具體的事務到底是JDBC事務、JTA事務,仍是ORM框架自定義的事務。
在Spring中實現事務管理有兩種方式,一種是傳統的編程式事務管理,也就是程序員在編寫程序代碼實現事務的管理,具體包括定義事務的開始、在程序異常時進行事務的回滾及程序正常執行後的事務提交。
另外一種則是基於AOP技術實現的聲明式事務管理,事務管理自己是一項共有的系統級服務功能,徹底能夠將事務管理抽象成一個事務切面,程序員再也不關心事務管理的問題,把主要精力放在覈心業務邏輯代碼的編寫上,而後在須要進行事務管理的方法上切入事務切面,使之具備事務管理的功能,達到事務管理的目的。
Spring編程式事務
PlatformTransactionManager:
userDaoImpl
package com.silvan.dao; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; import com.silvan.pojo.User; /** *spring編程式事務 * @author Administrator */ public class UserDaoImpl implements UserDao { public DataSourceTransactionManager transactionManager;// 事務管理器的實現類,做用如建立事務,管理事務等 public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate對象來完成jdbc 操做 public void setTransactionManager( DataSourceTransactionManager transactionManager) { this.transactionManager = transactionManager; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public String getUser(int id) { String name = null; try{ List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from t_user where id=?", id); if(result.iterator() != null){ name = (String) result.get(0).get("name"); } }catch(Exception e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ jdbcTemplate.update("update t_user set name=? where id=? ","2544",id); }catch(Exception e){ e.printStackTrace(); } } @Override public void addUser(User user) { TransactionStatus ts=null; try{ //定義事務規則 TransactionDefinition td=new DefaultTransactionDefinition(); //根據事務規則,建立一個新的事務或者獲取以前已經建立的事務 ts=transactionManager.getTransaction(td); jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName()); // Integer.parseInt("asfdas"); transactionManager.commit(ts); }catch(Exception e){ e.printStackTrace(); try { transactionManager.rollback(ts); }catch (Exception e1) { e1.printStackTrace(); } } } @Override public void delUser(int id) { try{ jdbcTemplate.update("delete from t_user where id=?",id); }catch(Exception e){ e.printStackTrace(); } } }
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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 建立數據源對象 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <!-- 建立事務管理對象,並注入數據源 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource"></property> </bean> <!-- 建立jdbc操做對象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <!-- 注入jdbc事務操做對象和事務管理對象 --> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> <property name="transactionManager" ref="transactionManager"></property> </bean> </beans>
Spring聲明式事務
Spring爲聲明式事務提供了簡單而強大的支持,所謂聲明式事務,是指在Spring的配置文件中使用相應的標籤對事務進行配置,這樣作的好處是Spring能夠幫助咱們管理事務,例如:何時提交事務、何時回滾事務等。
從開發效率與易維護的角度來看,Spring聲明式事務管理是實際開發中比較經常使用的。
基於 <tx> 命名空間的聲明式事務管理:
① 在xml中啓用tx和aop兩個命名空間
xmlns:tx=http://www.springframework.org/schema/tx
xsi:schemaLocation="http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
② 在xml中配置通知、切入點以及Advisor
修改一下文件:
UserDaoImpl
package com.silvan.dao; import java.util.List; import java.util.Map; import org.springframework.jdbc.core.JdbcTemplate; import com.silvan.pojo.User; /** *spring聲明式事務 * @author Administrator * */ public class UserDaoImpl implements UserDao { public JdbcTemplate jdbcTemplate;//spring中要使用Jdbctemplate對象來完成jdbc 操做 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override public String getUser(int id) { String name = null; try{ List<Map<String, Object>> result = jdbcTemplate.queryForList("select name from t_user where id=?", id); if(result.iterator() != null){ name = (String) result.get(0).get("name"); } }catch(Exception e){ e.printStackTrace(); } return name; } @Override public void updUser(int id) { try{ jdbcTemplate.update("update t_user set name=? where id=? ","2544",id); }catch(Exception e){ e.printStackTrace(); } } @Override public void addUser(User user) { try{ jdbcTemplate.update("insert into t_user(id,name) values(?,?)",user.getId(),user.getName()); Integer.parseInt("asfdas"); }catch(Exception e){ e.printStackTrace(); throw new RuntimeException(); } } @Override public void delUser(int id) { try{ jdbcTemplate.update("delete from t_user where id=?",id); }catch(Exception e){ e.printStackTrace(); } } }
Bean.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:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 建立數據源對象 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property> <property name="url" value="jdbc:oracle:thin:@localhost:1521:zhouyq"></property> <property name="username" value="zhou"></property> <property name="password" value="123456"></property> </bean> <!-- 配置JdbcTemplate,若是不用spring的jdbc能夠省略 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 建立事務管理對象 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="testAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切入點和advisor --> <aop:config> <aop:pointcut expression="execution(* com.silvan.service.*.*(..))" id="pointcut"/> <aop:advisor advice-ref="testAdvice" pointcut-ref="pointcut"/> </aop:config> <bean id="userService" class="com.silvan.service.UserServiceImpl"> <property name="userDao" ref="userDao"></property> </bean> <bean id="userDao" class="com.silvan.dao.UserDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> </beans>
事務回滾:
默認spring事務只在發生未被捕獲的 runtimeexcetpion時纔回滾。
spring aop 異常捕獲原理:被攔截的方法需顯式拋出異常,並不能經任何處理,這樣aop代理才能捕獲到方法的異常,才能進行回滾,默認狀況下aop只捕獲runtimeexception的異常,但能夠經過配置來捕獲特定的異常並回滾,換句話說在service的方法中不使用try catch 或者在catch中最後加上throw new runtimeexcetpion(),這樣程序異常時才能被aop捕獲進而回滾
事務的傳播方式:
事務的傳播行爲用於指定在多個事務方法間調用時,事務是如何在這些方法間傳播的,spring共支持7種傳播行爲。
表示業務邏輯方法須要在一個事務中運行,若是該方法在運行時,已經處在一個事務中,則直接加入到該事務中,不然本身建立一個新的事務。即:若是存在一個事務,則支持當前事務。若是沒有事務則開啓。(在實際開發中經常使用該傳播方式。)
表示業務邏輯方法若是在某個事務範圍內被調用,則該方法直接成爲當前事務的一部分。若是該方法在事務範圍外被調用,則該方法在無事務的環境下執行。即:若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。
表示業務邏輯方法只能在一個已經存在的事務中執行,該方法不能建立本身的事務,若是該方法在沒有事務的環境下被調用,容器就會拋出事務不存在的異常。 即:若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。
表示無論當前是否有事務存在,該業務邏輯方法都會爲本身建立一個全新的事務。若是該方法已經運行在一個事務中,則原有事務會被掛起,新的事務會被建立,直到該方法執行結束後新事務纔算結束,原先的事務再恢復執行。即: 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起,新事務運行完畢後,再接着運行被掛起的事務。
表示業務邏輯方法不須要事務。若是該方法目前沒有關聯到某個事務,容器不會爲它建立事務。若是該方法在一個事務中被調用,則該事務會被掛起,在方法調用結束後原先的事務纔會恢復執行。即:老是非事務地執行,並掛起任何存在的事務,當前方法運行完畢後,被掛起的事務才恢復執行。
表示業務邏輯方法絕對不能在事務範圍內執行。若是該方法在某個事務中執行,容器會拋出異常,只有沒有關聯到任何事務時該方法才能正常執行。即:老是非事務地執行,若是存在一個活動事務,則拋出異常
表示若是一個活動的事務存在,業務邏輯方法則運行在一個嵌套的事務中,若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點,內部事務的回滾不會對外部事務形成影響,它只對DataSourceTransactionManager事務管理器生效。即:若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。
事務傳播特性:
對基於方法級別的事務管理而言,方法開始執行時建立事務,方法運行過程當中若出現異常則進行事務回滾,方法若是正常執行完成則進行事務的提交。於是事務管理的主要任務就是事務的建立、事務的回滾與事務的提交,其中是否須要建立事務及如何建立事務時由事務傳播行爲控制的,一般數據的讀取時不須要事務管理的,或者也可爲其指定只讀事務,而對於插入、修改與刪除數據的方法來講,就有必要進行事務管理了,在未指定事務傳播行爲時,Spring2.x將啓用默認的REQUIRED。
Spring中事務隔離級別
這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.
如下四個與JDBC的隔離級別相對應
這是事務最低的隔離級別,它充許令外一個事務能夠看到這個事務未提交的數據。這種隔離級別會產生髒讀、不可重複讀和幻像讀。
保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。
這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。它除了保證一個事務不能讀取另外一個事務未提交的數據外,還保證了避免下面的狀況產生,即:不可重複讀。
這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀、不可重複讀外,還避免了幻像讀。可是併發性最差。