Spring事務管理分爲聲明式事務管理和編程式事務管理,聲明式事務管理又分爲xml和註解兩種配置方式。應該優先選擇聲明式事務,由於聲明式事務對程序代碼的影響最小,所以最符合非侵入式輕量級容器的理想 。只有在進行少許事務操做時,才應該選擇編程式事務管理的方式。java
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 導入數據庫配置文件 --> <context:property-placeholder location="config.properties"/> <!-- 配置數據源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="driverClassName"><value>${jdbc_driverClassName}</value></property> <property name="url"><value>${jdbc_url}</value></property> <property name="username"><value>${jdbc_username}</value></property> <property name="password"><value>${jdbc_password}</value></property> <!-- ... --> </bean> <!-- 配置jdbcTemplate,使用jdbcTemplate操做數據庫 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="userService" class="com.springdemo.tx.UserService"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- xml方式的配置聲明式事務 start--> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.springdemo.tx.UserService.*(..))"/> </aop:config> <!-- xml方式的配置聲明式事務 end--> </beans>
jdbc_driverClassName=com.mysql.jdbc.Driver jdbc_url=jdbc:mysql://192.168.1.77:3306/testdb?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true jdbc_username=root jdbc_password=root
public class UserService { private JdbcTemplate jdbcTemplate; public void addUser(String name, int age){ jdbcTemplate.update("insert into test007 (name,age) values (name,age)", name, age); } public void updateUserName(String name, int id){ jdbcTemplate.update("update test007 set name=? where id=?", name, id); } public void updateUserAge(int age, int id){ jdbcTemplate.update("update test007 set age=? where id=?", age, id); } public void updateUser(String name, int age, int id){ updateUserName(name, id); //模擬異常 Double.parseDouble("我不是Double,因此會拋出NumberFormatException異常"); updateUserAge(age, id); } public JdbcTemplate getJdbcTemplate() { return jdbcTemplate; } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
public static void main(String[] args) { ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml"); UserService userService = (UserService) ac.getBean("userService"); userService.updateUser("小王", 18, 1); }
<!-- 開啓註解配置 --> <tx:annotation-driven transaction-manager="txManager"/>
而後在須要事務管理的方法上添加@Transactional註解mysql
@Transactional public void updateUser(String name, int age, int id){ updateUserName(name, id); //模擬異常 Double.parseDouble("我不是Double,因此會拋出NumberFormatException異常"); updateUserAge(age, id); }
注意:註解方式和xml方式能夠二選一,也能夠結合使用,二者沒有依賴關係。spring
異常捕獲會致使事務機制失效,繼而不會觸發回滾。
能夠調用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()手動回滾。sql
@Transactional public void updateUser(String name, int age, int id){ try{ updateUserName(name, id); //模擬異常 Double.parseDouble("我不是Double,因此會拋出NumberFormatException異常"); updateUserAge(age, id); }catch(NumberFormatException ex){ //手動回滾 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); ex.printStackTrace(); } }
Spring事務管理機制默認只在拋出RuntimeException時纔會觸發回滾,能夠設置rollbackFor屬性來指定其餘類型的異常也能回滾。數據庫
//rollbackFor指定拋出Exception類型異常時回滾 @Transactional(rollbackFor=Exception.class) public void updateUser(String name, int age, int id) throws Exception{ updateUserName(name, id); if(id == 1){ throw new Exception("Id爲1的用戶不可修改"); } updateUserAge(age, id); }
<bean id="userService" class="com.springdemo.tx.UserService"> <property name="jdbcTemplate" ref="jdbcTemplate"/> <property name="transactionTemplate" ref="transactionTemplate"/> </bean> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="txManager"/> </bean> <!-- 註釋聲明式事務管理配置 --> <!-- <tx:annotation-driven transaction-manager="txManager"/> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.springdemo.tx.UserService.*(..))"/> </aop:config> -->
private TransactionTemplate transactionTemplate; public void updateUser(final String name, final int age, final int id){ transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus status) { updateUserName(name, id); //模擬異常 Double.parseDouble("我不是Double,因此會拋出NumberFormatException異常"); updateUserAge(age, id); } }); } public TransactionTemplate getTransactionTemplate() { return transactionTemplate; } public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; }