宅男在线永久免费观看网直播,亚洲欧洲日产国码无码久久99,野花社区在线观看视频,亚洲人交乣女bbw,一本一本久久a久久精品综合不卡

全部
常見問題
產(chǎn)品動態(tài)
精選推薦

事務(wù)管理入門

管理 管理 編輯 刪除

什么是事務(wù)?

我們在開發(fā)企業(yè)應(yīng)用時,通常業(yè)務(wù)人員的一個操作實際上是對數(shù)據(jù)庫讀寫的多步操作的結(jié)合。由于數(shù)據(jù)操作在順序執(zhí)行的過程中,任何一步操作都有可能發(fā)生異常,異常會導(dǎo)致后續(xù)操作無法完成,此時由于業(yè)務(wù)邏輯并未正確的完成,之前成功操作的數(shù)據(jù)并不可靠,如果要讓這個業(yè)務(wù)正確的執(zhí)行下去,通常有實現(xiàn)方式:

  1. 記錄失敗的位置,問題修復(fù)之后,從上一次執(zhí)行失敗的位置開始繼續(xù)執(zhí)行后面要做的業(yè)務(wù)邏輯。
  2. 在執(zhí)行失敗的時候,回退本次執(zhí)行的所有過程,讓操作恢復(fù)到原始狀態(tài),帶問題修復(fù)之后,重新執(zhí)行原來的業(yè)務(wù)邏輯。

事務(wù)就是針對上述方式2的實現(xiàn)。事務(wù),一般是指要做的或所做的事情,就是上面所說的業(yè)務(wù)人員的一個操作(比如電商系統(tǒng)中,一個創(chuàng)建訂單的操作包含了創(chuàng)建訂單、商品庫存的扣減兩個基本操作。如果創(chuàng)建訂單成功,庫存扣減失敗,那么就會出現(xiàn)商品超賣的問題,所以最基本的最發(fā)就是需要為這兩個操作用事務(wù)包括起來,保證這兩個操作要么都成功,要么都失敗)。

這樣的場景在實際開發(fā)過程中非常多,所以今天就來一起學(xué)習(xí)一下Spring Boot中的事務(wù)管理如何使用!

快速入門

在Spring Boot中,當(dāng)我們使用了spring-boot-starter-jdbcspring-boot-starter-data-jpa依賴的時候,框架會自動默認(rèn)分別注入DataSourceTransactionManager或JpaTransactionManager。所以我們不需要任何額外配置就可以用@Transactional注解進行事務(wù)的使用。

我們以之前實現(xiàn)的《使用Spring Data JPA訪問MySQL》的示例作為基礎(chǔ)工程進行事務(wù)的使用學(xué)習(xí)。在該樣例工程中,我們引入了spring-data-jpa,并創(chuàng)建了User實體以及對User的數(shù)據(jù)訪問對象UserRepository,在單元測試類中實現(xiàn)了使用UserRepository進行數(shù)據(jù)讀寫的單元測試用例,如下:

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

	@Autowired
	private UserRepository userRepository;

	@Test
	public void test() throws Exception {

		// 創(chuàng)建10條記錄
		userRepository.save(new User("AAA", 10));
		userRepository.save(new User("BBB", 20));
		userRepository.save(new User("CCC", 30));
		userRepository.save(new User("DDD", 40));
		userRepository.save(new User("EEE", 50));
		userRepository.save(new User("FFF", 60));
		userRepository.save(new User("GGG", 70));
		userRepository.save(new User("HHH", 80));
		userRepository.save(new User("III", 90));
		userRepository.save(new User("JJJ", 100));

		// 省略后續(xù)的一些驗證操作
	}

}

可以看到,在這個單元測試用例中,使用UserRepository對象連續(xù)創(chuàng)建了10個User實體到數(shù)據(jù)庫中,下面我們?nèi)藶榈膩碇圃煲恍┊惓?,看看會發(fā)生什么情況。

通過@Max(50)來為User的age設(shè)置最大值為50,這樣通過創(chuàng)建時User實體的age屬性超過50的時候就可以觸發(fā)異常產(chǎn)生。

@Entity
@Data
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @Max(50)
    private Integer age;

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

}

