前面講解了基於JDBC驅動的Spring的持久化管理,本篇開始則着重介紹下與事務相關的操作。
通過本文你可以了解到:
1 Spring 事務管理的機制
2 基於JDBC持久化的事務管理
Spring的事務管理的機制
Spring本身並不提供事務管理,它只是把事務管理提交給事務管理器,而事務管理器則有多種實現,常見的就是基於JDBC的、Hibernate的、JPA以及JTA的。
操作流程可以參考下面的圖片:
其實還有好多種類的事務管理器,這里就不一一列舉了。
基於JDBC持久化的事務管理
基於JDBC的持久化,其實就是使用JDBC驅動,在利用spring模板的情況下實現的持久化。
與Hibernate不同的是,它沒有一些Session的概念以及實體關聯關系等,因此在查詢結果的時候,需要手動的進行轉換。
其他的方面來說,還是很簡單實用的。
下面看一下主要的代碼實現流程:
觀察上面的實現結構,整個代碼在DAO層的實現部分編寫,其中包括主要的兩個bean,一個是Spring的JDBC模板,一個是事務處理,這兩個bean都會依賴於dataSource。
DAO(data access object)數據訪問對象,一般應用架構都會設計這樣一層,用於存放於數據庫進行交互的代碼,以使應用層次化,便於管理和開發。
因此就好理解下面的配置文件了:
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- JDBC數據源配置 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123qwe"/> </bean> <!-- 配置DAO實現類,注入jdbcTemplate和transactionManager --> <bean id="newjdbcdao" class="com.spring.chap6.dao.NewJdbcImpl" > <property name="jdbcTemplate" ref="jdbcTemplate" /> <property name="transactionManager" ref="transactionManager"/> </bean> <!-- 將數據源注入到jdbctemplate中 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 將數據源注入到transactionManager中 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </beans>
下面是dao的接口部分,僅僅給出了查詢所有數據和插入一條數據的例子:
public interface NewJdbc { //插入數據 public void insertPerson(String id,String name,int age); //通過ID查詢數據 public void findAllPersons(); }
下面是重要的部分,查詢方法,僅僅通過模板提供了一個模板的使用樣例。
其中query方法含有兩個參數。
一個是查詢SQL語句,另一個是轉換類(用於把查詢結果ResultSet轉換成POJO類)。
而插入數據的方法中,使用了事務管理。
當執行new Integer("hello!")時,由於字符串無法轉換到整型出錯,會導致事務回滾,寫操作回滾。
其中的事務處理的源碼參考如下,還沒看明白,日后慢慢學習:
源碼給出,僅供參考:
public <T> T execute(TransactionCallback<T> action) throws TransactionException { if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); } else { TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { result = action.doInTransaction(status); } catch (RuntimeException ex) { // Transactional code threw application exception -> rollback rollbackOnException(status, ex); throw ex; } catch (Error err) { // Transactional code threw error -> rollback rollbackOnException(status, err); throw err; } catch (Exception ex) { // Transactional code threw unexpected exception -> rollback rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); } this.transactionManager.commit(status); return result; } }
下面則是DAO的實現部分的所有代碼:
public class NewJdbcImpl extends JdbcDaoSupport implements NewJdbc{ public DataSourceTransactionManager transactionManager; public void setTransactionManager(DataSourceTransactionManager transactionManager) { this.transactionManager = transactionManager; } /** * 插入數據 */ public void insertPerson(final String id,final String name,final int age){ TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); final JdbcTemplate jdbcTemplate = this.getJdbcTemplate(); transactionTemplate.execute(new TransactionCallbackWithoutResult() { protected void doInTransactionWithoutResult(TransactionStatus arg0) { //這個方法內部,是一個事務 jdbcTemplate.update("insert into persons(id,name,age) values (?,?,?)", id,name,age); new Integer("hello!"); } }); } /** * 查詢所有的數據 */ public void findAllPersons(){ List<Person> list = this.getJdbcTemplate().query("select * from persons", new PersonRowMapper()); for(Person p : list){ System.out.println("id:"+p.getId()+" name:"+p.getName()+" age:"+p.getAge()); } } /** * 轉換查詢結果 * @author xingoo */ class PersonRowMapper implements RowMapper{ public Object mapRow(ResultSet rs, int rowNum) throws SQLException { Person person = new Person(); person.setId(rs.getString(1)); person.setName(rs.getString(2)); person.setAge(rs.getInt(3)); return person; } } }
Person的POJO類:
public class Person { private String id; private String name; private int age; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
測試時使用的main方法(一直沒有使用單元測試,真不專業,習慣下次要改!)
public class test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); NewJdbc newjdbc = (NewJdbc)ctx.getBean("newjdbcdao"); newjdbc.insertPerson("005", "xingoo5", 25); newjdbc.findAllPersons(); } }
數據庫的SQL部分可以參考前一篇。
【這里遺留了一個問題,當事務失敗回滾時,查詢語句也無法執行了。難道是因為使用的同一個JdbcTempalte的緣故?這個問題需要看源碼探究,暫時記錄一下。】
1 根據網上搜索的資料:
根據默認規則,如果在執行回調方法的過程中拋出了未檢查異常,或者顯式調用了TransacationStatus.setRollbackOnly() 方法,則回滾事務;如果事務執行完成或者拋出了 checked 類型的異常,則提交事務。
但是回滾事務,為什么程序直接停止了呢。
參考
[1] Spring JDBC事務管理
[2] 《Spring in Action》