<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          支付寶一面:多線程事務(wù)怎么回滾?說用 @Transactional 可以回去等...

          共 10399字,需瀏覽 21分鐘

           ·

          2023-01-05 15:24

          點(diǎn)擊關(guān)注公眾號,Java干貨 及時送達(dá) 68c7ebe651913877f2034056c0fe9815.webp

          推薦閱讀:

          學(xué)習(xí)?Spring Cloud 微服務(wù)的最佳姿勢!

          Spring Cloud 2022 正式發(fā)布!


          背景介紹

          1,最近有一個大數(shù)據(jù)量插入的操作入庫的業(yè)務(wù)場景,需要先做一些其他修改操作,然后在執(zhí)行插入操作,由于插入數(shù)據(jù)可能會很多,用到多線程去拆分?jǐn)?shù)據(jù)并行處理來提高響應(yīng)時間,如果有一個線程執(zhí)行失敗,則全部回滾。

          2,在spring中可以使用@Transactional注解去控制事務(wù),使出現(xiàn)異常時會進(jìn)行回滾,在多線程中,這個注解則不會生效,如果主線程需要先執(zhí)行一些修改數(shù)據(jù)庫的操作,當(dāng)子線程在進(jìn)行處理出現(xiàn)異常時,主線程修改的數(shù)據(jù)則不會回滾,導(dǎo)致數(shù)據(jù)錯誤。

          3,下面用一個簡單示例演示多線程事務(wù)。

          公用的類和方法

                
                /**
          ?*?平均拆分list方法.
          ?*?@param?source
          ?*?@param?n
          ?*?@param?<T>
          ?*?@return
          ?*/
          public?static?<T>?List<List<T>>?averageAssign(List<T>?source,int?n){
          ????List<List<T>>?result=new?ArrayList<List<T>>();
          ????int?remaider=source.size()%n;
          ????int?number=source.size()/n;
          ????int?offset=0;//偏移量
          ????for(int?i=0;i<n;i++){
          ????????List<T>?value=null;
          ????????if(remaider>0){
          ????????????value=source.subList(i*number+offset,?(i+1)*number+offset+1);
          ????????????remaider--;
          ????????????offset++;
          ????????}else{
          ????????????value=source.subList(i*number+offset,?(i+1)*number+offset);
          ????????}
          ????????result.add(value);
          ????}
          ????return?result;
          }
          /**??線程池配置
          ?*?@version?V1.0
          ?*/
          public?class?ExecutorConfig?{
          ????private?static?int?maxPoolSize?=?Runtime.getRuntime().availableProcessors();
          ????private?volatile?static?ExecutorService?executorService;
          ????public?static?ExecutorService?getThreadPool()?{
          ????????if?(executorService?==?null){
          ????????????synchronized?(ExecutorConfig.class){
          ????????????????if?(executorService?==?null){
          ????????????????????executorService?=??newThreadPool();
          ????????????????}
          ????????????}
          ????????}
          ????????return?executorService;
          ????}

          ????private?static??ExecutorService?newThreadPool(){
          ????????int?queueSize?=?500;
          ????????int?corePool?=?Math.min(5,?maxPoolSize);
          ????????return?new?ThreadPoolExecutor(corePool,?maxPoolSize,?10000L,?TimeUnit.MILLISECONDS,
          ????????????new?LinkedBlockingQueue<>(queueSize),new?ThreadPoolExecutor.AbortPolicy());
          ????}
          ????private?ExecutorConfig(){}
          }
          /**?獲取sqlSession
          ?*?@author?86182
          ?*?@version?V1.0
          ?*/
          @Component
          public?class?SqlContext?{
          ????@Resource
          ????private?SqlSessionTemplate?sqlSessionTemplate;

          ????public?SqlSession?getSqlSession(){
          ????????SqlSessionFactory?sqlSessionFactory?=?sqlSessionTemplate.getSqlSessionFactory();
          ????????return?sqlSessionFactory.openSession();
          ????}
          }

          另外,如果你近期準(zhǔn)備面試跳槽,建議在 Java面試庫 小程序在線刷題,涵蓋 2000+?道 Java 面試題,幾乎覆蓋了所有主流技術(shù)面試題。

          示例事務(wù)不成功操作

                
                ??/**
          ?*?測試多線程事務(wù).
          ?*?@param?employeeDOList
          ?*/
          @Override
          @Transactional
          public?void?saveThread(List<EmployeeDO>?employeeDOList)?{
          ????try?{
          ????????//先做刪除操作,如果子線程出現(xiàn)異常,此操作不會回滾
          ????????this.getBaseMapper().delete(null);
          ????????//獲取線程池
          ????????ExecutorService?service?=?ExecutorConfig.getThreadPool();
          ????????//拆分?jǐn)?shù)據(jù),拆分5份
          ????????List<List<EmployeeDO>>?lists=averageAssign(employeeDOList,?5);
          ????????//執(zhí)行的線程
          ????????Thread?[]threadArray?=?new?Thread[lists.size()];
          ????????//監(jiān)控子線程執(zhí)行完畢,再執(zhí)行主線程,要不然會導(dǎo)致主線程關(guān)閉,子線程也會隨著關(guān)閉
          ????????CountDownLatch?countDownLatch?=?new?CountDownLatch(lists.size());
          ????????AtomicBoolean?atomicBoolean?=?new?AtomicBoolean(true);
          ????????for?(int?i?=0;i<lists.size();i++){
          ????????????if?(i==lists.size()-1){
          ????????????????atomicBoolean.set(false);
          ????????????}
          ????????????List<EmployeeDO>?list??=?lists.get(i);
          ????????????threadArray[i]?=??new?Thread(()?->?{
          ????????????????try?{
          ?????????????????//最后一個線程拋出異常
          ????????????????????if?(!atomicBoolean.get()){
          ????????????????????????throw?new?ServiceException("001","出現(xiàn)異常");
          ????????????????????}
          ????????????????????//批量添加,mybatisPlus中自帶的batch方法
          ????????????????????this.saveBatch(list);
          ????????????????}finally?{
          ????????????????????countDownLatch.countDown();
          ????????????????}

          ????????????});
          ????????}
          ????????for?(int?i?=?0;?i?<lists.size();?i++){
          ????????????service.execute(threadArray[i]);
          ????????}
          ????????//當(dāng)子線程執(zhí)行完畢時,主線程再往下執(zhí)行
          ????????countDownLatch.await();
          ????????System.out.println("添加完畢");
          ????}catch?(Exception?e){
          ????????log.info("error",e);
          ????????throw?new?ServiceException("002","出現(xiàn)異常");
          ????}finally?{
          ?????????connection.close();
          ?????}
          }

          數(shù)據(jù)庫中存在一條數(shù)據(jù):

          d2bee8bc0968f3f538e6fba9e9a5e55c.webp

          Spring Boot 基礎(chǔ)就不介紹了,推薦下這個實(shí)戰(zhàn)教程:https://github.com/javastacks/spring-boot-best-practice

                
                //測試用例
          @RunWith(SpringRunner.class)
          @SpringBootTest(classes?=?{?ThreadTest01.class,?MainApplication.class})
          public?class?ThreadTest01?{

          ????@Resource
          ????private?EmployeeBO?employeeBO;

          ????/**
          ?????*???測試多線程事務(wù).
          ?????*?@throws?InterruptedException
          ?????*/
          ????@Test
          ????public??void?MoreThreadTest2()?throws?InterruptedException?{
          ????????int?size?=?10;
          ????????List<EmployeeDO>?employeeDOList?=?new?ArrayList<>(size);
          ????????for?(int?i?=?0;?i<size;i++){
          ????????????EmployeeDO?employeeDO?=?new?EmployeeDO();
          ????????????employeeDO.setEmployeeName("lol"+i);
          ????????????employeeDO.setAge(18);
          ????????????employeeDO.setGender(1);
          ????????????employeeDO.setIdNumber(i+"XX");
          ????????????employeeDO.setCreatTime(Calendar.getInstance().getTime());
          ????????????employeeDOList.add(employeeDO);
          ????????}
          ????????try?{
          ????????????employeeBO.saveThread(employeeDOList);
          ????????????System.out.println("添加成功");
          ????????}catch?(Exception?e){
          ????????????e.printStackTrace();
          ????????}
          ????}
          }

          測試結(jié)果:

          f9033af86a94e2849d61800f206f4e77.webp69a8a1bf10c88040c09f4995e38adfb7.webp

          可以發(fā)現(xiàn)子線程組執(zhí)行時,有一個線程執(zhí)行失敗,其他線程也會拋出異常,但是主線程中執(zhí)行的刪除操作,沒有回滾,@Transactional注解沒有生效。

          點(diǎn)擊關(guān)注公眾號,Java干貨 及時送達(dá) 68c7ebe651913877f2034056c0fe9815.webp

          使用sqlSession控制手動提交事務(wù)

                
                ?@Resource
          ??SqlContext?sqlContext;
          ?/**
          ?*?測試多線程事務(wù).
          ?*?@param?employeeDOList
          ?*/
          @Override
          public?void?saveThread(List<EmployeeDO>?employeeDOList)?throws?SQLException?{
          ????//?獲取數(shù)據(jù)庫連接,獲取會話(內(nèi)部自有事務(wù))
          ????SqlSession?sqlSession?=?sqlContext.getSqlSession();
          ????Connection?connection?=?sqlSession.getConnection();
          ????try?{
          ????????//?設(shè)置手動提交
          ????????connection.setAutoCommit(false);
          ????????//獲取mapper
          ????????EmployeeMapper?employeeMapper?=?sqlSession.getMapper(EmployeeMapper.class);
          ????????//先做刪除操作
          ????????employeeMapper.delete(null);
          ????????//獲取執(zhí)行器
          ????????ExecutorService?service?=?ExecutorConfig.getThreadPool();
          ????????List<Callable<Integer>>?callableList??=?new?ArrayList<>();
          ????????//拆分list
          ????????List<List<EmployeeDO>>?lists=averageAssign(employeeDOList,?5);
          ????????AtomicBoolean?atomicBoolean?=?new?AtomicBoolean(true);
          ????????for?(int?i?=0;i<lists.size();i++){
          ????????????if?(i==lists.size()-1){
          ????????????????atomicBoolean.set(false);
          ????????????}
          ????????????List<EmployeeDO>?list??=?lists.get(i);
          ????????????//使用返回結(jié)果的callable去執(zhí)行,
          ????????????Callable<Integer>?callable?=?()?->?{
          ????????????????//讓最后一個線程拋出異常
          ????????????????if?(!atomicBoolean.get()){
          ????????????????????throw?new?ServiceException("001","出現(xiàn)異常");
          ????????????????}
          ??????????????return?employeeMapper.saveBatch(list);
          ????????????};
          ????????????callableList.add(callable);
          ????????}
          ????????//執(zhí)行子線程
          ???????List<Future<Integer>>?futures?=?service.invokeAll(callableList);
          ????????for?(Future<Integer>?future:futures)?{
          ????????//如果有一個執(zhí)行不成功,則全部回滾
          ????????????if?(future.get()<=0){
          ????????????????connection.rollback();
          ?????????????????return;
          ????????????}
          ????????}
          ????????connection.commit();
          ????????System.out.println("添加完畢");
          ????}catch?(Exception?e){
          ????????connection.rollback();
          ????????log.info("error",e);
          ????????throw?new?ServiceException("002","出現(xiàn)異常");
          ????}finally?{
          ?????????connection.close();
          ?????}
          }
          //?sql
          <insert?id="saveBatch"?parameterType="List">
          ?INSERT?INTO
          ?employee?(employee_id,age,employee_name,birth_date,gender,id_number,creat_time,update_time,status)
          ?values
          ?????<foreach?collection="list"?item="item"?index="index"?separator=",">
          ?????(
          ?????#{item.employeeId},
          ?????#{item.age},
          ?????#{item.employeeName},
          ?????#{item.birthDate},
          ?????#{item.gender},
          ?????#{item.idNumber},
          ?????#{item.creatTime},
          ?????#{item.updateTime},
          ?????#{item.status}
          ?????????)
          ?????</foreach>
          ?</insert>

          數(shù)據(jù)庫中一條數(shù)據(jù):

          7b84b60adb8ddc4b8841541147546858.webp

          測試結(jié)果:拋出異常,

          32b06f173e6f05763760647f5f03f354.webp

          刪除操作的數(shù)據(jù)回滾了,數(shù)據(jù)庫中的數(shù)據(jù)依舊存在,說明事務(wù)成功了。

          7f26d2043a782b7c08071084cd575445.webp另外,如果你近期準(zhǔn)備面試跳槽,建議在 Java面試庫 小程序在線刷題,涵蓋 2000+?道 Java 面試題,幾乎覆蓋了所有主流技術(shù)面試題。

          成功操作示例:

                ?@Resource
          SqlContext?sqlContext;
          /**
          ?*?測試多線程事務(wù).
          ?*?@param?employeeDOList
          ?*/
          @Override
          public?void?saveThread(List<EmployeeDO>?employeeDOList)?throws?SQLException?{
          ????//?獲取數(shù)據(jù)庫連接,獲取會話(內(nèi)部自有事務(wù))
          ????SqlSession?sqlSession?=?sqlContext.getSqlSession();
          ????Connection?connection?=?sqlSession.getConnection();
          ????try?{
          ????????//?設(shè)置手動提交
          ????????connection.setAutoCommit(false);
          ????????EmployeeMapper?employeeMapper?=?sqlSession.getMapper(EmployeeMapper.class);
          ????????//先做刪除操作
          ????????employeeMapper.delete(null);
          ????????ExecutorService?service?=?ExecutorConfig.getThreadPool();
          ????????List<Callable<Integer>>?callableList??=?new?ArrayList<>();
          ????????List<List<EmployeeDO>>?lists=averageAssign(employeeDOList,?5);
          ????????for?(int?i?=0;i<lists.size();i++){
          ????????????List<EmployeeDO>?list??=?lists.get(i);
          ????????????Callable<Integer>?callable?=?()?->?employeeMapper.saveBatch(list);
          ????????????callableList.add(callable);
          ????????}
          ????????//執(zhí)行子線程
          ???????List<Future<Integer>>?futures?=?service.invokeAll(callableList);
          ????????for?(Future<Integer>?future:futures)?{
          ????????????if?(future.get()<=0){
          ????????????????connection.rollback();
          ?????????????????return;
          ????????????}
          ????????}
          ????????connection.commit();
          ????????System.out.println("添加完畢");
          ????}catch?(Exception?e){
          ????????connection.rollback();
          ????????log.info("error",e);
          ????????throw?new?ServiceException("002","出現(xiàn)異常");
          ???????//?throw?new?ServiceException(ExceptionCodeEnum.EMPLOYEE_SAVE_OR_UPDATE_ERROR);
          ????}
          }

          測試結(jié)果:

          f4b7a9c7436dd4a29170138ff905048a.webp

          數(shù)據(jù)庫中數(shù)據(jù):

          刪除的刪除了,添加的添加成功了,測試成功。

          bb3b6008037129ae93d8c25ef1e39c56.webp

          版權(quán)聲明:本文為CSDN博主「weixin_43225491」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。原文鏈接:https://blog.csdn.net/weixin_43225491/article/details/117705686

          End


          Spring 旗下最牛逼的國產(chǎn)項(xiàng)目!

          23 種設(shè)計(jì)模式實(shí)戰(zhàn)(很全)

          Spring Boot 3.0 正式發(fā)布,王炸!!

          Spring Cloud Alibaba 最新重磅發(fā)布!

          Spring 6.0 正式發(fā)布,新王登基!!

          65b2c058bc42192ff23bf1e7b68f8078.webpSpring Cloud 微服務(wù)最新課程!
          瀏覽 52
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中国 免费XXXX18在线观看 | 人妻巨大乳HD免费看 | 操骚逼视屏 | 国产精品久久7777 | 波多野结衣无码一区二区 |