一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring + mybatis + mysql使用事物的幾種方法總結(jié)

Spring + mybatis + mysql使用事物的幾種方法總結(jié)

2021-04-29 10:57一灰灰Blog Java教程

這篇文章主要給大家總結(jié)介紹了關(guān)于Spring + mybatis + mysql使用事物的幾種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

本文主要記錄下spring是如何支持事物的,以及在spring結(jié)合mybatis時,可以怎么簡單的實現(xiàn)數(shù)據(jù)庫的事物功能,下面話不多說了,來一起看看詳細的介紹吧。

i. 前提

 

case1:兩張表的的事物支持情況

首先準(zhǔn)備兩張表,一個user表,一個story表,結(jié)構(gòu)如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
create table `user` (
 `id` int(11) unsigned not null auto_increment,
 `name` varchar(20) not null default '' comment '用戶名',
 `pwd` varchar(26) not null default '' comment '密碼',
 `isdeleted` tinyint(1) not null default '0',
 `created` varchar(13) not null default '0',
 `updated` varchar(13) not null default '0',
 primary key (`id`),
 key `name` (`name`)
) engine=innodb default charset=utf8mb4;
 
create table `story` (
 `id` int(11) unsigned not null auto_increment,
 `userid` int(20) unsigned not null default '0' comment '作者的userid',
 `name` varchar(20) not null default '' comment '作者名',
 `title` varchar(26) not null default '' comment '密碼',
 `story` text comment '故事內(nèi)容',
 `isdeleted` tinyint(1) not null default '0',
 `created` varchar(13) not null default '0',
 `updated` varchar(13) not null default '0',
 primary key (`id`),
 key `userid` (`userid`)
) engine=innodb default charset=utf8mb4;

我們的事物場景在于用戶修改name時,要求兩張表的name都需要一起修改,不允許出現(xiàn)不一致的情況

case2:單表的事物支持

轉(zhuǎn)賬,一個用戶減錢,另一個用戶加錢

?
1
2
3
4
5
6
7
8
9
10
create table `money` (
 `id` int(11) unsigned not null auto_increment,
 `name` varchar(20) not null default '' comment '用戶名',
 `money` int(26) not null default '0' comment '錢',
 `isdeleted` tinyint(1) not null default '0',
 `created` varchar(13) not null default '0',
 `updated` varchar(13) not null default '0',
 primary key (`id`),
 key `name` (`name`)
) engine=innodb default charset=utf8mb4;

相比上面那個case,這個更加簡單了,下面的實例則主要根據(jù)這個進行說明,至于case1,則留待擴展里面進行

首先是實現(xiàn)對應(yīng)的dao和entity

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@data
public class moneyentity implements serializable {
 private static final long serialversionuid = -7074788842783160025l;
 private int id;
 private string name;
 private int money;
 private int isdeleted;
 private int created;
 private int updated;
}
 
public interface moneydao {
 moneyentity querymoney(@param("id") int userid);
 // 加錢,負數(shù)時表示減錢
 int incrementmoney(@param("id") int userid, @param("addmoney") int addmoney);
}

對應(yīng)的mapper文件為

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<!doctype mapper
 public "-//mybatis.org//dtd mapper 3.0//en"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.git.hui.demo.mybatis.mapper.moneydao">
 <sql id="moneyentity">
 id, `name`, `money`, `isdeleted`, `created`, `updated`
 </sql>
 
 <select id="querymoney" resulttype="com.git.hui.demo.mybatis.entity.moneyentity">
 select
 <include refid="moneyentity"/>
 from money
 where id=#{id}
 
 </select>
 
 <update id="incrementmoney">
 update money
 set money=money + #{addmoney}
 where id=#{id}
 </update>
</mapper>

對應(yīng)的mybatis連接數(shù)據(jù)源的相關(guān)配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<bean class="org.springframework.beans.factory.config.propertyplaceholderconfigurer">
 <property name="locations">
 <value>classpath*:jdbc.properties</value>
 </property>
