diff --git a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSpringTransaction.java b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSpringTransaction.java index f15d2e68..c29d74b6 100644 --- a/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSpringTransaction.java +++ b/mybatis-flex-spring/src/main/java/com/mybatisflex/spring/FlexSpringTransaction.java @@ -88,6 +88,6 @@ public class FlexSpringTransaction implements Transaction { @Override public Integer getTimeout() throws SQLException { - return getConnection().getNetworkTimeout(); + return null; } } diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/config/RestTemplateConfig.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/config/RestTemplateConfig.java new file mode 100644 index 00000000..c5605f4c --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/config/RestTemplateConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.acount.accountservice.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + return restTemplate; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/controller/AccountController.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/controller/AccountController.java new file mode 100644 index 00000000..4b7f2250 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/controller/AccountController.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.acount.accountservice.controller; + +import io.seata.core.context.RootContext; +import java.math.BigDecimal; +import mybatisflex.test.acount.accountservice.service.AccountService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class AccountController { + + @Autowired + AccountService accountService; + + @GetMapping + public void debit(@RequestParam String userId, @RequestParam BigDecimal orderMoney) { + System.out.println("account XID " + RootContext.getXID()); + accountService.debit(userId, orderMoney); + } + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/Account.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/Account.java new file mode 100644 index 00000000..9f36d497 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/Account.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.acount.accountservice.persistence; + +import java.math.BigDecimal; + +public class Account { + private Integer id; + + private String userId; + + private BigDecimal money; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public BigDecimal getMoney() { + return money; + } + + public void setMoney(BigDecimal money) { + this.money = money; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/AccountMapper.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/AccountMapper.java new file mode 100644 index 00000000..282c10f5 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/persistence/AccountMapper.java @@ -0,0 +1,28 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.acount.accountservice.persistence; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface AccountMapper { + + Account selectByUserId(@Param("userId") String userId); + + int updateById(Account record); + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/service/AccountService.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/service/AccountService.java new file mode 100644 index 00000000..14fbfa26 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/java/mybatisflex/test/acount/accountservice/service/AccountService.java @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.acount.accountservice.service; + +import java.math.BigDecimal; +import mybatisflex.test.acount.accountservice.persistence.Account; +import mybatisflex.test.acount.accountservice.persistence.AccountMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AccountService { + + private static final String ERROR_USER_ID = "1002"; + + @Autowired + private AccountMapper accountMapper; + + public void debit(String userId, BigDecimal num) { + Account account = accountMapper.selectByUserId(userId); + account.setMoney(account.getMoney().subtract(num)); + accountMapper.updateById(account); + + if (ERROR_USER_ID.equals(userId)) { + throw new RuntimeException("account branch exception"); + } + } + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/application.yml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/application.yml new file mode 100755 index 00000000..62c571bf --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/application.yml @@ -0,0 +1,129 @@ +server: + port: 8083 +mybatis-flex: + seata-config: + enable: true #启动seata + seata-mode: XA #xa或者ta + datasource: + accountdb: + url: jdbc:mysql://127.0.0.1:3306/db_account + username: root + password: 131496 +spring: + main: + allow-circular-references: true +#spring: +# datasource: +# type: com.alibaba.druid.pool.DruidDataSource +# driver-class-name: com.mysql.jdbc.Driver +# url: jdbc:mysql://127.0.0.1:3306/db_account?useSSL=false&serverTimezone=UTC +# username: root +# password: 131496 +seata: + enabled: true + application-id: account-service + tx-service-group: my_test_tx_group + enable-auto-data-source-proxy: false + #use-jdk-proxy: false + client: + rm: + async-commit-buffer-limit: 1000 + report-retry-count: 5 + table-meta-check-enable: false + report-success-enable: false + lock: + retry-interval: 10 + retry-times: 30 + retry-policy-branch-rollback-on-conflict: true + tm: + commit-retry-count: 5 + rollback-retry-count: 5 + undo: + data-validation: true + log-serialization: jackson + log-table: undo_log + log: + exceptionRate: 100 + service: + vgroup-mapping: + my_test_tx_group: default + grouplist: + default: 127.0.0.1:8091 + #enable-degrade: false + #disable-global-transaction: false + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + worker-thread-size: default + boss-thread-size: 1 + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true + config: + type: file + consul: + server-addr: 127.0.0.1:8500 + apollo: + apollo-meta: http://192.168.1.204:8801 + app-id: seata-server + namespace: application + etcd3: + server-addr: http://localhost:2379 + nacos: + namespace: + serverAddr: localhost + group: SEATA_GROUP + zk: + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" + registry: + type: file + consul: + cluster: default + server-addr: 127.0.0.1:8500 + etcd3: + cluster: default + serverAddr: http://localhost:2379 + eureka: + application: default + weight: 1 + service-url: http://localhost:8761/eureka + nacos: + cluster: default + server-addr: localhost + namespace: + redis: + server-addr: localhost:6379 + db: 0 + password: + cluster: default + timeout: 0 + sofa: + server-addr: 127.0.0.1:9603 + application: default + region: DEFAULT_ZONE + datacenter: DefaultDataCenter + cluster: default + group: SEATA_GROUP + addressWaitTime: 3000 + zk: + cluster: default + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/mapper/AccountMapper.xml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/mapper/AccountMapper.xml new file mode 100644 index 00000000..b5f265b5 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/account-service/src/main/resources/mapper/AccountMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + update account_tbl + set money = #{money,jdbcType=DECIMAL} + where id = #{id} + + + diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/OrderClient.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/OrderClient.java new file mode 100644 index 00000000..d02e0c55 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/OrderClient.java @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.businessservice.client; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class OrderClient { + + @Autowired + private RestTemplate restTemplate; + + public void create(String userId, String commodityCode, int orderCount) { + String url = "http://127.0.0.1:8082/api/order/debit?userId=" + userId + "&commodityCode=" + commodityCode + + "&count=" + orderCount; + try { + restTemplate.getForEntity(url, Void.class); + } catch (Exception e) { + throw new RuntimeException(String.format("create url %s ,error:", url)); + } + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/StockClient.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/StockClient.java new file mode 100644 index 00000000..00897bec --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/client/StockClient.java @@ -0,0 +1,38 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.businessservice.client; + +import io.seata.core.context.RootContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class StockClient { + + @Autowired + private RestTemplate restTemplate; + + public void deduct(String commodityCode, int orderCount) { + System.out.println("business to stock " + RootContext.getXID()); + String url = "http://127.0.0.1:8081/api/stock/deduct?commodityCode=" + commodityCode + "&count=" + orderCount; + try { + restTemplate.getForEntity(url, Void.class); + } catch (Exception e) { + throw new RuntimeException(String.format("deduct url %s ,error:",url),e); + } + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/config/RestTemplateConfig.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/config/RestTemplateConfig.java new file mode 100644 index 00000000..ef16cb29 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/config/RestTemplateConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.businessservice.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + return restTemplate; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/controller/BusinessController.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/controller/BusinessController.java new file mode 100644 index 00000000..5c66fb0b --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/controller/BusinessController.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.businessservice.controller; + +import javax.servlet.http.HttpServletRequest; +import mybatisflex.test.businessservice.service.BusinessService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/business") +@RestController +public class BusinessController { + + @Autowired + private BusinessService businessService; + + /** + * 购买下单,模拟全局事务提交 + * + * @return + */ + @RequestMapping("/purchase/commit") + public Boolean purchaseCommit(HttpServletRequest request) { + businessService.purchase("1001", "2001", 1); + return true; + } + + /** + * 购买下单,模拟全局事务回滚 + * + * @return + */ + @RequestMapping("/purchase/rollback") + public Boolean purchaseRollback() { + try { + businessService.purchase("1002", "2001", 1); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/service/BusinessService.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/service/BusinessService.java new file mode 100644 index 00000000..6a74f0e8 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/java/mybatisflex/test/businessservice/service/BusinessService.java @@ -0,0 +1,50 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.businessservice.service; + +import io.seata.core.context.RootContext; +import io.seata.spring.annotation.GlobalTransactional; +import mybatisflex.test.businessservice.client.OrderClient; +import mybatisflex.test.businessservice.client.StockClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class BusinessService { + + private static final Logger LOGGER = LoggerFactory.getLogger(BusinessService.class); + + @Autowired + private StockClient stockClient; + @Autowired + private OrderClient orderClient; + + /** + * 减库存,下订单 + * + * @param userId + * @param commodityCode + * @param orderCount + */ + @GlobalTransactional + public void purchase(String userId, String commodityCode, int orderCount) { + LOGGER.info("purchase begin ... xid: " + RootContext.getXID()); + stockClient.deduct(commodityCode, orderCount); + orderClient.create(userId, commodityCode, orderCount); + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/resources/application.yml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/resources/application.yml new file mode 100755 index 00000000..173f96c5 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/business-service/src/main/resources/application.yml @@ -0,0 +1,113 @@ +server: + port: 8084 +spring: + main: + allow-circular-references: true +seata: + enabled: true + application-id: business-service + tx-service-group: my_test_tx_group + enable-auto-data-source-proxy: false + #use-jdk-proxy: false + client: + rm: + async-commit-buffer-limit: 1000 + report-retry-count: 5 + table-meta-check-enable: false + report-success-enable: false + lock: + retry-interval: 10 + retry-times: 30 + retry-policy-branch-rollback-on-conflict: true + tm: + commit-retry-count: 5 + rollback-retry-count: 5 + undo: + data-validation: true + log-serialization: jackson + log-table: undo_log + log: + exceptionRate: 100 + service: + vgroup-mapping: + my_test_tx_group: default + grouplist: + default: 127.0.0.1:8091 + #enable-degrade: false + #disable-global-transaction: false + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + worker-thread-size: default + boss-thread-size: 1 + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true + config: + type: file + consul: + server-addr: 127.0.0.1:8500 + apollo: + apollo-meta: http://192.168.1.204:8801 + app-id: seata-server + namespace: application + etcd3: + server-addr: http://localhost:2379 + nacos: + namespace: + serverAddr: localhost + group: SEATA_GROUP + zk: + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" + registry: + type: file + consul: + cluster: default + server-addr: 127.0.0.1:8500 + etcd3: + cluster: default + serverAddr: http://localhost:2379 + eureka: + application: default + weight: 1 + service-url: http://localhost:8761/eureka + nacos: + cluster: default + server-addr: localhost + namespace: + redis: + server-addr: localhost:6379 + db: 0 + password: + cluster: default + timeout: 0 + sofa: + server-addr: 127.0.0.1:9603 + application: default + region: DEFAULT_ZONE + datacenter: DefaultDataCenter + cluster: default + group: SEATA_GROUP + addressWaitTime: 3000 + zk: + cluster: default + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/config/SeataRestTemplateAutoConfiguration.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/config/SeataRestTemplateAutoConfiguration.java new file mode 100644 index 00000000..5749ff4c --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/config/SeataRestTemplateAutoConfiguration.java @@ -0,0 +1,59 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.commonservice.config; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import javax.annotation.PostConstruct; +import mybatisflex.test.commonservice.interceptor.SeataRestTemplateInterceptor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class SeataRestTemplateAutoConfiguration { + @Autowired(required = false) + private Collection restTemplates; + @Autowired + private SeataRestTemplateInterceptor seataRestTemplateInterceptor; + + public SeataRestTemplateAutoConfiguration() { + } + + @Bean + public SeataRestTemplateInterceptor seataRestTemplateInterceptor() { + return new SeataRestTemplateInterceptor(); + } + + @PostConstruct + public void init() { + if (this.restTemplates != null) { + Iterator var1 = this.restTemplates.iterator(); + + while (var1.hasNext()) { + RestTemplate restTemplate = (RestTemplate)var1.next(); + List interceptors = new ArrayList(restTemplate.getInterceptors()); + interceptors.add(this.seataRestTemplateInterceptor); + restTemplate.setInterceptors(interceptors); + } + } + + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/filter/SeataFilter.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/filter/SeataFilter.java new file mode 100644 index 00000000..598d7161 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/filter/SeataFilter.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.commonservice.filter; + +import io.seata.core.context.RootContext; +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang.StringUtils; +import org.springframework.stereotype.Component; + +@Component +public class SeataFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest)servletRequest; + String xid = req.getHeader(RootContext.KEY_XID.toLowerCase()); + boolean isBind = false; + if (StringUtils.isNotBlank(xid)) { + RootContext.bind(xid); + isBind = true; + } + try { + filterChain.doFilter(servletRequest, servletResponse); + } finally { + if (isBind) { + RootContext.unbind(); + } + } + } + + @Override + public void destroy() { + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/interceptor/SeataRestTemplateInterceptor.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/interceptor/SeataRestTemplateInterceptor.java new file mode 100644 index 00000000..6b5062d4 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/common-service/src/main/java/mybatisflex/test/commonservice/interceptor/SeataRestTemplateInterceptor.java @@ -0,0 +1,44 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.commonservice.interceptor; + +import io.seata.core.context.RootContext; +import java.io.IOException; +import org.apache.commons.lang.StringUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.support.HttpRequestWrapper; +import org.springframework.stereotype.Component; + + +public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor { + public SeataRestTemplateInterceptor() { + } + + public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, + ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { + HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest); + String xid = RootContext.getXID(); + if (StringUtils.isNotEmpty(xid)) { + requestWrapper.getHeaders().add(RootContext.KEY_XID, xid); + } + + return clientHttpRequestExecution.execute(requestWrapper, bytes); + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/client/AccountClient.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/client/AccountClient.java new file mode 100644 index 00000000..e3d5167b --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/client/AccountClient.java @@ -0,0 +1,37 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.client; + +import java.math.BigDecimal; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class AccountClient { + + @Autowired + private RestTemplate restTemplate; + + public void debit(String userId, BigDecimal orderMoney) { + String url = "http://127.0.0.1:8083?userId=" + userId + "&orderMoney=" + orderMoney; + try { + restTemplate.getForEntity(url, Void.class); + } catch (Exception e) { + throw new RuntimeException(String.format("debit url %s ,error:",url),e); + } + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/config/RestTemplateConfig.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/config/RestTemplateConfig.java new file mode 100644 index 00000000..62e68996 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/config/RestTemplateConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + return restTemplate; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/controller/OrderController.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/controller/OrderController.java new file mode 100644 index 00000000..948e3659 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/controller/OrderController.java @@ -0,0 +1,39 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.controller; + +import io.seata.core.context.RootContext; +import mybatisflex.test.orderservice.service.OrderService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/order") +@RestController +public class OrderController { + + @Autowired + OrderService orderService; + + @GetMapping(value = "/debit") + public void debit(@RequestParam String userId, @RequestParam String commodityCode, @RequestParam Integer count) { + System.out.println("order XID " + RootContext.getXID()); + orderService.create(userId, commodityCode, count); + } + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/Order.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/Order.java new file mode 100644 index 00000000..4e800413 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/Order.java @@ -0,0 +1,70 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.persistence; + +import java.math.BigDecimal; + +public class Order { + private Integer id; + + private String userId; + + private String commodityCode; + + private Integer count; + + private BigDecimal money; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getCommodityCode() { + return commodityCode; + } + + public void setCommodityCode(String commodityCode) { + this.commodityCode = commodityCode; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } + + public BigDecimal getMoney() { + return money; + } + + public void setMoney(BigDecimal money) { + this.money = money; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/OrderMapper.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/OrderMapper.java new file mode 100644 index 00000000..e6259c41 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/persistence/OrderMapper.java @@ -0,0 +1,25 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.persistence; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OrderMapper { + + int insert(Order record); + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/service/OrderService.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/service/OrderService.java new file mode 100644 index 00000000..5f774b8b --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/java/mybatisflex/test/orderservice/service/OrderService.java @@ -0,0 +1,47 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.orderservice.service; + + +import java.math.BigDecimal; +import mybatisflex.test.orderservice.client.AccountClient; +import mybatisflex.test.orderservice.persistence.Order; +import mybatisflex.test.orderservice.persistence.OrderMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class OrderService { + + @Autowired + private AccountClient accountClient; + @Autowired + private OrderMapper orderMapper; + + public void create(String userId, String commodityCode, Integer count) { + BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5)); + Order order = new Order(); + order.setUserId(userId); + order.setCommodityCode(commodityCode); + order.setCount(count); + order.setMoney(orderMoney); + + orderMapper.insert(order); + + accountClient.debit(userId, orderMoney); + + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/application.yml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/application.yml new file mode 100755 index 00000000..3cac9a3c --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/application.yml @@ -0,0 +1,129 @@ +server: + port: 8082 +mybatis-flex: + seata-config: + enable: true #启动seata + seata-mode: XA #xa或者ta + datasource: + orderdb: + url: jdbc:mysql://127.0.0.1:3306/db_order + username: root + password: 131496 +spring: + main: + allow-circular-references: true +#spring: +# datasource: +# type: com.alibaba.druid.pool.DruidDataSource +# driver-class-name: com.mysql.jdbc.Driver +# url: jdbc:mysql://127.0.0.1:3306/db_order?useSSL=false&serverTimezone=UTC +# username: root +# password: 131496 +seata: + enabled: true + application-id: order-service + tx-service-group: my_test_tx_group + enable-auto-data-source-proxy: false + #use-jdk-proxy: false + client: + rm: + async-commit-buffer-limit: 1000 + report-retry-count: 5 + table-meta-check-enable: false + report-success-enable: false + lock: + retry-interval: 10 + retry-times: 30 + retry-policy-branch-rollback-on-conflict: true + tm: + commit-retry-count: 5 + rollback-retry-count: 5 + undo: + data-validation: true + log-serialization: jackson + log-table: undo_log + log: + exceptionRate: 100 + service: + vgroup-mapping: + my_test_tx_group: default + grouplist: + default: 127.0.0.1:8091 + #enable-degrade: false + #disable-global-transaction: false + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + worker-thread-size: default + boss-thread-size: 1 + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true + config: + type: file + consul: + server-addr: 127.0.0.1:8500 + apollo: + apollo-meta: http://192.168.1.204:8801 + app-id: seata-server + namespace: application + etcd3: + server-addr: http://localhost:2379 + nacos: + namespace: + serverAddr: localhost + group: SEATA_GROUP + zk: + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" + registry: + type: file + consul: + cluster: default + server-addr: 127.0.0.1:8500 + etcd3: + cluster: default + serverAddr: http://localhost:2379 + eureka: + application: default + weight: 1 + service-url: http://localhost:8761/eureka + nacos: + cluster: default + server-addr: localhost + namespace: + redis: + server-addr: localhost:6379 + db: 0 + password: + cluster: default + timeout: 0 + sofa: + server-addr: 127.0.0.1:9603 + application: default + region: DEFAULT_ZONE + datacenter: DefaultDataCenter + cluster: default + group: SEATA_GROUP + addressWaitTime: 3000 + zk: + cluster: default + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/mapper/OrderMapper.xml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/mapper/OrderMapper.xml new file mode 100644 index 00000000..b052cac6 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/order-service/src/main/resources/mapper/OrderMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + insert into order_tbl (user_id, commodity_code, count, money) + values (#{userId,jdbcType=VARCHAR}, #{commodityCode,jdbcType=VARCHAR}, #{count,jdbcType=INTEGER}, + #{money,jdbcType=DECIMAL}) + + + diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/sql/all_in_one.sql b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/sql/all_in_one.sql new file mode 100644 index 00000000..e4a1f88e --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/sql/all_in_one.sql @@ -0,0 +1,97 @@ +# Account +DROP SCHEMA IF EXISTS db_account; +CREATE SCHEMA db_account; +USE db_account; + +CREATE TABLE `account_tbl` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `user_id` VARCHAR(255) DEFAULT NULL, + `money` INT(11) DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8; + +INSERT INTO account_tbl (id, user_id, money) +VALUES (1, '1001', 10000); +INSERT INTO account_tbl (id, user_id, money) +VALUES (2, '1002', 10000); + +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; + +# Order +DROP SCHEMA IF EXISTS db_order; +CREATE SCHEMA db_order; +USE db_order; + +CREATE TABLE `order_tbl` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `user_id` VARCHAR(255) DEFAULT NULL, + `commodity_code` VARCHAR(255) DEFAULT NULL, + `count` INT(11) DEFAULT '0', + `money` INT(11) DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8; + +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; + +# stock +DROP SCHEMA IF EXISTS db_stock; +CREATE SCHEMA db_stock; +USE db_stock; + +CREATE TABLE `stock_tbl` +( + `id` INT(11) NOT NULL AUTO_INCREMENT, + `commodity_code` VARCHAR(255) DEFAULT NULL, + `count` INT(11) DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `commodity_code` (`commodity_code`) +) ENGINE = InnoDB + DEFAULT CHARSET = utf8; + + +INSERT INTO stock_tbl (id, commodity_code, count) +VALUES (1, '2001', 1000); + +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, + PRIMARY KEY (`id`), + UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/config/RestTemplateConfig.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/config/RestTemplateConfig.java new file mode 100644 index 00000000..6d204018 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/config/RestTemplateConfig.java @@ -0,0 +1,30 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.stockservice.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + return restTemplate; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/controller/StockController.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/controller/StockController.java new file mode 100644 index 00000000..2d394839 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/controller/StockController.java @@ -0,0 +1,65 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.stockservice.controller; + +import io.seata.core.context.RootContext; +import java.sql.SQLException; +import mybatisflex.test.stockservice.persistence.Stock; +import mybatisflex.test.stockservice.service.StockService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/stock") +@RestController +public class StockController { + + @Autowired + StockService stockService; + + @GetMapping(value = "/deduct") + public void deduct(@RequestParam String commodityCode, @RequestParam Integer count) throws SQLException { + System.out.println("stock XID " + RootContext.getXID()); + stockService.deduct(commodityCode, count); + } + + @GetMapping(value = "/get/{id}") + public Stock getById(@PathVariable("id") Integer id) { + return stockService.get(id); + } + + @GetMapping(value = "/batch/update") + public void batchUpdateCond() { + try { + stockService.batchUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @GetMapping(value = "/batch/delete") + public void batchDeleteCond() { + try { + stockService.batchDelete(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/Stock.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/Stock.java new file mode 100644 index 00000000..68605249 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/Stock.java @@ -0,0 +1,48 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.stockservice.persistence; + +public class Stock { + private Integer id; + + private String commodityCode; + + private Integer count; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCommodityCode() { + return commodityCode; + } + + public void setCommodityCode(String commodityCode) { + this.commodityCode = commodityCode; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/StockMapper.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/StockMapper.java new file mode 100644 index 00000000..f8225297 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/persistence/StockMapper.java @@ -0,0 +1,36 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.stockservice.persistence; + +import java.util.List; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +@Mapper +public interface StockMapper { + + Stock selectById(@Param("id") Integer id); + + Stock findByCommodityCode(@Param("commodityCode") String commodityCode); + + int updateById(Stock record); + + void insert(Stock record); + + void insertBatch(List records); + + int updateBatch(@Param("list") List ids, @Param("commodityCode") String commodityCode); +} diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/service/StockService.java b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/service/StockService.java new file mode 100644 index 00000000..0c603dfc --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/java/mybatisflex/test/stockservice/service/StockService.java @@ -0,0 +1,134 @@ +/* + * Copyright 1999-2021 Seata.io Group. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package mybatisflex.test.stockservice.service; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import javax.sql.DataSource; +import io.seata.spring.annotation.GlobalLock; +import io.seata.spring.annotation.GlobalTransactional; +import mybatisflex.test.stockservice.persistence.Stock; +import mybatisflex.test.stockservice.persistence.StockMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class StockService { + + @Autowired + private StockMapper stockMapper; + @Autowired + private DataSource dataSource; + + public void deduct(String commodityCode, int count) { + //There is a latent isolation problem here. + //I hope that users can solve it and deepen their understanding of seata isolation. + //At the bottom I will put a reference solution. + Stock stock = stockMapper.findByCommodityCode(commodityCode); + stock.setCount(stock.getCount() - count); + stockMapper.updateById(stock); + } + + @GlobalLock + public Stock get(Integer id) { + return stockMapper.selectById(id); + } + + /** + * 0.8.0 release + * + * @throws SQLException + */ + @GlobalTransactional + public void batchUpdate() throws SQLException { + Connection connection = null; + PreparedStatement preparedStatement = null; + try { + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + String sql = "update stock_tbl set count = ?" + " where id = ? and commodity_code = ?"; + preparedStatement = connection.prepareStatement(sql); + preparedStatement.setInt(1, 100); + preparedStatement.setLong(2, 1); + preparedStatement.setString(3, "2001"); + preparedStatement.addBatch(); + preparedStatement.setInt(1, 200); + preparedStatement.setLong(2, 2); + preparedStatement.setString(3, "2002"); + preparedStatement.addBatch(); + preparedStatement.setInt(1, 300); + preparedStatement.setLong(2, 3); + preparedStatement.setString(3, "2003"); + preparedStatement.addBatch(); + preparedStatement.executeBatch(); + connection.commit(); + System.out.println(1 / 0); + } catch (Exception e) { + throw e; + } finally { + connection.close(); + preparedStatement.close(); + } + } + + /** + * 0.8.0 release + * + * @throws SQLException + */ + @GlobalTransactional + public void batchDelete() throws SQLException { + Connection connection = null; + PreparedStatement preparedStatement = null; + try { + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + String sql = "delete from stock_tbl where count = ? and commodity_code = ?"; + preparedStatement = connection.prepareStatement(sql); + preparedStatement.setInt(1, 11); + preparedStatement.setString(2, "2001"); + preparedStatement.addBatch(); + preparedStatement.setInt(1, 22); + preparedStatement.setString(2, "2002"); + preparedStatement.addBatch(); + preparedStatement.setInt(1, 33); + preparedStatement.setString(2, "2003"); + preparedStatement.addBatch(); + preparedStatement.executeBatch(); + connection.commit(); + System.out.println(1 / 0); + } catch (Exception e) { + throw e; + } finally { + connection.close(); + preparedStatement.close(); + } + } +} + +/* +reference solution: + @Transactional + public void deduct(String commodityCode, int count) { + //select + for update + Stock stock = stockMapper.findByCommodityCode(commodityCode); + stock.setCount(stock.getCount() - count); + stockMapper.updateById(stock); + } + 1.select for update,refer https://seata.io/zh-cn/docs/overview/faq.html#4 + 2.(optional)use @Transactional,keep X locks held until connection submission +*/ diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/application.yml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/application.yml new file mode 100755 index 00000000..86474919 --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/application.yml @@ -0,0 +1,129 @@ +server: + port: 8081 +mybatis-flex: + seata-config: + enable: true #启动seata + seata-mode: XA #xa或者ta + datasource: + stockdb: + url: jdbc:mysql://127.0.0.1:3306/db_stock + username: root + password: 131496 +#spring: +# datasource: +# type: com.alibaba.druid.pool.DruidDataSource +# driver-class-name: com.mysql.jdbc.Driver +# url: jdbc:mysql://127.0.0.1:3306/db_stock?useSSL=false&serverTimezone=UTC +# username: root +# password: 131496 +spring: + main: + allow-circular-references: true +seata: + enabled: true + application-id: stock-service + tx-service-group: my_test_tx_group + enable-auto-data-source-proxy: false + #use-jdk-proxy: false + client: + rm: + async-commit-buffer-limit: 1000 + report-retry-count: 5 + table-meta-check-enable: false + report-success-enable: false + lock: + retry-interval: 10 + retry-times: 30 + retry-policy-branch-rollback-on-conflict: true + tm: + commit-retry-count: 5 + rollback-retry-count: 5 + undo: + data-validation: true + log-serialization: jackson + log-table: undo_log + log: + exceptionRate: 100 + service: + vgroup-mapping: + my_test_tx_group: default + grouplist: + default: 127.0.0.1:8091 + #enable-degrade: false + #disable-global-transaction: false + transport: + shutdown: + wait: 3 + thread-factory: + boss-thread-prefix: NettyBoss + worker-thread-prefix: NettyServerNIOWorker + server-executor-thread-prefix: NettyServerBizHandler + share-boss-worker: false + client-selector-thread-prefix: NettyClientSelector + client-selector-thread-size: 1 + client-worker-thread-prefix: NettyClientWorkerThread + worker-thread-size: default + boss-thread-size: 1 + type: TCP + server: NIO + heartbeat: true + serialization: seata + compressor: none + enable-client-batch-send-request: true + config: + type: file + consul: + server-addr: 127.0.0.1:8500 + apollo: + apollo-meta: http://192.168.1.204:8801 + app-id: seata-server + namespace: application + etcd3: + server-addr: http://localhost:2379 + nacos: + namespace: + serverAddr: localhost + group: SEATA_GROUP + zk: + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" + registry: + type: file + consul: + cluster: default + server-addr: 127.0.0.1:8500 + etcd3: + cluster: default + serverAddr: http://localhost:2379 + eureka: + application: default + weight: 1 + service-url: http://localhost:8761/eureka + nacos: + cluster: default + server-addr: localhost + namespace: + redis: + server-addr: localhost:6379 + db: 0 + password: + cluster: default + timeout: 0 + sofa: + server-addr: 127.0.0.1:9603 + application: default + region: DEFAULT_ZONE + datacenter: DefaultDataCenter + cluster: default + group: SEATA_GROUP + addressWaitTime: 3000 + zk: + cluster: default + server-addr: 127.0.0.1:2181 + session-timeout: 6000 + connect-timeout: 2000 + username: "" + password: "" diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/mapper/StorageMapper.xml b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/mapper/StorageMapper.xml new file mode 100644 index 00000000..9fd9327a --- /dev/null +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata-demo/stock-service/src/main/resources/mapper/StorageMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + update stock_tbl + set count = #{count,jdbcType=INTEGER} + WHERE id = #{id} + + + + insert into stock_tbl (commodity_code, count) + values (#{commodityCode,jdbcType=VARCHAR}, #{count,jdbcType=INTEGER}) + + + + insert into stock_tbl (commodity_code, count) + values + + (#{item.commodityCode,jdbcType=VARCHAR}, #{item.count,jdbcType=INTEGER}) + + + + + update stock_tbl set count = 100 + WHERE id IN + + #{item} + + and commodity_code = #{commodityCode,jdbcType=VARCHAR} + + diff --git a/mybatis-flex-test/mybatis-flex-spring-boot-seata/pom.xml b/mybatis-flex-test/mybatis-flex-spring-boot-seata/pom.xml index 6e1055b1..5e9742c0 100644 --- a/mybatis-flex-test/mybatis-flex-spring-boot-seata/pom.xml +++ b/mybatis-flex-test/mybatis-flex-spring-boot-seata/pom.xml @@ -25,7 +25,7 @@ com.mybatis-flex mybatis-flex-codegen - 1.5.6 + ${mybatis-flex.version} diff --git a/mybatis-flex-test/pom.xml b/mybatis-flex-test/pom.xml index de11100a..c2ddea99 100644 --- a/mybatis-flex-test/pom.xml +++ b/mybatis-flex-test/pom.xml @@ -19,6 +19,7 @@ mybatis-flex-spring-boot-test mybatis-flex-spring-cloud-test mybatis-flex-spring-boot-seata + mybatis-flex-spring-boot-seata-demo