執(zhí)行測試用例,可以看到控制臺中拋出了如下異常,關(guān)于age字段的錯誤:

2020-07-09 11:55:29.581 ERROR 24424 --- [           main] o.h.i.ExceptionMapperStandardImpl        : HHH000346: Error during managed flush [Validation failed for classes [com.didispace.chapter310.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage='最大不能超過50', propertyPath=age, rootBeanClass=class com.didispace.chapter310.User, messageTemplate='{javax.validation.constraints.Max.message}'}
]]

此時查數(shù)據(jù)庫中的User表:

b5d2620241223172138147.png

可以看到,測試用例執(zhí)行到一半之后因為異常中斷了,前5條數(shù)據(jù)正確插入而后5條數(shù)據(jù)沒有成功插入,如果這10條數(shù)據(jù)需要全部成功或者全部失敗,那么這時候就可以使用事務(wù)來實現(xiàn),做法非常簡單,我們只需要在test函數(shù)上添加@Transactional注解即可。

@Test
@Transactional
public void test() throws Exception {

    // 省略測試內(nèi)容

}

再來執(zhí)行該測試用例,可以看到控制臺中輸出了回滾日志(Rolled back transaction for test context)。

2020-07-09 12:48:23.831  INFO 24889 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@f6efaab testClass = Chapter310ApplicationTests, testInstance = com.didispace.chapter310.Chapter310ApplicationTests@60816371, testMethod = test@Chapter310ApplicationTests, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@3c19aaa5 testClass = Chapter310ApplicationTests, locations = '{}', classes = '{class com.didispace.chapter310.Chapter310Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@34cd072c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@528931cf, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2353b3e6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7ce6a65d], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@4b85edeb]; rollback [true]
2020-07-09 12:48:24.011  INFO 24889 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@f6efaab testClass = Chapter310ApplicationTests, testInstance = com.didispace.chapter310.Chapter310ApplicationTests@60816371, testMethod = test@Chapter310ApplicationTests, testException = javax.validation.ConstraintViolationException: Validation failed for classes [com.didispace.chapter310.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
	ConstraintViolationImpl{interpolatedMessage='最大不能超過50', propertyPath=age, rootBeanClass=class com.didispace.chapter310.User, messageTemplate='{javax.validation.constraints.Max.message}'}
], mergedContextConfiguration = [WebMergedContextConfiguration@3c19aaa5 testClass = Chapter310ApplicationTests, locations = '{}', classes = '{class com.didispace.chapter310.Chapter310Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@34cd072c, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@528931cf, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2353b3e6, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@7ce6a65d], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]

事務(wù)詳解

上面的例子中我們使用了默認(rèn)的事務(wù)配置,可以滿足一些基本的事務(wù)需求,但是當(dāng)我們項目較大較復(fù)雜時(比如,有多個數(shù)據(jù)源等),這時候需要在聲明事務(wù)時,指定不同的事務(wù)管理器。對于不同數(shù)據(jù)源的事務(wù)管理配置可以見《Spring Data JPA的多數(shù)據(jù)源配置》中的設(shè)置。在聲明事務(wù)時,只需要通過value屬性指定配置的事務(wù)管理器名即可,例如:@Transactional(value="transactionManagerPrimary")。

除了指定不同的事務(wù)管理器之后,還能對事務(wù)進行隔離級別和傳播行為的控制,下面分別詳細(xì)解釋:

隔離級別

隔離級別是指若干個并發(fā)的事務(wù)之間的隔離程度,與我們開發(fā)時候主要相關(guān)的場景包括:臟讀取、重復(fù)讀、幻讀。

我們可以看org.springframework.transaction.annotation.Isolation枚舉類中定義了五個表示隔離級別的值:

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);
}
  • DEFAULT:這是默認(rèn)值,表示使用底層數(shù)據(jù)庫的默認(rèn)隔離級別。對大部分?jǐn)?shù)據(jù)庫而言,通常這值就是:READ_COMMITTED。
  • READ_UNCOMMITTED:該隔離級別表示一個事務(wù)可以讀取另一個事務(wù)修改但還沒有提交的數(shù)據(jù)。該級別不能防止臟讀和不可重復(fù)讀,因此很少使用該隔離級別。
  • READ_COMMITTED:該隔離級別表示一個事務(wù)只能讀取另一個事務(wù)已經(jīng)提交的數(shù)據(jù)。該級別可以防止臟讀,這也是大多數(shù)情況下的推薦值。
  • REPEATABLE_READ:該隔離級別表示一個事務(wù)在整個過程中可以多次重復(fù)執(zhí)行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復(fù)讀。
  • SERIALIZABLE:所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會用到該級別。