</bean>
 
 
<bean id="datasource" class="com.alibaba.druid.pool.druiddatasource" init-method="init" destroy-method="close">
 <property name="driverclassname" value="${driver}"/>
 <property name="url" value="${url}"/>
 <property name="username" value="${username}"/>
 <property name="password" value="${password}"/>
 
 <property name="filters" value="stat"/>
 
 <property name="maxactive" value="20"/>
 <property name="initialsize" value="1"/>
 <property name="maxwait" value="60000"/>
 <property name="minidle" value="1"/>
 
 <property name="timebetweenevictionrunsmillis" value="60000"/>
 <property name="minevictableidletimemillis" value="300000"/>
 
 <property name="validationquery" value="select 'x'"/>
 <property name="testwhileidle" value="true"/>
 <property name="testonborrow" value="false"/>
 <property name="testonreturn" value="false"/>
 
 <property name="poolpreparedstatements" value="true"/>
 <property name="maxpoolpreparedstatementperconnectionsize" value="50"/>
</bean>
 
 
<bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
 <property name="datasource" ref="datasource"/>
 <!-- 指定mapper文件 -->
 <property name="mapperlocations" value="classpath*:mapper/*.xml"/>
</bean>
 
 
<!-- 指定掃描dao -->
<bean class="org.mybatis.spring.mapper.mapperscannerconfigurer">
 <property name="basepackage" value="com.git.hui.demo.mybatis"/>
</bean>

ii. 實例演示

 

通過網(wǎng)上查詢,spring事物管理總共有四種方式,下面逐一進行演示,每種方式是怎么玩的,然后看實際項目中應(yīng)該如何抉擇

1. 硬編碼方式

編程式事物管理,既通過transactiontemplate來實現(xiàn)多個db操作的事物管理

a. 實現(xiàn)

