Spring的資料庫操作和事務管理
一.概述
spring的資料庫操作:spring是一個優秀的一站式框架,其中涵蓋了很多持久化框架模板物件,如JDBC,hibernate,mybatis物件模板,極大地簡化了資料庫操作。
事務:表示邏輯上的一組操作,這組操作要麼一起成功要麼一起失敗。最經典的就是銀行轉賬業務,假設甲轉一百塊給乙,在轉賬過程中,可能發生各種異常,若此時沒有事務,可能會發生甲的錢少了,而乙的錢沒有增加的事件。
二.動態代理手寫一個AOP的事務管理器(spring管理事務的機制)
假設現在在專案中不引入spring框架,我們來自己手寫一個面向切面的事務管理機制。事務管理應該放在service,dao層只負責資料庫的增刪改查,假設我們現在有一系列service程式碼(如UserService,CustomerService,MoneyService),要是在每一個service單獨實現事務管理,顯然過於複雜,不符合程式設計中程式碼複用的思想。那麼我們可以把這些重複的事務管理操作抽取到一個工廠類中,工廠負責生產Service,並根據是否有註解,區分這個Service是否要進行事務處理。我們可以用動態代理技術對Service進行代理,對需要進行事務處理的Service的方法進行增強。最終實現Service層的事務自動管理。
三.spring中的事務操作解析public <T extends Service> T getService(Class<T> clazz){//把泛型限制在Service的子類中 try{ //--把所有Service都寫在配置檔案中,根據配置檔案建立具體的Service String infName = clazz.getSimpleName(); String implName = prop.getProperty(infName); final T service = (T) Class.forName(implName).newInstance(); //動態代理生成代理後的Service,實現事務管理自動化 //--為了實現AOP,生成service代理,根據註解確定在Service方法執行之前和之後做一些操作,比如:事務管理/記錄日誌/細粒度許可權控制.... T proxyService = (T) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces() , new InvocationHandler(){ //根據註解控制事務 public Object invoke(Object proxy, Method method,Object[] args) throws Throwable { if(method.isAnnotationPresent(Tran.class)){//如果有註解,則管理事務: try{ TransactionManager.startTran();//--自定義事務管理器,開啟事務 Object obj = method.invoke(service, args);//--真正執行方法 TransactionManager.commit();//--提交事務 return obj; }catch (InvocationTargetException e) { TransactionManager.rollback();//--回滾事務 throw new RuntimeException(e.getTargetException()); } catch (Exception e) { TransactionManager.rollback();//--回滾事務 throw new RuntimeException(e); }finally{ TransactionManager.release();//--釋放資源 } }else{//如果沒有註解,則不管理事務,直接執行方法 return method.invoke(service, args); } } }); return proxyService; }catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }
spring提供了很多主流的持久化框架模板,包括最基礎的JDBC,ORM框架hibernate,mybatis等。具體模板類對應如下:
下面以JDBC模板為例,解析spring持久化模板類的使用。
1.匯入必要的jar包
以mysql為例,故要匯入mysql驅動包
spring中jdbc操作相關包
spring事務操作相關包
2.編寫測試類:spring持久化模板使用非常簡單,獲得模板物件後直接就可以對資料庫進行增刪改查
@Test
// JDBC 模板的基本使用:
public void demo1(){
//註冊資料庫配置
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_crc");
dataSource.setUsername("root");
dataSource.setPassword("123");
//獲得模板類後就可以直接操作資料庫
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("insert into account values (null,?,?)", " crc
",10000d);
}
3.每一次使用都要配置資料庫顯然過於麻煩,故把資料庫的配置交給spring管理,在主配置檔案中配置如下bean,並使用c3p0代替原來的資料來源
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///spring_crc"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</bean>
4.還可以把模板類物件交給spring管理,由spring建立模板類物件
<! -- 配置 JDBC 模板 物件-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
5.此時JDBC模板的操作可以簡化成如下形式
@RunWith(SpringJUnit4ClassRunner. class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringDemo3 {
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
@Test
// 插入操作
public void demo1(){
jdbcTemplate.update("insert into account values (null,?,?)", " crc
",10000d);
}
@Test
// 修改操作
public void demo2(){
jdbcTemplate.update("update account set name=?,money =? where id = ?", "
hello",10000d,5);
}
@Test
// 刪除操作
public void demo3(){
jdbcTemplate.update("delete from account where id = ?", 5);
}
@Test
// 查詢一條記錄
public void demo4(){
Account account = jdbcTemplate.queryForObject("select * from account where
id = ?", new MyRowMapper(), 1);
System. out.println(account);
}
小結:至此,JDBC的簡單使用全部完畢,我們可以把資料庫配置和模板物件建立交給spring管理,最後只需要
呼叫簡單的API就可以完成增刪改查工作。
四.spring中的事務管理(主要解析宣告式事務)
1.事務的基本說明
事務的四個特性
原子性 :強調事務的不可分割.
一致性 :事務的執行的前後資料的完整性保持一致.
隔離性 :一個事務執行的過程中,不應該受到其他事務的干擾
永續性 :事務一旦結束,資料就持久到資料庫事務的安全性問題
髒讀 :一個事務讀到了另一個事務的未提交的資料
不可重複讀 :一個事務讀到了另一個事務已經提交的 update 的資料導致多次查詢結果不一致.
虛幻讀 :一個事務讀到了另一個事務已經提交的 insert 的資料導致多次查詢結果不一致.
事務的隔離級別
未提交讀 :髒讀,不可重複讀,虛讀都有可能發生
已提交讀 :避免髒讀。但是不可重複讀和虛讀有可能發生
可重複讀 :避免髒讀和不可重複讀.但是虛讀有可能發生.
序列化的 :避免以上所有讀問題.其中,
Mysql 預設:可重複讀
Oracle 預設:讀已提交2.spring管理事務的物件
org.springframework.jdbc.datasource. DataSourceTransactionManager 使用 Spring JDBC 或 iBatis 進行持久
化資料時使用
org.springframework.orm.hibernate3.
HibernateTransactionManager
使用Hibernate
版本進行持久化資料時使用
3.spring中可以定製的事務資訊
*
隔離級別
*
傳播行為
*
超時資訊
* 是否只讀
其中事務的傳播行為分為以下幾種
*
保證同一個事務中
PROPAGATION_REQUIRED
支援當前事務,如果不存在 就新建一個(預設)
PROPAGATION_SUPPORTS
支援當前事務,如果不存在,就不使用事務
PROPAGATION_MANDATORY
支援當前事務,如果不存在,丟擲異常
* 保證沒有在同一個事務中
PROPAGATION_REQUIRES_NEW
如果有事務存在,掛起當前事務,建立一個新的事務
PROPAGATION_NOT_SUPPORTED
以非事務方式執行,如果有事務存在,掛起當前事務PROPAGATION_NEVER
以非事務方式執行,如果有事務存在,丟擲異常
PROPAGATION_NESTED
如果當前事務存在,則巢狀事務執行
4.spring中管理事務的兩種方式spring中管理方式的方式可分為兩種:程式設計式和宣告式事務。由於程式設計式事務使用較少(不符合spring習慣),故接下來重點闡述
宣告式事務,宣告式事務又可以分為XML配置方式和註解配置方式。在配置之前首先應該明確,spring中管理事務物件的依賴關係
如圖:
現在以配置JDBC的事務管理為例,分別演示用XML方式和註解方式配置宣告式事務。如下:
XML方式
步驟1.匯入AOP開發相關的包,因為XML方式配置就是基於AOP開發思想的
aop 聯盟.jar;Spring-aop.jar;aspectJ.jar;spring-aspects.jar
步驟2.配置事務管理器,注意上圖,JDBC事務管理器依賴於DataSource存在,故在配置中需要體現此種關係
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
步驟3.配置事務的通知
<! -- 配置事務的增強 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<! --
方法中可以配置的幾個屬性
isolation="DEFAULT" 隔離級別
propagation="REQUIRED" 傳播行為
read-only="false" 只讀
timeout="-1" 過期時間
rollback-for="" -Exception
no-rollback-for="" +Exception
-->
<!-- 配置需要事務管理的方法,以轉賬方法為例-->
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
步驟4.配置將通知織入目標
<aop:config>
<!--表示把事務織入到AccountServiceIml類中,所有以tranfer開頭的方法中-->
<aop:pointcut expression="execution(*
com.cai.transaction.AccountServiceImpl.transfer(..))" id="pointcut1"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
註解方式
步驟1,導包和開啟事務管理與XML方式一致,不再累述。
步驟2.開啟註解管理事務
<! -- 開啟註解事務管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
步驟3.在使用的類中新增一個註解@Transactional
小結:對比以上兩種方式,可以得出一個結論,XML方式配置比較麻煩,但是比較統一,可以一次性解決問題。註解配置方式比較簡單,
且比較靈活,可以在比較少事務管理的時候使用註解方式。在對專案要求嚴謹時,使用XML方式。
五.總結
本部落格一開始先使用動態代理技術手寫了一個AOP的事務管理物件,讓人大致明白了spring內部是
怎麼進行事務管理的。然後簡單介紹了spring中資料庫操作的模板類及其API,最後使用兩種方式
配置事務管理操作。通過這樣一套流程,就可以使用spring進行完整的資料庫操作了。