指定方法:通過使用isolation屬性設(shè)置,例如:

@Transactional(isolation = Isolation.DEFAULT)

傳播行為

所謂事務(wù)的傳播行為是指,如果在開始當(dāng)前事務(wù)之前,一個事務(wù)上下文已經(jīng)存在,此時有若干選項可以指定一個事務(wù)性方法的執(zhí)行行為。

我們可以看org.springframework.transaction.annotation.Propagation枚舉類中定義了6個表示傳播行為的枚舉值:

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
}
  • REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。
  • SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
  • MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
  • REQUIRES_NEW:創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
  • NOT_SUPPORTED:以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。
  • NEVER:以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常。
  • NESTED:如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果當(dāng)前沒有事務(wù),則該取值等價于REQUIRED。

指定方法:通過使用propagation屬性設(shè)置,例如:

@Transactional(propagation = Propagation.REQUIRED)

注:本文轉(zhuǎn)載自“程序猿DD”,如有侵權(quán),請聯(lián)系刪除!

請登錄后查看

哈哈哈醬 最后編輯于2024-12-23 17:30:39

快捷回復(fù)
回復(fù)
回復(fù)
回復(fù)({{post_count}}) {{!is_user ? '我的回復(fù)' :'全部回復(fù)'}}
排序 默認(rèn)正序 回復(fù)倒序 點贊倒序

{{item.user_info.nickname ? item.user_info.nickname : item.user_name}} LV.{{ item.user_info.bbs_level || item.bbs_level }}

作者 管理員 企業(yè)

{{item.floor}}# 同步到gitee 已同步到gitee {{item.is_suggest == 1? '取消推薦': '推薦'}}
{{item.is_suggest == 1? '取消推薦': '推薦'}}
沙發(fā) 板凳 地板 {{item.floor}}#
{{item.user_info.title || '暫無簡介'}}
附件

{{itemf.name}}

{{item.created_at}}  {{item.ip_address}}
打賞
已打賞¥{{item.reward_price}}
{{item.like_count}}
{{item.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)

{{itemc.user_info.nickname}}

{{itemc.user_name}}

回復(fù) {{itemc.comment_user_info.nickname}}

附件

{{itemf.name}}

{{itemc.created_at}}
打賞
已打賞¥{{itemc.reward_price}}
{{itemc.like_count}}
{{itemc.showReply ? '取消回復(fù)' : '回復(fù)'}}
刪除
回復(fù)
回復(fù)
查看更多
打賞
已打賞¥{{reward_price}}
1143
{{like_count}}
{{collect_count}}
添加回復(fù) ({{post_count}})

相關(guān)推薦

快速安全登錄

使用微信掃碼登錄
{{item.label}} 加精
{{item.label}} {{item.label}} 板塊推薦 常見問題 產(chǎn)品動態(tài) 精選推薦 首頁頭條 首頁動態(tài) 首頁推薦
取 消 確 定
回復(fù)
回復(fù)
問題:
問題自動獲取的帖子內(nèi)容,不準(zhǔn)確時需要手動修改. [獲取答案]
答案:
提交
bug 需求 取 消 確 定
打賞金額
當(dāng)前余額:¥{{rewardUserInfo.reward_price}}
{{item.price}}元
請輸入 0.1-{{reward_max_price}} 范圍內(nèi)的數(shù)值
打賞成功
¥{{price}}
完成 確認(rèn)打賞

微信登錄/注冊

切換手機號登錄

{{ bind_phone ? '綁定手機' : '手機登錄'}}

{{codeText}}
切換微信登錄/注冊
暫不綁定
CRMEB客服

CRMEB咨詢熱線 咨詢熱線

400-8888-794

微信掃碼咨詢

CRMEB開源商城下載 源碼下載 CRMEB幫助文檔 幫助文檔
返回頂部 返回頂部
CRMEB客服