那么,我們的轉(zhuǎn)賬case可以如下實現(xiàn)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
@repository
public class codedemo1 {
 @autowired
 private moneydao moneydao;
 @autowired
 private transactiontemplate transactiontemplate;
 /**
 * 轉(zhuǎn)賬
 *
 * @param inuserid
 * @param outuserid
 * @param paymoney
 * @param status 0 表示正常轉(zhuǎn)賬, 1 表示內(nèi)部拋出一個異常, 2 表示新開一個線程,修改inuserid的錢 +200, 3 表示新開一個線程,修改outuserid的錢 + 200
 */
 public void transfor(final int inuserid, final int outuserid, final int paymoney, final int status) {
 transactiontemplate.execute(new transactioncallbackwithoutresult() {
 protected void dointransactionwithoutresult(transactionstatus transactionstatus) {
 moneyentity entity = moneydao.querymoney(outuserid);
 if (entity.getmoney() > paymoney) { // 可以轉(zhuǎn)賬
 
 // 先減錢
 moneydao.incrementmoney(outuserid, -paymoney);
 
 
 testcase(inuserid, outuserid, status);
 
 // 再加錢
 moneydao.incrementmoney(inuserid, paymoney);
 system.out.println("轉(zhuǎn)賬完成! now: " + system.currenttimemillis());
 }
 }
 });
 }
 
 
 // 下面都是測試用例相關(guān)
 private void testcase(final int inuserid, final int outuserid, final int status) {
 if (status == 1) {
 throw new illegalargumentexception("轉(zhuǎn)賬異常!!!");
 } else if(status == 2) {
 addmoney(inuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 } else if (status == 3) {
 addmoney(outuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 }
 }
 
 
 public void addmoney(final int userid) {
 system.out.printf("內(nèi)部加錢: " + system.currenttimemillis());
 new thread(new runnable() {
 public void run() {
 moneydao.incrementmoney(userid, 200);
 system.out.println(" sub modify success! now: " + system.currenttimemillis());
 }
 }).start();
 }
}

主要看上面的transfor方法,內(nèi)部通過 transactiontemplate 來實現(xiàn)事物的封裝,內(nèi)部有三個db操作,一個查詢,兩個更新,具體分析后面說明

上面的代碼比較簡單了,唯一需要關(guān)注的就是transactiontemplate這個bean如何定義的,xml文件中與前面重復(fù)的就不貼了,直接貼上關(guān)鍵代碼, 一個是根據(jù)datasource創(chuàng)建的transactionmanager,一個則是根據(jù)transactionmanager創(chuàng)建的transactiontemplate

?
1
2
3
4
5
6
7
8
<!--編程式事物-->
<bean id="transactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
 <property name="datasource" ref="datasource"/>
</bean>
 
<bean id="transactiontemplate" class="org.springframework.transaction.support.transactiontemplate">
 <property name="transactionmanager" ref="transactionmanager"/>
</bean>

b. 測試用例

正常演示情況, 演示沒有任何異常,不考慮并發(fā)的情況

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@runwith(springjunit4classrunner.class)
@contextconfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource1.xml"})
public class codedemo1test {
 @autowired
 private codedemo1 codedemo1;
 
 @autowired
 private moneydao moneydao;
 
 @test
 public void testtransfor() {
 
 system.out.println("---------before----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 
 
 codedemo1.transfor(1, 2, 10, 0);
 
 system.out.println("---------after----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 }
}

輸出如下,兩個賬號的錢都沒有問題

---------before----------
id: 1 money = 10000
id: 2 money = 50000
轉(zhuǎn)賬完成! now: 1526130394266
---------after----------
id: 1 money = 10010
id: 2 money = 49990

轉(zhuǎn)賬過程中出現(xiàn)異常,特別是轉(zhuǎn)賬方錢已扣,收款方還沒收到錢時,也就是case中的status為1的場景

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 內(nèi)部拋異常的情況
@test
public void testtransforexception() {
 
 system.out.println("---------before----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 
 
 try {
 codedemo1.transfor(1, 2, 10, 1);
 } catch (exception e) {
 e.printstacktrace();
 }
 
 system.out.println("---------after----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
}

對此,我們希望把轉(zhuǎn)賬方的錢還回去, 輸出如下,發(fā)現(xiàn)兩個的錢都沒有變化

---------before----------
id: 1 money = 10010
id: 2 money = 49990
---------after----------
id: 1 money = 10010
java.lang.illegalargumentexception: 轉(zhuǎn)賬異常!!!
 ... // 省略異常信息
id: 2 money = 49990

當(dāng)status為2,表示在轉(zhuǎn)賬人錢已扣,收款人錢沒收到之間,又有人給收款人轉(zhuǎn)了200,此時根據(jù)mysql的鎖機制,另外人的轉(zhuǎn)賬應(yīng)該是立馬到的(因為收款人賬號沒有被鎖住),且金額不應(yīng)該有問題

輸出結(jié)果如下:

---------before----------
id: 1 money = 10010
id: 2 money = 49990
## 右邊是注釋: 轉(zhuǎn)賬過程中,另外存錢立馬到賬,沒有被鎖住
內(nèi)部加錢: 1526130827480
sub modify success! now: 1526130827500
## 存錢結(jié)束
轉(zhuǎn)賬完成! now: 1526130830488
---------after----------
id: 1 money = 10220
id: 2 money = 49980

當(dāng)status為3, 表示在轉(zhuǎn)賬人錢已扣,收款人錢沒收到之間,又有人給轉(zhuǎn)賬人轉(zhuǎn)了200,這時因為轉(zhuǎn)賬人的記錄以及被加了寫鎖,因此只能等待轉(zhuǎn)賬的事物提交之后,才有可能+200成功,當(dāng)然最終的金額也得一致

輸出結(jié)果如下

---------before----------
id: 1 money = 10220
id: 2 money = 49980
## 右邊是注釋:內(nèi)部存錢了,但沒有馬上成功
## 直到轉(zhuǎn)賬完成后,才立馬存成功,注意兩個時間戳
內(nèi)部加錢: 1526131101046
轉(zhuǎn)賬完成! now: 1526131104051
sub modify success! now: 1526131104053
---------after----------
id: 1 money = 10230
id: 2 money = 50170

c. 小結(jié)

至此,編程式事物已經(jīng)實例演示ok,從上面的過程,給人的感覺就和直接寫事物相關(guān)的sql一樣,

start transaction;

-- 這中間就是 transactiontemplate#execute 方法內(nèi)部的邏輯
-- 也就是需要事物管理的一組sql

commit;

2. 基于transactionproxyfactorybean方式

接下來的三個就是聲明式事物管理,這種用得也比較少,因為需要每個事物管理類,添加一個transactionproxyfactorybean

a. 實現(xiàn)

除了將 transactiontemplate 干掉,并將內(nèi)部的sql邏輯移除之外,對比前面的,發(fā)現(xiàn)基本上沒有太多差別

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class factorybeandemo2 {
 @autowired
 private moneydao moneydao;
 /**
 * 轉(zhuǎn)賬
 *
 * @param inuserid
 * @param outuserid
 * @param paymoney
 * @param status 0 表示正常轉(zhuǎn)賬, 1 表示內(nèi)部拋出一個異常, 2 表示新開一個線程,修改inuserid的錢 +200, 3 表示新開一個線程,修改outuserid的錢 + 200
 */
 public void transfor(final int inuserid, final int outuserid, final int paymoney, final int status) {
 
 moneyentity entity = moneydao.querymoney(outuserid);
 if (entity.getmoney() > paymoney) { // 可以轉(zhuǎn)賬
 
 // 先減錢
 moneydao.incrementmoney(outuserid, -paymoney);
 
 
 testcase(inuserid, outuserid, status);
 
 
 // 再加錢
 moneydao.incrementmoney(inuserid, paymoney);
 system.out.println("轉(zhuǎn)賬完成! now: " + system.currenttimemillis());
 }
 
 
 }
 
 
 private void testcase(final int inuserid, final int outuserid, final int status) {
 if (status == 1) {
 throw new illegalargumentexception("轉(zhuǎn)賬異常!!!");
 } else if (status == 2) {
 addmoney(inuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 } else if (status == 3) {
 addmoney(outuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 }
 }
 
 
 public void addmoney(final int userid) {
 system.out.println("內(nèi)部加錢: " + system.currenttimemillis());
 new thread(new runnable() {
 public void run() {
 moneydao.incrementmoney(userid, 200);
 system.out.println("sub modify success! now: " + system.currenttimemillis());
 }
 }).start();
 }
}

重點來了,主要是需要配置一個 transactionproxybeanfactory,我們知道beanfactory就是我們自己來創(chuàng)建bean的一種手段,相關(guān)的xml配置如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!--編程式事物-->
<bean id="transactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
 <property name="datasource" ref="datasource"/>
</bean>
 
<bean id="factorybeandemo2" class="com.git.hui.demo.mybatis.repository.transaction.factorybeandemo2"/>
 
<!-- 配置業(yè)務(wù)層的代理 -->
<bean id="factorybeandemoproxy" class="org.springframework.transaction.interceptor.transactionproxyfactorybean">
 <!-- 配置目標(biāo)對象 -->
 <property name="target" ref="factorybeandemo2" />
 <!-- 注入事務(wù)管理器 -->
 <property name="transactionmanager" ref="transactionmanager"/>
 <!-- 注入事務(wù)的屬性 -->
 <property name="transactionattributes">
 <props>
 <!--
 prop的格式:
 * propagation :事務(wù)的傳播行為
 * isotation :事務(wù)的隔離級別
 * readonly :只讀
 * -exception :發(fā)生哪些異常回滾事務(wù)
 * +exception :發(fā)生哪些異常不回滾事務(wù)
 -->
 <!-- 這個key對應(yīng)的就是目標(biāo)類中的方法-->
 <prop key="transfor">propagation_required</prop>
 <!-- <prop key="transfer">propagation_required,readonly</prop> -->
 <!-- <prop key="transfer">propagation_required,+java.lang.arithmeticexception</prop> -->
 </props>
 </property>
</bean>

通過上面的配置,大致可以了解到這個通過transactionproxyfactorybean就是創(chuàng)建了一個factorybeandemo2的代理類,這個代理類內(nèi)部封裝好事物相關(guān)的邏輯,可以看做是前面編程式的一種簡單通用抽象

b. 測試

測試代碼與前面基本相同,唯一的區(qū)別就是我們使用的應(yīng)該是上面beanfactory生成的bean,而不是直接使用factorybeandemo2

正常演示case:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@runwith(springjunit4classrunner.class)
@contextconfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource2.xml"})
public class factorybeandemo1test {
 @resource(name = "factorybeandemoproxy")
 private factorybeandemo2 factorybeandemo2;
 @autowired
 private moneydao moneydao;
 
 @test
 public void testtransfor() {
 system.out.println("---------before----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 factorybeandemo2.transfor(1, 2, 10, 0);
 system.out.println("---------after----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 }
}

輸出

---------before----------
id: 1 money = 10000
id: 2 money = 50000
轉(zhuǎn)賬完成! now: 1526132058886
---------after----------
id: 1 money = 10010
id: 2 money = 49990

status為1,內(nèi)部異常的情況下,我們希望錢也不會有問題

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@test
public void testtransforexception() {
 system.out.println("---------before----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 
 try {
 factorybeandemo2.transfor(1, 2, 10, 1);
 } catch (exception e) {
 system.out.println(e.getmessage());;
 }
 
 system.out.println("---------after----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
}

輸出為

---------before----------
id: 1 money = 10010
id: 2 money = 49990
轉(zhuǎn)賬異常!!!
---------after----------
id: 1 money = 10010
id: 2 money = 49990

status為2 時,分析結(jié)果與上面應(yīng)該相同,輸出如下

---------before----------
id: 1 money = 10010
id: 2 money = 49950
內(nèi)部加錢: 1526133325376
sub modify success! now: 1526133325387
轉(zhuǎn)賬完成! now: 1526133328381
---------after----------
id: 1 money = 10220
id: 2 money = 49940

status為3時,輸出

---------before----------
id: 1 money = 10220
id: 2 money = 49940
內(nèi)部加錢: 1526133373466
轉(zhuǎn)賬完成! now: 1526133376476
sub modify success! now: 1526133376480
---------after----------
id: 1 money = 10230
id: 2 money = 50130

c. 小結(jié)

transactionproxyfactorybean 的思路就是利用代理模式來實現(xiàn)事物管理,生成一個代理類,攔截目標(biāo)方法,將一組sql的操作封裝到事物中進行;相比較于硬編碼,無侵入,而且支持靈活的配置方式

缺點也顯而易見,每個都要進行配置,比較繁瑣

3. xml使用方式

spring有兩大特點,ioc和aop,對于事物這種情況而言,我們可不可以使用aop來做呢?

對于需要開啟事物的方法,攔截掉,執(zhí)行前開始事物,執(zhí)行完畢之后提交事物,出現(xiàn)異常時回滾

這樣一看,感覺還是蠻有希望的,而下面兩種姿勢正是這么玩的,因此需要加上aspect的依賴

?
1
2
3
4
5
<dependency>
 <groupid>org.aspectj</groupid>
 <artifactid>aspectjweaver</artifactid>
 <version>1.8.7</version>
</dependency>

a. 實現(xiàn)

java類與第二種完全一致,變動的只有xml

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 首先添加命名空間 -->
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemalocation="...
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx.xsd"
 
<!--對應(yīng)的事物通知和切面配置-->
<tx:advice id="txadvice" transaction-manager="transactionmanager">
 <tx:attributes>
 <!--
 propagation :事務(wù)傳播行為
 isolation :事務(wù)的隔離級別
 read-only :只讀
 rollback-for:發(fā)生哪些異常回滾
 no-rollback-for :發(fā)生哪些異常不回滾
 timeout :過期信息
 -->
 <tx:method name="transfor" propagation="required"/>
 </tx:attributes>
</tx:advice>
 
 
<!-- 配置切面 -->
<aop:config>
 <!-- 配置切入點 -->
 <aop:pointcut expression="execution(* com.git.hui.demo.mybatis.repository.transaction.xmldemo3.*(..))" id="pointcut1"/>
 <!-- 配置切面 -->
 <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
</aop:config>

觀察上面的配置,再想想第二種方式,思路都差不多了,但是這種方式明顯更加通用,通過切面和切點,可以減少大量的配置

b. 測試

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@runwith(springjunit4classrunner.class)
@contextconfiguration({"classpath*:spring/service.xml", "classpath*:test-datasource3.xml"})
public class xmlbeantest {
 @autowired
 private xmldemo3 xmldemo;
 
 @autowired
 private moneydao moneydao;
 
 
 @test
 public void testtransfor() {
 
 system.out.println("---------before----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 
 
 xmldemo.transfor(1, 2, 10, 0);
 
 system.out.println("---------after----------");
 system.out.println("id: 1 money = " + moneydao.querymoney(1).getmoney());
 system.out.println("id: 2 money = " + moneydao.querymoney(2).getmoney());
 }
}

這個測試起來,和一般的寫法就沒啥兩樣了,比第二種的factorybean的注入方式簡單點

正常輸出

---------before----------
id: 1 money = 10000
id: 2 money = 50000
轉(zhuǎn)賬完成! now: 1526135301273
---------after----------
id: 1 money = 10010
id: 2 money = 49990

status=1 出現(xiàn)異常時,輸出

---------before----------
id: 1 money = 10010
id: 2 money = 49990
轉(zhuǎn)賬異常!!!
---------after----------
id: 1 money = 10010
id: 2 money = 49990

status=2 轉(zhuǎn)賬過程中,又存錢的場景,輸出,與前面預(yù)期一致

---------before----------
id: 1 money = 10010
id: 2 money = 49990
內(nèi)部加錢: 1526135438403
sub modify success! now: 1526135438421
轉(zhuǎn)賬完成! now: 1526135441410
---------after----------
id: 1 money = 10220
id: 2 money = 49980

status=3 的輸出,與前面預(yù)期一致

---------before----------
id: 1 money = 10220
id: 2 money = 49980
內(nèi)部加錢: 1526135464341
轉(zhuǎn)賬完成! now: 1526135467349
sub modify success! now: 1526135467352
---------after----------
id: 1 money = 10230
id: 2 money = 50170

4. 注解方式

這個就是消滅xml,用注解來做的方式,就是將前面xml中的配置用 @transactional注解替換

a. 實現(xiàn)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@repository
public class annodemo4 {
 @autowired
 private moneydao moneydao;
 /**
 * 轉(zhuǎn)賬
 *
 * @param inuserid
 * @param outuserid
 * @param paymoney
 * @param status 0 表示正常轉(zhuǎn)賬, 1 表示內(nèi)部拋出一個異常, 2 表示新開一個線程,修改inuserid的錢 +200, 3 表示新開一個線程,修改outuserid的錢 + 200
 *
 *
 * transactional注解中的的屬性 propagation :事務(wù)的傳播行為 isolation :事務(wù)的隔離級別 readonly :只讀
 * rollbackfor :發(fā)生哪些異常回滾 norollbackfor :發(fā)生哪些異常不回滾
 * rollbackforclassname 根據(jù)異常類名回滾
 */
 @transactional(propagation = propagation.required, isolation = isolation.default, readonly = false)
 public void transfor(final int inuserid, final int outuserid, final int paymoney, final int status) {
 moneyentity entity = moneydao.querymoney(outuserid);
 if (entity.getmoney() > paymoney) { // 可以轉(zhuǎn)賬
 // 先減錢
 moneydao.incrementmoney(outuserid, -paymoney);
 testcase(inuserid, outuserid, status);
 // 再加錢
 moneydao.incrementmoney(inuserid, paymoney);
 system.out.println("轉(zhuǎn)賬完成! now: " + system.currenttimemillis());
 }
 }
 
 private void testcase(final int inuserid, final int outuserid, final int status) {
 if (status == 1) {
 throw new illegalargumentexception("轉(zhuǎn)賬異常!!!");
 } else if (status == 2) {
 addmoney(inuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 } else if (status == 3) {
 addmoney(outuserid);
 try {
 thread.sleep(3000);
 } catch (interruptedexception e) {
 e.printstacktrace();
 }
 }
 }
 
 private void addmoney(final int userid) {
 system.out.println("內(nèi)部加錢: " + system.currenttimemillis());
 new thread(new runnable() {
 public void run() {
 moneydao.incrementmoney(userid, 200);
 system.out.println("sub modify success! now: " + system.currenttimemillis());
 }
 }).start();
 }
}

因此需要在xml中配置,開啟事物注解

?
1
2
3
4
5
6
<!--編程式事物-->
<bean id="transactionmanager" class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
 <property name="datasource" ref="datasource"/>
</bean>
 
<tx:annotation-driven transaction-manager="transactionmanager"/>

這樣一看,就更加清晰了,實際項目中,xml和注解方式也是用得最多的場景了

b. 測試case

和第三種測試case完全相同, 輸出結(jié)果也一樣,直接省略

iii. 小結(jié)

 

上面說了spring中四種使用事物的姿勢,其中硬編碼方式可能是最好理解的,就相當(dāng)于將我們寫sql中,使用事物的方式直接翻譯成對應(yīng)的java代碼了;而factorybean方式相當(dāng)于特殊情況特殊對待,為每個事物來一個代理類來增強事物功能;后面的兩個則原理差不多都是利用事物通知(aop)來實現(xiàn),定義切點及相關(guān)信息

編程式:

  • 注入 transactiontemplate
  • 將利用事物的邏輯封裝到 transactiontemplate#execute方法內(nèi)

代理beanfactory:

  • 利用 transactionproxyfactorybean 為事物相關(guān)類生成代理
  • 使用方通過factorybean獲取代理類,作為使用的bean

xml配置:

  • 利用 tx標(biāo)簽 + aop方式來實現(xiàn)
  • <tx:advice> 標(biāo)簽定義事物通知,內(nèi)部可有較多的配置信息
  • <aop:config> 配置切點,切面

注解方式:

  • 在開啟事物的方法or類上添加 @transactional 注解即可
  • 開啟事物注解 <tx:annotation-driven transaction-manager="transactionmanager"/>

iv. 其他

 

1. 參考

文檔

spring事務(wù)管理的四種方式

源碼

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。

原文鏈接:https://liuyueyi.github.io/hexblog/2018/05/12/Spring學(xué)習(xí)之事物的使用姿勢/

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: ai换脸杨幂被c在线观看 | 国产精品亚洲精品日韩已方 | 99久久香蕉 | 国模一区二区三区视频一 | 俺去也亚洲色图 | 青青草国产青春综合久久 | 亚色九九九全国免费视频 | 男人插曲女人身体 | 二区三区不卡不卡视频 | 美人老师沦为 | 91桃花视频| 毛片免费视频观看 | 爆操萝莉 | 午夜国产精品影院在线观看 | 国产精品国产三级在线专区 | 视频污版 | 国色天香社区视频免费观看3 | 色依依视频视频在线观看 | 亚洲国产资源 | 十大网站免费货源 | 国产精品特黄毛片 | 石原莉奈adn093店长未婚妻 | 国产美女下面流出白浆视频 | 欧美3p大片在线观看完整版 | 亚洲欧洲网站 | 全黄毛片 | 猫咪社区在线播放 | www视频在线免费观看 | 国产成人99精品免费观看 | 精品一区二区三区在线视频观看 | 亚洲精品动漫在线观看 | 久久久精品免费免费直播 | 邪恶肉肉全彩色无遮琉璃神社 | 春意午夜影院 | 无限在线观看免费入口 | 日韩一级欧美一级一级国产 | 日韩一区二区三区精品 | porono日本人xxx| 香蕉视频在线观看网站 | 草莓香蕉绿巨人丝瓜榴莲污在线观看 | 日韩夫妻性生活 |