1. 程式人生 > >Spring的資料庫操作和事務管理

Spring的資料庫操作和事務管理

一.概述

spring的資料庫操作:spring是一個優秀的一站式框架,其中涵蓋了很多持久化框架模板物件,如JDBC,hibernate,mybatis物件模板,極大地簡化了資料庫操作。

事務:表示邏輯上的一組操作,這組操作要麼一起成功要麼一起失敗。最經典的就是銀行轉賬業務,假設甲轉一百塊給乙,在轉賬過程中,可能發生各種異常,若此時沒有事務,可能會發生甲的錢少了,而乙的錢沒有增加的事件。

二.動態代理手寫一個AOP的事務管理器(spring管理事務的機制)

假設現在在專案中不引入spring框架,我們來自己手寫一個面向切面的事務管理機制。事務管理應該放在service,dao層只負責資料庫的增刪改查,假設我們現在有一系列service程式碼(如UserService,CustomerService,MoneyService),要是在每一個service單獨實現事務管理,顯然過於複雜,不符合程式設計中程式碼複用的思想。那麼我們可以把這些重複的事務管理操作抽取到一個工廠類中,工廠負責生產Service,並根據是否有註解,區分這個Service是否要進行事務處理。我們可以用動態代理技術對Service進行代理,對需要進行事務處理的Service的方法進行增強。最終實現Service層的事務自動管理。

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中的事務操作解析

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進行完整的資料庫操作了。