簡單說一下事務(wù)
''是指作為單個邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行脚草。''
四大特性(ACID)
- 原子性(Atomicity): 指事務(wù)不可分割,要么全做披坏,要么都不做态坦。
- 一致性(Consisitency):指事務(wù)完成前后,所有數(shù)據(jù)保持一致狀態(tài)棒拂。譬如轉(zhuǎn)賬操作伞梯,前后總額不能改后變。
- 隔離性(Isolation):指開啟事務(wù)后帚屉,不受其它事務(wù)影響谜诫。
- 持久性(Durability):指事務(wù)完成后,對數(shù)據(jù)庫記錄的修改是永久的攻旦。
事務(wù)隔離級別
并發(fā)的情況下會破壞掉事務(wù)的特性喻旷,導(dǎo)致操作數(shù)據(jù)的不一致。情況有以下幾種牢屋。
-
臟讀
當(dāng)前事務(wù)讀到另一事務(wù)未提交的數(shù)據(jù)且预。
我向你轉(zhuǎn)賬1元,然后你看到了烙无,給我兩個饅頭锋谐,我拿到饅頭走了,接著把事務(wù)回滾了截酷。涮拗。 -
不可重復(fù)讀
在一個事務(wù)中,多次查詢同一記錄,結(jié)果不一致多搀。原因是別的事務(wù)修改了該條記錄并提交了歧蕉。 -
幻讀
開啟事務(wù)后,兩次統(tǒng)計(jì)數(shù)據(jù)庫記錄條數(shù)康铭,結(jié)果顯示不一致惯退。
在做統(tǒng)計(jì)時就很為難。
解決辦法利用鎖機(jī)制从藤,常用的是樂觀鎖催跪,為表中添加一個vision字段;或者直接指定會話的隔離級別夷野。
注:不可重復(fù)讀和幻讀都是讀取別人以及提交的事務(wù)懊蒸,只是前者是讀取一個數(shù)據(jù)項(xiàng),后者則是針對一批悯搔。
以往對事務(wù)的操作
JDBC中
//JDBC中骑丸,事務(wù)自動提交,得手動打開妒貌,(偽代碼)
Connection conn =
DataSourceUtils.getConnection();
//開啟事務(wù)
conn.setAutoCommit(false);
try {
Object obj =
callback.doInConnection(conn);
conn.commit(); //提交事務(wù)
return retVal;
}catch (Exception e) {
conn.rollback();//回滾事務(wù)
throw e;
}finally {
conn.close();
}
后來交由SessionFactory管理
Session session = factory.openSession();
try {
session.beginTransaction();
//do something
//session.save(obj);
session.beginTransaction().commit();
} catch (HibernateException e) {
session.beginTransaction().rollback();
e.printStackTrace();
} finally {
if(session != null && session.isOpen()) {
session.close();
}
}
再后來還是得不到滿足通危,因?yàn)槊總€方法都得這樣,重復(fù)代碼過多灌曙。而且擴(kuò)展性很差菊碟。于是想到了ThreadLocal模式,即將session放在ThreadLocal變量中在刺。并利用struts2攔截器逆害,自動開啟事務(wù),提交事務(wù)和關(guān)閉事務(wù)蚣驼。但是你需要每次都獲取當(dāng)前線程的session魄幕。
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String ret = null;
Session session = null;
try {
session = HibernateUtil.getSession();
session.beginTransaction();
System.out.println("********獲取session開啟事務(wù)***********");
ret = invocation.invoke();//該攔截器負(fù)責(zé)為這次請求創(chuàng)建一個session并管理業(yè)務(wù)和關(guān)閉session
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
} finally {
if(session != null && session.isOpen()) {
System.out.println("********事務(wù)結(jié)束關(guān)閉session***********");
session.close();
}
}
return ret;
}
對的,沒錯颖杏,像下面這樣獲取session纯陨。
//ThreadLocal local
public static Session getSession() {
Session session = local.get();
if(session == null || !session.isOpen()) {
session = factory.openSession();
local.set(session);
}
return session;
}
那么現(xiàn)在,Spring 來了输玷。你不再從應(yīng)用程序中主動獲取資源队丝,你只需要專注你的業(yè)務(wù)就好靡馁。像這種東西欲鹏,Spring來幫你搞定!
//既然是事務(wù)臭墨,Spring提供了一個管理事務(wù)的類赔嚎,我們將其注入IoC容器
<!-- 注入事務(wù)管理類 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
//上面是通過set方法注入了類依賴的對象,我們也要將其注入
//為類中注入了與數(shù)據(jù)庫連接用到的相關(guān)參數(shù)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<constructor-arg name="url" value="jdbc:mysql://127.0.0.1:3306/dbname"></constructor-arg>
<constructor-arg name="username" value="root"></constructor-arg>
<constructor-arg name="password" value="root"></constructor-arg>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
//這個是hibernate自身屬性配置
<!-- 配置信息建議查看源代碼 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- mappingResources 配置.hbm.xml
源碼解釋:Set Hibernate mapping resources to be found in the class path,
like "example.hbm.xml" or "mypackage/example.hbm.xml".
packagesToScan 指定注解類的包,自動掃描尤误,類似spring-component組件
都是數(shù)組類型
-->
<property name="mappingResources">
<list>
<value>User.hbm.xml</value>
</list>
</property>
</bean>
接下來我們要使用AOP侠畔,相關(guān)注釋很明顯了。
<!-- 定義事務(wù)通知损晤,定義了如何實(shí)施事務(wù)(實(shí)施事務(wù)的方法名和事務(wù)屬性)软棺,
需要使用事務(wù)管理器管理事務(wù),定義了如何選擇目標(biāo)對象的方法及事務(wù)的事務(wù)屬性
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="list*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 切入點(diǎn)和事務(wù)通知,切入點(diǎn)選擇實(shí)施事務(wù)的目標(biāo)對象 -->
<aop:config>
<aop:pointcut id="tx" expression="execution(* cn.smog.action.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="tx" />
</aop:config>
為了方便理解,我決定畫張圖摊趾。
AopTx.png