1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringBoot+ Dubbo + Mybatis + Nacos +Seata整合來實現(xiàn)Dubbo分布式事務

        共 15857字,需瀏覽 32分鐘

         ·

        2021-02-15 18:25

        作者:請叫我東子

        https://blog.csdn.net/u010046908/article/details/100536439

        1.簡介

        本文主要介紹SpringBoot2.1.5 + Dubbo 2.7.3 + Mybatis 3.4.2 + Nacos 1.1.3 +Seata 0.8.0整合來實現(xiàn)Dubbo分布式事務管理,使用Nacos 作為 Dubbo和Seata的注冊中心和配置中心,使用 MySQL 數(shù)據(jù)庫和 MyBatis來操作數(shù)據(jù)。

        如果你還對SpringBoot、Dubbo、Nacos、SeataMybatis 不是很了解的話,這里我為大家整理個它們的官網(wǎng)網(wǎng)站,如下

        • SpringBoot:https://spring.io/projects/spring-boot
        • Dubbo:http://dubbo.apache.org/en-us/
        • Nacos:https://nacos.io/zh-cn/docs/quick-start.html
        • Seata:https://github.com/seata/seata/wiki/Home_Chinese
        • MyBatis:http://www.mybatis.org/mybatis-3/zh/index.html

        在這里我們就不一個一個介紹它們是怎么使用和原理,詳細請學習官方文檔,在這里我將開始對它們進行整合,完成一個簡單的案例,來讓大家了解Seata來實現(xiàn)Dubbo分布式事務管理的基本流程。

        2.環(huán)境準備

        2.1 下載nacos并安裝啟動

        nacos下載:https://github.com/alibaba/nacos/releases/tag/1.1.3

        Nacos 快速入門:https://nacos.io/en-us/docs/quick-start.html

        sh?startup.sh?-m?standalone
        1

        在瀏覽器打開Nacos web 控制臺:http://192.168.10.200:8848/nacos/index.html

        輸入nacos的賬號和密碼 分別為nacos:nacos

        這是時候naocs 就正常啟動了。

        2.2 下載seata0.8.0 并安裝啟動

        2.2.1 在 Seata Release 下載最新版的 Seata Server 并解壓得到如下目錄:

        .
        ├──bin
        ├──conf
        ├──file_store
        └──lib
        12345

        2.2.2 修改 conf/registry.conf 配置,

        目前seata支持如下的file、nacos 、apollo、zk、consul的注冊中心和配置中心。這里我們以nacos 為例。將 type 改為 nacos

        registry?{
        ??#?file?nacos
        ??type?=?"nacos"

        ??nacos?{
        ????serverAddr?=?"192.168.10.200:8848"
        ????namespace?=?"public"
        ????cluster?=?"default"
        ??}
        ??file?{
        ????name?=?"file.conf"
        ??}
        }

        config?{
        ??#?file、nacos?、apollo、zk、consul
        ??type?=?"nacos"

        ??nacos?{
        ????serverAddr?=?"192.168.10.200:8848"
        ????namespace?=?"public"
        ????cluster?=?"default"
        ??}

        ??file?{
        ????name?=?"file.conf"
        ??}
        }

        1234567891011121314151617181920212223242526272829
        • serverAddr = “192.168.10.200:8848” :nacos 的地址
        • namespace = “public” :nacos的命名空間默認為public
        • cluster = “default” :集群設置未默認 default

        注意:seata0.9.0之后,配置如下, 其中namespace = ""

        registry?{
        ??#?file?nacos
        ??type?=?"nacos"

        ??nacos?{
        ????serverAddr?=?"192.168.10.200:8848"
        ????namespace?=?""
        ????cluster?=?"default"
        ??}
        ??file?{
        ????name?=?"file.conf"
        ??}
        }

        config?{
        ??#?file、nacos?、apollo、zk、consul
        ??type?=?"nacos"

        ??nacos?{
        ????serverAddr?=?"192.168.10.200:8848"
        ????namespace?=?""
        ??}

        ??file?{
        ????name?=?"file.conf"
        ??}
        }
        123456789101112131415161718192021222324252627

        2.2.3 修改 conf/nacos-config.txt配置

        注意:如果你的seata是1.1之后的版本請按照如下導入方式操作

        1. 下載https://github.com/seata/seata/blob/develop/script/config-center/目錄下的config.txt文件放入到seata-server目錄下的conf中,可更改文件名為nacos-config.txt。
        2. 下載https://github.com/seata/seata/tree/develop/script/config-center/nacos/目錄下的nacos-config.py文件(window可用python命令執(zhí)行,linux也可執(zhí)行,前提先有安裝python)或者nacos-config.sh文件(window下可放在git命令行執(zhí)行,linux可執(zhí)行文件)
        transport.type=TCP
        transport.server=NIO
        transport.heartbeat=true
        transport.thread-factory.boss-thread-prefix=NettyBoss
        transport.thread-factory.worker-thread-prefix=NettyServerNIOWorker
        transport.thread-factory.server-executor-thread-prefix=NettyServerBizHandler
        transport.thread-factory.share-boss-worker=false
        transport.thread-factory.client-selector-thread-prefix=NettyClientSelector
        transport.thread-factory.client-selector-thread-size=1
        transport.thread-factory.client-worker-thread-prefix=NettyClientWorkerThread
        transport.thread-factory.boss-thread-size=1
        transport.thread-factory.worker-thread-size=8
        transport.shutdown.wait=3
        service.vgroup_mapping.order-service-seata-service-group=default
        service.vgroup_mapping.account-service-seata-service-group=default
        service.vgroup_mapping.storage-service-seata-service-group=default
        service.vgroup_mapping.business-service-seata-service-group=default
        service.enableDegrade=false
        service.disable=false
        service.max.commit.retry.timeout=-1
        service.max.rollback.retry.timeout=-1
        client.async.commit.buffer.limit=10000
        client.lock.retry.internal=10
        client.lock.retry.times=30
        store.mode=db
        store.file.dir=file_store/data
        store.file.max-branch-session-size=16384
        store.file.max-global-session-size=512
        store.file.file-write-buffer-cache-size=16384
        store.file.flush-disk-mode=async
        store.file.session.reload.read_size=100
        store.db.driver-class-name=com.mysql.jdbc.Driver
        store.db.datasource=dbcp
        store.db.db-type=mysql
        store.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=true
        store.db.user=lidong
        store.db.password=cwj887766@@
        store.db.min-conn=1
        store.db.max-conn=3
        store.db.global.table=global_table
        store.db.branch.table=branch_table
        store.db.query-limit=100
        store.db.lock-table=lock_table
        recovery.committing-retry-period=1000
        recovery.asyn-committing-retry-period=1000
        recovery.rollbacking-retry-period=1000
        recovery.timeout-retry-period=1000
        transaction.undo.data.validation=true
        transaction.undo.log.serialization=jackson
        transaction.undo.log.save.days=7
        transaction.undo.log.delete.period=86400000
        transaction.undo.log.table=undo_log
        transport.serialization=seata
        transport.compressor=none
        metrics.enabled=false
        metrics.registry-type=compact
        metrics.exporter-list=prometheus
        metrics.exporter-prometheus-port=9898
        service.default.grouplist=127.0.0.1:8091
        1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859

        這里主要修改了如下幾項:

        • store.mode :存儲模式 默認file 這里我修改為db 模式 ,并且需要三個表global_tablebranch_tablelock_table
        • store.db.driver-class-name:默認沒有,會報錯。添加了 com.mysql.jdbc.Driver
        • store.db.datasource=dbcp :數(shù)據(jù)源 dbcp
        • store.db.db-type=mysql : 存儲數(shù)據(jù)庫的類型為mysql
        • store.db.url=jdbc:mysql://192.168.10.200:3306/seata?useUnicode=true : 修改為自己的數(shù)據(jù)庫url、port、數(shù)據(jù)庫名稱
        • store.db.user=lidong :數(shù)據(jù)庫的賬號
        • store.db.password=cwj887766@@ :數(shù)據(jù)庫的密碼
        • service.vgroup_mapping.order-service-seata-service-group=default
        • service.vgroup_mapping.account-service-seata-service-group=default
        • service.vgroup_mapping.storage-service-seata-service-group=default
        • service.vgroup_mapping.business-service-seata-service-group=default

        也可以在 Nacos 配置頁面添加,data-id 為 service.vgroup_mapping.${YOUR_SERVICE_NAME}-seata-service-group, group 為 SEATA_GROUP, 如果不添加該配置,啟動后會提示no available server to connect to services-server

        注意: 配置文件末尾有空行,需要刪除,否則會提示失敗,盡管實際上是成功的

        db模式下的所需的三個表的數(shù)據(jù)庫腳本位于seata\conf\db_store.sql

        global_table的表結構

        CREATE?TABLE?`global_table`?(
        ??`xid`?varchar(128)?NOT?NULL,
        ??`transaction_id`?bigint(20)?DEFAULT?NULL,
        ??`status`?tinyint(4)?NOT?NULL,
        ??`application_id`?varchar(64)?DEFAULT?NULL,
        ??`transaction_service_group`?varchar(64)?DEFAULT?NULL,
        ??`transaction_name`?varchar(64)?DEFAULT?NULL,
        ??`timeout`?int(11)?DEFAULT?NULL,
        ??`begin_time`?bigint(20)?DEFAULT?NULL,
        ??`application_data`?varchar(2000)?DEFAULT?NULL,
        ??`gmt_create`?datetime?DEFAULT?NULL,
        ??`gmt_modified`?datetime?DEFAULT?NULL,
        ??PRIMARY?KEY?(`xid`),
        ??KEY?`idx_gmt_modified_status`?(`gmt_modified`,`status`),
        ??KEY?`idx_transaction_id`?(`transaction_id`)
        )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;

        1234567891011121314151617

        branch_table的表結構

        CREATE?TABLE?`branch_table`?(
        ??`branch_id`?bigint(20)?NOT?NULL,
        ??`xid`?varchar(128)?NOT?NULL,
        ??`transaction_id`?bigint(20)?DEFAULT?NULL,
        ??`resource_group_id`?varchar(32)?DEFAULT?NULL,
        ??`resource_id`?varchar(256)?DEFAULT?NULL,
        ??`lock_key`?varchar(128)?DEFAULT?NULL,
        ??`branch_type`?varchar(8)?DEFAULT?NULL,
        ??`status`?tinyint(4)?DEFAULT?NULL,
        ??`client_id`?varchar(64)?DEFAULT?NULL,
        ??`application_data`?varchar(2000)?DEFAULT?NULL,
        ??`gmt_create`?datetime?DEFAULT?NULL,
        ??`gmt_modified`?datetime?DEFAULT?NULL,
        ??PRIMARY?KEY?(`branch_id`),
        ??KEY?`idx_xid`?(`xid`)
        )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;


        123456789101112131415161718

        lock_table的表結構

        create?table?`lock_table`?(
        ??`row_key`?varchar(128)?not?null,
        ??`xid`?varchar(96),
        ??`transaction_id`?long?,
        ??`branch_id`?long,
        ??`resource_id`?varchar(256)?,
        ??`table_name`?varchar(32)?,
        ??`pk`?varchar(32)?,
        ??`gmt_create`?datetime?,
        ??`gmt_modified`?datetime,
        ??primary?key(`row_key`)
        );
        123456789101112

        2.2.4 將 Seata 配置添加到 Nacos 中

        使用命令如下

        cd?conf
        sh?nacos-config.sh?localhost
        12

        成功后會提示

        init?nacos?config?finished,?please?start?seata-server
        1

        在 Nacos 管理頁面應該可以看到有 59 個 Group 為SEATA_GROUP的配置

        2.2.5 啟動 Seata Server

        使用db 模式啟動

        ?cd?..
        ?sh?./bin/seata-server.sh
        12

        這時候在 Nacos 的服務列表下面可以看到一個名為serverAddr的服務

        3 案例分析

        參考官網(wǎng)中用戶購買商品的業(yè)務邏輯。整個業(yè)務邏輯由4個微服務提供支持:

        • 庫存服務:扣除給定商品的存儲數(shù)量。
        • 訂單服務:根據(jù)購買請求創(chuàng)建訂單。
        • 帳戶服務:借記用戶帳戶的余額。
        • 業(yè)務服務:處理業(yè)務邏輯。

        請求邏輯架構

        3.1 github地址

        springboot-dubbo-seata:https://github.com/lidong1665/springboot-dubbo-seata

        • samples-common :公共模塊
        • samples-account :用戶賬號模塊
        • samples-order :訂單模塊
        • samples-storage :庫存模塊
        • samples-business :業(yè)務模塊

        3.2 賬戶服務:AccountDubboService

        /**
        ?*?@Author:?lidong
        ?*?@Description??賬戶服務接口
        ?*?@Date?Created?in?2019/9/5?16:37
        ?*/

        public?interface?AccountDubboService?{

        ????/**
        ?????*?從賬戶扣錢
        ?????*/

        ????ObjectResponse?decreaseAccount(AccountDTO?accountDTO);
        }
        123456789101112

        3.3 訂單服務:OrderDubboService

        /**
        ?*?@Author:?lidong
        ?*?@Description??訂單服務接口
        ?*?@Date?Created?in?2019/9/5?16:28
        ?*/

        public?interface?OrderDubboService?{

        ????/**
        ?????*?創(chuàng)建訂單
        ?????*/

        ????ObjectResponse?createOrder(OrderDTO?orderDTO);
        }
        123456789101112

        3.4 庫存服務:StorageDubboService

        /**
        ?*?@Author:?lidong
        ?*?@Description??庫存服務
        ?*?@Date?Created?in?2019/9/5?16:22
        ?*/

        public?interface?StorageDubboService?{

        ????/**
        ?????*?扣減庫存
        ?????*/

        ????ObjectResponse?decreaseStorage(CommodityDTO?commodityDTO);
        }

        12345678910111213

        3.5 業(yè)務服務:BusinessService

        /**
        ?*?@Author:?lidong
        ?*?@Description
        ?*?@Date?Created?in?2019/9/5?17:17
        ?*/

        public?interface?BusinessService?{

        ????/**
        ?????*?出處理業(yè)務服務
        ??????*?@param?businessDTO
        ?????*?@return
        ?????*/

        ????ObjectResponse?handleBusiness(BusinessDTO?businessDTO);
        }
        123456789101112131415

        業(yè)務邏輯的具體實現(xiàn)主要體現(xiàn)在 訂單服務的實現(xiàn)和業(yè)務服務的實現(xiàn)

        訂單服務的實現(xiàn)

        @Service
        public?class?TOrderServiceImpl?extends?ServiceImpl<TOrderMapper,?TOrder>?implements?ITOrderService?{

        ????@Reference(version?=?"1.0.0")
        ????private?AccountDubboService?accountDubboService;

        ????/**
        ?????*?創(chuàng)建訂單
        ?????*?@Param:??OrderDTO??訂單對象
        ?????*?@Return:??OrderDTO??訂單對象
        ?????*/

        ????@Override
        ????public?ObjectResponse?createOrder(OrderDTO?orderDTO)?{
        ????????ObjectResponse?response?=?new?ObjectResponse<>();
        ????????//扣減用戶賬戶
        ????????AccountDTO?accountDTO?=?new?AccountDTO();
        ????????accountDTO.setUserId(orderDTO.getUserId());
        ????????accountDTO.setAmount(orderDTO.getOrderAmount());
        ????????ObjectResponse?objectResponse?=?accountDubboService.decreaseAccount(accountDTO);

        ????????//生成訂單號
        ????????orderDTO.setOrderNo(UUID.randomUUID().toString().replace("-",""));
        ????????//生成訂單
        ????????TOrder?tOrder?=?new?TOrder();
        ????????BeanUtils.copyProperties(orderDTO,tOrder);
        ????????tOrder.setCount(orderDTO.getOrderCount());
        ????????tOrder.setAmount(orderDTO.getOrderAmount().doubleValue());
        ????????try?{
        ????????????baseMapper.createOrder(tOrder);
        ????????}?catch?(Exception?e)?{
        ????????????response.setStatus(RspStatusEnum.FAIL.getCode());
        ????????????response.setMessage(RspStatusEnum.FAIL.getMessage());
        ????????????return?response;
        ????????}

        ????????if?(objectResponse.getStatus()?!=?200)?{
        ????????????response.setStatus(RspStatusEnum.FAIL.getCode());
        ????????????response.setMessage(RspStatusEnum.FAIL.getMessage());
        ????????????return?response;
        ????????}

        ????????response.setStatus(RspStatusEnum.SUCCESS.getCode());
        ????????response.setMessage(RspStatusEnum.SUCCESS.getMessage());
        ????????return?response;
        ????}
        }
        12345678910111213141516171819202122232425262728293031323334353637383940414243444546

        整個業(yè)務的實現(xiàn)邏輯

        @Service
        @Slf4j
        public?class?BusinessServiceImpl?implements?BusinessService{

        ????@Reference(version?=?"1.0.0")
        ????private?StorageDubboService?storageDubboService;

        ????@Reference(version?=?"1.0.0")
        ????private?OrderDubboService?orderDubboService;

        ????private?boolean?flag;

        ????/**
        ?????*?處理業(yè)務邏輯
        ?????*?@Param:
        ?????*?@Return:
        ?????*/


        ????@GlobalTransactional(timeoutMills?=?300000,?name?=?"dubbo-gts-seata-example")
        ????@Override
        ????public?ObjectResponse?handleBusiness(BusinessDTO?businessDTO)?{
        ????????log.info("開始全局事務,XID?=?"?+?RootContext.getXID());
        ????????ObjectResponse?objectResponse?=?new?ObjectResponse<>();
        ????????//1、扣減庫存
        ????????CommodityDTO?commodityDTO?=?new?CommodityDTO();
        ????????commodityDTO.setCommodityCode(businessDTO.getCommodityCode());
        ????????commodityDTO.setCount(businessDTO.getCount());
        ????????ObjectResponse?storageResponse?=?storageDubboService.decreaseStorage(commodityDTO);
        ????????//2、創(chuàng)建訂單
        ????????OrderDTO?orderDTO?=?new?OrderDTO();
        ????????orderDTO.setUserId(businessDTO.getUserId());
        ????????orderDTO.setCommodityCode(businessDTO.getCommodityCode());
        ????????orderDTO.setOrderCount(businessDTO.getCount());
        ????????orderDTO.setOrderAmount(businessDTO.getAmount());
        ????????ObjectResponse?response?=?orderDubboService.createOrder(orderDTO);

        ????????//打開注釋測試事務發(fā)生異常后,全局回滾功能
        //????????if?(!flag)?{
        //??????????? throw new RuntimeException("測試拋異常后,分布式事務回滾!");
        //????????}

        ????????if?(storageResponse.getStatus()?!=?200?||?response.getStatus()?!=?200)?{
        ????????????throw?new?DefaultException(RspStatusEnum.FAIL);
        ????????}

        ????????objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode());
        ????????objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage());
        ????????objectResponse.setData(response.getData());
        ????????return?objectResponse;
        ????}
        }
        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

        3.6 使用seata的分布式事務解決方案處理dubbo的分布式事務

        我們只需要在業(yè)務處理的方法handleBusiness添加一個注解 @GlobalTransactional

        @GlobalTransactional(timeoutMills?=?300000,?name?=?"dubbo-gts-seata-example")
        ????@Override
        ????public?ObjectResponse?handleBusiness(BusinessDTO?businessDTO)?{
        ????
        ????}
        12345
        • timeoutMills: 超時時間
        • name :事務名稱

        3.7 準備數(shù)據(jù)庫

        注意: MySQL必須使用InnoDB engine.

        創(chuàng)建數(shù)據(jù)庫 并導入數(shù)據(jù)庫腳本

        DROP?DATABASE?IF?EXISTS?seata;
        CREATE?DATABASE?seata;
        USE?seata;

        DROP?TABLE?IF?EXISTS?`t_account`;
        CREATE?TABLE?`t_account`?(
        ??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
        ??`user_id`?varchar(255)?DEFAULT?NULL,
        ??`amount`?double(14,2)?DEFAULT?'0.00',
        ??PRIMARY?KEY?(`id`)
        )?ENGINE=InnoDB?AUTO_INCREMENT=2?DEFAULT?CHARSET=utf8;

        --?----------------------------
        --?Records?of?t_account
        --?----------------------------
        INSERT?INTO?`t_account`?VALUES?('1',?'1',?'4000.00');

        --?----------------------------
        --?Table?structure?for?t_order
        --?----------------------------
        DROP?TABLE?IF?EXISTS?`t_order`;
        CREATE?TABLE?`t_order`?(
        ??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
        ??`order_no`?varchar(255)?DEFAULT?NULL,
        ??`user_id`?varchar(255)?DEFAULT?NULL,
        ??`commodity_code`?varchar(255)?DEFAULT?NULL,
        ??`count`?int(11)?DEFAULT?'0',
        ??`amount`?double(14,2)?DEFAULT?'0.00',
        ??PRIMARY?KEY?(`id`)
        )?ENGINE=InnoDB?AUTO_INCREMENT=64?DEFAULT?CHARSET=utf8;

        --?----------------------------
        --?Records?of?t_order
        --?----------------------------

        --?----------------------------
        --?Table?structure?for?t_storage
        --?----------------------------
        DROP?TABLE?IF?EXISTS?`t_storage`;
        CREATE?TABLE?`t_storage`?(
        ??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
        ??`commodity_code`?varchar(255)?DEFAULT?NULL,
        ??`name`?varchar(255)?DEFAULT?NULL,
        ??`count`?int(11)?DEFAULT?'0',
        ??PRIMARY?KEY?(`id`),
        ??UNIQUE?KEY?`commodity_code`?(`commodity_code`)
        )?ENGINE=InnoDB?AUTO_INCREMENT=2?DEFAULT?CHARSET=utf8;

        --?----------------------------
        --?Records?of?t_storage
        --?----------------------------
        INSERT?INTO?`t_storage`?VALUES?('1',?'C201901140001',?'水杯',?'1000');

        --?----------------------------
        --?Table?structure?for?undo_log
        --?注意此處0.3.0+?增加唯一索引?ux_undo_log
        --?----------------------------
        DROP?TABLE?IF?EXISTS?`undo_log`;
        CREATE?TABLE?`undo_log`?(
        ??`id`?bigint(20)?NOT?NULL?AUTO_INCREMENT,
        ??`branch_id`?bigint(20)?NOT?NULL,
        ??`xid`?varchar(100)?NOT?NULL,
        ??`context`?varchar(128)?NOT?NULL,
        ??`rollback_info`?longblob?NOT?NULL,
        ??`log_status`?int(11)?NOT?NULL,
        ??`log_created`?datetime?NOT?NULL,
        ??`log_modified`?datetime?NOT?NULL,
        ??`ext`?varchar(100)?DEFAULT?NULL,
        ??PRIMARY?KEY?(`id`),
        ??UNIQUE?KEY?`ux_undo_log`?(`xid`,`branch_id`)
        )?ENGINE=InnoDB?AUTO_INCREMENT=1?DEFAULT?CHARSET=utf8;

        --?----------------------------
        --?Records?of?undo_log
        --?----------------------------
        SET?FOREIGN_KEY_CHECKS=1;
        12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576

        會看到如上的4個表

        +-------------------------+
        |?Tables_in_seata?????????|
        +-------------------------+
        |?t_account???????????????|
        |?t_order?????????????????|
        |?t_storage???????????????|
        |?undo_log????????????????|
        +-------------------------+
        12345678

        這里為了簡化我將這個三張表創(chuàng)建到一個庫中,使用是三個數(shù)據(jù)源來實現(xiàn)。

        3.8 我們以賬號服務samples-account為例 ,分析需要注意的配置項目

        3.8.1 引入的依賴

        "1.0"?encoding="UTF-8"?>
        "http://maven.apache.org/POM/4.0.0"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        ?????????xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd">
        ????4.0.0
        ????
        ????????org.springframework.boot
        ????????spring-boot-starter-parent
        ????????2.1.5.RELEASE
        ?????????
        ????

        ????springboot-dubbo-seata
        ????pom
        ????springboot-dubbo-seata
        ????io.seata
        ????1.0.0
        ????samples-account
        ????
        ????????2.1.5.RELEASE
        ????????1.8
        ????????1.1.10
        ????????1.3.2
        ????????2.3
        ????????0.2.3
        ????????1.16.22
        ????????2.7.3
        ????????1.1.3
        ????????0.8.0
        ????????4.1.32.Final
        ????


        ????
        ????????
        ????????????org.springframework.boot
        ????????????spring-boot-starter-web
        ????????????${springboot.verison}
        ????????


        ????????
        ????????????org.springframework.boot
        ????????????spring-boot-starter
        ????????????${springboot.verison}
        ????????


        ????????
        ????????????org.springframework.boot
        ????????????spring-boot-starter-test
        ????????????${springboot.verison}
        ????????


        ????????
        ????????????com.alibaba
        ????????????druid
        ????????????${druid.version}
        ????????


        ????????
        ????????????org.mybatis.spring.boot
        ????????????mybatis-spring-boot-starter
        ????????????${mybatis.version}
        ????????


        ????????
        ????????????com.baomidou
        ????????????mybatis-plus
        ????????????${mybatis-plus.version}
        ????????


        ????????
        ????????????org.apache.dubbo
        ????????????dubbo
        ????????????${dubbo.version}
        ????????????
        ????????????????
        ????????????????????spring
        ????????????????????org.springframework
        ????????????????

        ????????????

        ????????

        ????????
        ????????????org.apache.dubbo
        ????????????dubbo-spring-boot-starter
        ????????????${dubbo.version}
        ????????


        ????????
        ????????
        ????????????org.apache.dubbo
        ????????????dubbo-config-spring
        ????????????${dubbo.version}
        ????????

        ????????
        ????????????org.apache.dubbo
        ????????????dubbo-registry-nacos
        ????????????${dubbo.version}
        ????????


        ????????
        ????????
        ????????????io.seata
        ????????????seata-all
        ????????????${seata.version}
        ????????



        ????????
        ????????????com.alibaba.nacos
        ????????????nacos-client
        ????????????${nacos-client.verison}
        ????????


        ????????
        ????????
        ????????????com.alibaba.boot
        ????????????nacos-config-spring-boot-starter
        ????????????${nacos.version}
        ????????????
        ????????????????
        ????????????????????nacos-client
        ????????????????????com.alibaba.nacos
        ????????????????

        ????????????

        ????????

        ????????
        ????????????org.springframework.boot
        ????????????spring-boot-maven-plugin
        ????????????${springboot.verison}
        ????????

        ????????
        ????????????org.projectlombok
        ????????????lombok
        ????????????${lombok.version}
        ????????



        ????????
        ????????????io.netty
        ????????????netty-all
        ????????????${netty.version}
        ????????

        ????????
        ????????????com.alibaba.spring
        ????????????spring-context-support
        ????????????1.0.2
        ????????

        ????????
        ????????????org.apache.httpcomponents
        ????????????httpclient
        ????????????4.5
        ????????


        ????????
        ????????????mysql
        ????????????mysql-connector-java
        ????????????5.1.47
        ????????

        ????



        ????
        ????????
        ????????????
        ????????????????org.springframework.boot
        ????????????????spring-boot-maven-plugin
        ????????????

        ????????????
        ????????????????org.apache.maven.plugins
        ????????????????maven-deploy-plugin
        ????????????????
        ????????????????????true
        ????????????????

        ????????????

        ????????????
        ????????????????org.apache.maven.plugins
        ????????????????maven-compiler-plugin
        ????????????????
        ????????????????????${java.version}
        ????????????????????${java.version}
        ????????????????

        ????????????

        ????????

        ????



        123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184

        注意:

        • seata-all: 這個是seata 所需的主要依賴
        • dubbo-spring-boot-starter: springboot dubbo的依賴

        其他的就不一一介紹,其他的一目了然,就知道是干什么的。

        3.8.2 application.properties配置

        server.port=8102
        spring.application.name=dubbo-account-example

        #====================================Dubbo配置===============================================
        dubbo.application.id= dubbo-account-example
        dubbo.application.name= dubbo-account-example
        dubbo.protocol.id=dubbo
        dubbo.protocol.name=dubbo
        dubbo.registry.id=dubbo-account-example-registry
        dubbo.registry.address=nacos://192.168.10.200:8848
        dubbo.protocol.port=20880
        dubbo.application.qosEnable=false
        dubbo.config-center.address=nacos://192.168.10.200:8848
        dubbo.metadata-report.address=nacos://192.168.10.200:8848

        #====================================mysql 配置============================================
        spring.datasource.driver-class-name=com.mysql.jdbc.Driver
        spring.datasource.url=jdbc:mysql://192.168.10.200:3306/seata?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
        spring.datasource.username=lidong
        spring.datasource.password=cwj887766@@


        #=====================================mybatis 配置======================================
        mybatis.mapper-locations=classpath*:/mapper/*.xml

        12345678910111213141516171819202122232425

        3.8.3 registry.conf 配置 (naocs的配置)

        registry {
        # file nacos
        type = "nacos"

        nacos {
        serverAddr = "192.168.10.200"
        namespace = "public"
        cluster = "default"
        }
        file {
        name = "file.conf"
        }
        }

        config {
        # file、nacos 、apollo、zk、consul
        type = "nacos"

        file {
        name = "file.conf"
        }

        nacos {
        serverAddr = "192.168.10.200"
        namespace = "public"
        cluster = "default"
        }
        }

        1234567891011121314151617181920212223242526272829

        3.8.5 SeataAutoConfig 配置

        package?io.seata.samples.integration.account.config;

        import?com.alibaba.druid.pool.DruidDataSource;

        import?io.seata.rm.datasource.DataSourceProxy;
        import?io.seata.spring.annotation.GlobalTransactionScanner;
        import?org.apache.ibatis.session.SqlSessionFactory;
        import?org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
        import?org.mybatis.spring.SqlSessionFactoryBean;
        import?org.springframework.beans.factory.annotation.Autowired;
        import?org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
        import?org.springframework.context.annotation.Bean;
        import?org.springframework.context.annotation.Configuration;
        import?org.springframework.context.annotation.Primary;
        import?org.springframework.core.io.support.PathMatchingResourcePatternResolver;

        /**
        ?*?@Author:?llidong
        ?*?@Description??seata?global?configuration
        ?*?@Date?Created?in?2019/9/05?10:28
        ?*/

        @Configuration
        public?class?SeataAutoConfig?{

        ????/**
        ?????*?autowired?datasource?config
        ?????*/

        ????@Autowired
        ????private?DataSourceProperties?dataSourceProperties;

        ????/**
        ?????*?init?durid?datasource
        ?????*
        ?????*?@Return:?druidDataSource??datasource?instance
        ?????*/

        ????@Bean
        ????@Primary
        ????public?DruidDataSource?druidDataSource(){
        ????????DruidDataSource?druidDataSource?=?new?DruidDataSource();
        ????????druidDataSource.setUrl(dataSourceProperties.getUrl());
        ????????druidDataSource.setUsername(dataSourceProperties.getUsername());
        ????????druidDataSource.setPassword(dataSourceProperties.getPassword());
        ????????druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        ????????druidDataSource.setInitialSize(0);
        ????????druidDataSource.setMaxActive(180);
        ????????druidDataSource.setMaxWait(60000);
        ????????druidDataSource.setMinIdle(0);
        ????????druidDataSource.setValidationQuery("Select?1?from?DUAL");
        ????????druidDataSource.setTestOnBorrow(false);
        ????????druidDataSource.setTestOnReturn(false);
        ????????druidDataSource.setTestWhileIdle(true);
        ????????druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
        ????????druidDataSource.setMinEvictableIdleTimeMillis(25200000);
        ????????druidDataSource.setRemoveAbandoned(true);
        ????????druidDataSource.setRemoveAbandonedTimeout(1800);
        ????????druidDataSource.setLogAbandoned(true);
        ????????return?druidDataSource;
        ????}

        ????/**
        ?????*?init?datasource?proxy
        ?????*?@Param:?druidDataSource??datasource?bean?instance
        ?????*?@Return:?DataSourceProxy??datasource?proxy
        ?????*/

        ????@Bean
        ????public?DataSourceProxy?dataSourceProxy(DruidDataSource?druidDataSource){
        ????????return?new?DataSourceProxy(druidDataSource);
        ????}

        ????/**
        ?????*?init?mybatis?sqlSessionFactory
        ?????*?@Param:?dataSourceProxy??datasource?proxy
        ?????*?@Return:?DataSourceProxy??datasource?proxy
        ?????*/

        ????@Bean
        ????public?SqlSessionFactory?sqlSessionFactory(DataSourceProxy?dataSourceProxy)?throws?Exception?{
        ????????SqlSessionFactoryBean?factoryBean?=?new?SqlSessionFactoryBean();
        ????????factoryBean.setDataSource(dataSourceProxy);
        ????????factoryBean.setMapperLocations(new?PathMatchingResourcePatternResolver()
        ????????????????.getResources("classpath*:/mapper/*.xml"));
        ????????return?factoryBean.getObject();
        ????}

        ????/**
        ?????*?init?global?transaction?scanner
        ?????*
        ?????*?@Return:?GlobalTransactionScanner
        ?????*/

        ????@Bean
        ????public?GlobalTransactionScanner?globalTransactionScanner(){
        ????????return?new?GlobalTransactionScanner("account-gts-seata-example",?"account-service-seata-service-group");
        ????}
        }

        12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394

        其中:

        @Bean
        ????public?GlobalTransactionScanner?globalTransactionScanner(){
        ????????return?new?GlobalTransactionScanner("account-gts-seata-example",?"account-service-seata-service-group");
        ????}
        1234

        GlobalTransactionScanner: 初始化全局的事務掃描器

        ?/**
        ?????*?Instantiates?a?new?Global?transaction?scanner.
        ?????*
        ?????*?@param?applicationId??the?application?id
        ?????*?@param?txServiceGroup?the?default?server?group
        ?????*/
        ????public?GlobalTransactionScanner(String?applicationId,?String?txServiceGroup)?{
        ????????this(applicationId,?txServiceGroup,?DEFAULT_MODE);
        ????}
        123456789
        • applicationId :為應用id 這里我傳入的是account-gts-seata-example
        • txServiceGroup: 默認server的分組 這里我傳入的是account-service-seata-service-group 這個和我們前面在nacos 配置的是保存一致。
        • DEFAULT_MODE:默認的事務模式 為AT_MODE + MT_MODE

        3.8.6 AccountExampleApplication 啟動類的配置

        package?io.seata.samples.integration.account;

        import?org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
        import?org.mybatis.spring.annotation.MapperScan;
        import?org.springframework.boot.SpringApplication;
        import?org.springframework.boot.autoconfigure.SpringBootApplication;
        import?org.springframework.boot.context.config.ConfigFileApplicationListener;

        @SpringBootApplication(scanBasePackages?=?"io.seata.samples.integration.account")
        @MapperScan({"io.seata.samples.integration.account.mapper"})
        @EnableDubbo(scanBasePackages?=?"io.seata.samples.integration.account")
        public?class?AccountExampleApplication?{

        ????public?static?void?main(String[]?args)?{
        ????????SpringApplication.run(AccountExampleApplication.class,?args);
        ????}

        }


        1234567891011121314151617181920
        • @EnableDubbo等同于 @DubboComponentScan@EnableDubboConfig組合
        • @DubboComponentScan 掃描 classpaths 下類中添加了 @Service@Reference 將自動注入到spring beans中。
        • @EnableDubboConfig 掃描的dubbo的外部化配置。

        4 啟動所有的sample模塊

        啟動 samples-account、samples-order、samples-storage、samples-business

        并且在nocos的控制臺查看注冊情況: http://192.168.10.200:8848/nacos/#/serviceManagement

        我們可以看到上面的服務都已經(jīng)注冊成功。

        5 測試

        5. 1 發(fā)送一個下單請求

        使用postman 發(fā)送 :http://localhost:8104/business/dubbo/buy

        參數(shù):

        {
        ????"userId":"1",
        ????"commodityCode":"C201901140001",
        ????"name":"fan",
        ????"count":50,
        ????"amount":"100"
        }
        1234567

        返回

        {
        ????"status":?200,
        ????"message":?"成功",
        ????"data":?null
        }
        12345

        這時候控制臺:

        2019-09-05 12:17:34.097  INFO 21860?---?[nio-8104-exec-4] i.s.s.i.c.controller.BusinessController ?:?請求參數(shù):BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
        2019-09-05?12:17:34.146??INFO?21860?---?[nio-8104-exec-4]?i.seata.tm.api.DefaultGlobalTransaction??:?Begin?new?global?transaction?[192.168.10.200:8091:2021380638]
        2019-09-05?12:17:34.150??INFO?21860?---?[nio-8104-exec-4]?i.s.s.i.c.service.BusinessServiceImpl????:?開始全局事務,XID?=?192.168.10.200:8091:2021380638
        2019-09-05?12:17:36.966??INFO?21860?---?[nio-8104-exec-4]?i.seata.tm.api.DefaultGlobalTransaction??:?[192.168.10.200:8091:2021380638]?commit?status:Committed
        1234

        事務提交成功,

        我們來看一下數(shù)據(jù)庫數(shù)據(jù)變化

        t_accountt_order

        t_storage

        數(shù)據(jù)沒有問題。

        5.2 測試回滾

        我們samples-businessBusinessServiceImplhandleBusiness2 下面的代碼去掉注釋

        if?(!flag)?{
        ??throw?new?RuntimeException("測試拋異常后,分布式事務回滾!");
        }
        123

        使用postman 發(fā)送 :http://localhost:8104/business/dubbo/buy2

        .響應結果:

        {
        ????"timestamp":?"2019-09-05T04:29:34.178+0000",
        ????"status":?500,
        ????"error":?"Internal?Server?Error",
        ????"message":?"測試拋異常后,分布式事務回滾!",
        ????"path":?"/business/dubbo/buy"
        }
        1234567

        控制臺

        2019-09-05 12:29:32.836  INFO 17264 ---?[nio-8104-exec-2] i.s.s.i.c.controller.BusinessController ?:?請求參數(shù):BusinessDTO(userId=1, commodityCode=C201901140001, name=fan, count=50, amount=100)
        2019-09-05?12:29:32.843??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?ContextCore[null]?extension?by?class[io.seata.core.context.ThreadLocalContextCore]
        2019-09-05?12:29:32.848??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?TransactionManager[null]?extension?by?class[io.seata.tm.DefaultTransactionManager]
        2019-09-05?12:29:32.849??INFO?17264?---?[nio-8104-exec-2]?io.seata.tm.TransactionManagerHolder?????:?TransactionManager?Singleton?io.seata.tm.DefaultTransactionManager@461585ac
        2019-09-05?12:29:32.859??INFO?17264?---?[nio-8104-exec-2]?i.s.common.loader.EnhancedServiceLoader??:?load?LoadBalance[null]?extension?by?class[io.seata.discovery.loadbalance.RandomLoadBalance]
        2019-09-05?12:29:32.893??INFO?17264?---?[nio-8104-exec-2]?i.seata.tm.api.DefaultGlobalTransaction??:?Begin?new?global?transaction?[192.168.10.200:8091:2021380674]
        2019-09-05?12:29:32.897??INFO?17264?---?[nio-8104-exec-2]?i.s.s.i.c.service.BusinessServiceImpl????:?開始全局事務,XID?=?192.168.10.200:8091:2021380674
        2019-09-05?12:29:34.143??INFO?17264?---?[nio-8104-exec-2]?i.seata.tm.api.DefaultGlobalTransaction??:?[192.168.10.200:8091:2021380674]?rollback?status:Rollbacked
        2019-09-05?12:29:34.158?ERROR?17264?---?[nio-8104-exec-2]?o.a.c.c.C.[.[.[/].[dispatcherServlet]????:?Servlet.service()?for?servlet?[dispatcherServlet]?in?context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException:?測試拋異常后,分布式事務回滾!] with root cause

        java.lang.RuntimeException:?測試拋異常后,分布式事務回滾!
        ?at?io.seata.samples.integration.call.service.BusinessServiceImpl.handleBusiness(BusinessServiceImpl.java:60)?~[classes/:na]
        ?at?io.seata.samples.integration.call.service.BusinessServiceImpl$$FastClassBySpringCGLIB$$2ab3d645.invoke()?~[classes/:na]
        ?at?org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)?~[spring-core-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?io.seata.spring.annotation.GlobalTransactionalInterceptor$1.execute(GlobalTransactionalInterceptor.java:104)?~[seata-all-0.8.0.jar:0.8.0]
        ?at?io.seata.tm.api.TransactionalTemplate.execute(TransactionalTemplate.java:64)?~[seata-all-0.8.0.jar:0.8.0]
        ?at?io.seata.spring.annotation.GlobalTransactionalInterceptor.handleGlobalTransaction(GlobalTransactionalInterceptor.java:101)?~[seata-all-0.8.0.jar:0.8.0]
        ?at?io.seata.spring.annotation.GlobalTransactionalInterceptor.invoke(GlobalTransactionalInterceptor.java:76)?~[seata-all-0.8.0.jar:0.8.0]
        ?at?org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)?~[spring-aop-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?io.seata.samples.integration.call.service.BusinessServiceImpl$$EnhancerBySpringCGLIB$$cb43f7ab.handleBusiness()?~[classes/:na]
        ?at?io.seata.samples.integration.call.controller.BusinessController.handleBusiness(BusinessController.java:37)?~[classes/:na]
        ?at?sun.reflect.NativeMethodAccessorImpl.invoke0(Native?Method)?~[na:1.8.0_144]
        ?at?sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)?~[na:1.8.0_144]
        ?at?sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)?~[na:1.8.0_144]
        ?at?java.lang.reflect.Method.invoke(Method.java:498)?~[na:1.8.0_144]
        ?at?org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?javax.servlet.http.HttpServlet.service(HttpServlet.java:660)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)?~[spring-webmvc-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?javax.servlet.http.HttpServlet.service(HttpServlet.java:741)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)?~[tomcat-embed-websocket-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)?~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
        ?at?org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)?~[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)?[na:1.8.0_144]
        ?at?java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)?[na:1.8.0_144]
        ?at?org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)?[tomcat-embed-core-9.0.19.jar:9.0.19]
        ?at?java.lang.Thread.run(Thread.java:748)?[na:1.8.0_144]


        1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980

        我們查看數(shù)據(jù)庫數(shù)據(jù),已經(jīng)回滾,和上面的數(shù)據(jù)一致。


        點擊閱讀全文前往微服務電商教程
        瀏覽 68
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            美女扒开粉嫩的尿囗给男生桶软件 | 久久精品屋 | 美女网站免费看 | 国产亚洲色婷婷久久99 | 男生下面伸进女人下面的视频 | 乱肉艳妇2在线观看潘金莲电影 | 青青草原成人网站 | 蜜桃乱伦视频 | av作品番号库大全 | 操逼图片视频 |