在Spring中數據庫事務是通過PlatformTransactionManager進行管理的,jdbcTemplate是不能支持事務的,而能夠支持事務的是org.springframework.transaction.support.TransactionTemplate模板,它是Spring所提供的事務管理器的模板
•事務的創建、提交和回滾是通過PlatformTransactionManager接口來完成的。
•當事務產生異常時會回滾事務,在默認的實現中所有的異常都會回滾。我們可以通過配置去修改在某些異常發生時回滾或者不回滾事務。
•當無異常時,會提交事務。
支持JTA事務,常用的是DataSourceTransactionManager,它繼承抽象事務管理器AbstractPlatformTransactionManager,而AbstractPlatformTransactionManager又實現了PlatformTransactionManager。這樣Spring就可以如同源碼中看到的那樣使用PlatformTransactionManager接口的方法,創建、提交或者回滾事務了。
配置事務管理器
MyBatis框架用得最多的事務管理器是DataSourceTransactionManager(org.springframework.jdbc.datasource.DataSourceTransactionManager),因此下面將以此例進行講解。如果使用的持久框架是Hibernate,那么你就要用到spring-orm包org.springframework.orm.hibernate4.HibernateTransactionManager了。它們大同小異,一般而言我們在使用時,還會加入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:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 數據庫連接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/springmvc?useSSL=false&serverTimezone=Hongkong&characterEncoding=utf-8&autoReconnect=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> <property name="maxActive" value="255"/> <property name="maxIdle" value="5"/> <property name="maxWait" value="10000"/> </bean> <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"/> </bean> </beans>
這里先引入了XML的命名空間,然后定義了數據庫連接池,於是使用了DataSourceTransactionManager去定義數據庫事務管理器,並且注入了數據庫連接池。這樣Spring就知道你已經將數據庫事務委托給事務管理器transactionManager管理了。在jdbcTemplate源碼分析時,筆者就已經指出,數據庫資源的產生和釋放如果沒有委托給數據庫管理器,那么就由jdbcTemplate管理,但是此時已經委托給了事務管理器,所以jdbcTemplate的數據庫資源和事務已經由事務管理器處理了。
在Spring中可以使用聲明式事務或者編程式事務,如今編程式事務幾乎不用了,因為它會產生冗余,代碼可讀性較差。聲明式事務又可以分為XML配置和注解事務,但XML方式也已經不常用了,目前主流方法是注解@Transactional。
用Java配置方式實現Spring數據庫事務
用Java配置的方式來實現Spring數據庫事務,需要在配置類中實現接口TransactionManagementConfigurer的annota-tionDrivenTransactionManager方法。Spring會把annotationDrivenTransactionManager方法返回的事務管理器作為程序中的事務管理器
代碼清單:使用Java配置方式實現Spring數據庫事物
package com.ssm.chapter13.config; import org.apache.commons.dbcp.BasicDataSourceFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.TransactionManagementConfigurer; import javax.sql.DataSource; import java.util.Properties; @Configuration @ComponentScan("com.ssm.chapter13.*") //使用事務驅動管理器 @EnableTransactionManagement public class JavaConfig implements TransactionManagementConfigurer { //數據源 private DataSource dataSource = null; /** * 配置數據源. * @return 數據源. */ @Bean(name = "dataSource") public DataSource initDataSource() { if (dataSource != null) { return dataSource; } Properties props = new Properties(); props.setProperty("driverClassName", "com.mysql.cj.jdbc.Driver"); props.setProperty("url", "jdbc:mysql://localhost:3306/springmvc?useSSL=false&serverTimezone=Hongkong&characterEncoding=utf-8&autoReconnect=true"); props.setProperty("username", "root"); props.setProperty("password", "123456"); props.setProperty("maxActive", "200"); props.setProperty("maxIdle", "20"); props.setProperty("maxWait", "30000"); try { dataSource = BasicDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } return dataSource; } /** * 配置jdbcTemplate * @return jdbcTemplate */ @Bean(name = "jdbcTemplate") public JdbcTemplate initjdbcTemplate() { JdbcTemplate jdbcTemplate = new JdbcTemplate(); jdbcTemplate.setDataSource(initDataSource()); return jdbcTemplate; } /** * 實現接口方法,使得返回數據庫事務管理器 */ @Override @Bean(name = "transactionManager") public PlatformTransactionManager annotationDrivenTransactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); //設置事務管理器管理的數據源 transactionManager.setDataSource(initDataSource()); return transactionManager; } }
實現了TransactionManagementConfigurer接口所定義的方法annotation DrivenTransactionManager,並且我們使用DataSourceTransactionManager去定義數據庫事務管理器的實例,然后把數據源設置給它。注意,使用注解@EnableTransactionManagement后,在Spring上下文中使用事務注解@Transactional,Spring就會知道使用這個數據庫事務管理器管理事務了。
編程式事務
編程式事務以代碼的方式管理事務,換句話說,事務將由開發者通過自己的代碼來實現,這里需要使用一個事務定義類接口——TransactionDefinition,暫時不進行深入的介紹,我們只要使用默認的實現類——DefaultTransactionDefinition就可以了。
代碼清單:編程式事務
package com.ssm.chapter13.main; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.DefaultTransactionDefinition; public class MainTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("ssm/chapter13/spring-cfg.xml");//ctx為Spring IoC容器 JdbcTemplate jdbcTemplate = ctx.getBean(JdbcTemplate.class); //事務定義類 TransactionDefinition def = new DefaultTransactionDefinition(); PlatformTransactionManager transactionManager = ctx.getBean(PlatformTransactionManager.class); TransactionStatus status = transactionManager.getTransaction(def); try { //執行SQL語句 jdbcTemplate.update("insert into t_role(role_name, note) " + "values('role_name_transactionManager', 'note_transactionManager')"); //提交事務 transactionManager.commit(status); } catch (Exception ex) { //回滾事務 transactionManager.rollback(status); } } }
從代碼中可以看到所有的事務都是由開發者自己進行控制的,由於事務已交由事務管理器管理,所以jdbcTemplate本身的數據庫資源已經由事務管理器管理,因此當它執行完insert語句時不會自動提交事務,這個時候需要使用事務管理器的commit方法,回滾事務需要使用rollback方法。
當然這是最簡單的使用方式,因為這個方式已經不是主流方式,甚至幾乎是不被推薦使用的方式,之所以介紹是因為它的代碼流程更為清晰,有助於未來對編程式事務的理解。
編程式事務是一種約定型的事務,在大部分情況下,當使用數據庫事務時,大部分的場景是在代碼中發生了異常時,需要回滾事務,而不發生異常時則是提交事務,從而保證數據庫數據的一致性。
