Citation dependency
<!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version> </dependency>
Project introduction
- Order items
- Deduct inventory items
There are two existing project simulation orders with feign call inventory deduction project.
Order item database:
/* Navicat MySQL Data Transfer Source Server : Local mysql Source Server Version : 50726 Source Host : localhost:3306 Source Database : seata-order Target Server Type : MYSQL Target Server Version : 50726 File Encoding : 65001 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for order -- ---------------------------- DROP TABLE IF EXISTS `order`; CREATE TABLE `order` ( `id` int(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary key Id', `user_id` int(20) DEFAULT NULL COMMENT 'user Id', `pay_money` decimal(11,0) DEFAULT NULL COMMENT 'Payment amount', `product_id` int(20) DEFAULT NULL COMMENT 'commodity Id', `status` int(11) DEFAULT NULL COMMENT 'state', `count` int(11) DEFAULT NULL COMMENT 'Quantity of commodities', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=291 DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT='Order form'; -- ---------------------------- -- Table structure for 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 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of undo_log -- ----------------------------
Inventory item database:
/* Navicat MySQL Data Transfer Source Server : Local mysql Source Server Version : 50726 Source Host : localhost:3306 Source Database : seata-product Target Server Type : MYSQL Target Server Version : 50726 File Encoding : 65001 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for product -- ---------------------------- DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( `id` int(20) NOT NULL COMMENT 'Primary key', `product_id` int(11) DEFAULT NULL COMMENT 'commodity Id', `price` decimal(11,0) DEFAULT NULL COMMENT 'Price', `count` int(11) DEFAULT NULL COMMENT 'Inventory quantity', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC COMMENT='Warehousing service'; -- ---------------------------- -- Records of product -- ---------------------------- INSERT INTO `product` VALUES ('1', '1', '50', '93'); INSERT INTO `product` VALUES ('2', '2', '30', '99'); -- ---------------------------- -- Table structure for 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 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of undo_log -- ----------------------------
Build inventory deduction items
Citation dependency
Rely on the top of the article
Write notes
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
package com.tuling; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; @EnableFeignClients @EnableDiscoveryClient @SpringBootApplication(exclude = DataSourceAutoConfiguration.class) public class Tulingvip06MsAlibabaStoreApplication { public static void main(String[] args) { SpringApplication.run(Tulingvip06MsAlibabaStoreApplication.class, args); } }
Write configuration add agent data source configuration
package com.tuling.seata.config; import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.context.properties.ConfigurationProperties; 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; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = {"com.tuling.seata.mapper"}) public class MyBatisConfig { /** * Get the attribute structure datasource from the configuration file, pay attention to the prefix. Here is druid. Configure according to your own situation, * The prefix of native datasource is "spring.datasource" * * @return */ @Bean @ConfigurationProperties(prefix = "spring.datasource.hikari") public DataSource hikariDataSource() { return new HikariDataSource(); } /** * Construct datasource proxy object and replace the original datasource * * @param hikariDataSource * @return */ @Primary @Bean("dataSource") public DataSourceProxy dataSourceProxy(DataSource hikariDataSource) { return new DataSourceProxy(hikariDataSource); } @Bean public SqlSessionFactoryBean sqlSessionFactory(DataSourceProxy dataSourceProxy) throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:/mybatis/mapper/**/*.xml")); sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:/mybatis/mybatis-config.xml")); sqlSessionFactoryBean.setTypeAliasesPackage("com.tuling.seata.domin"); sqlSessionFactoryBean.setDataSource(dataSourceProxy); return sqlSessionFactoryBean; } }
Modified configuration
#Name of Registration Center spring.application.name=nacos-seata-storage-server server.port=8082 #Database connection spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.auto-commit=true spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=60000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.pool-name=DatebookHikariCP spring.datasource.hikari.maximum-pool-size=30 spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-product?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false spring.datasource.hikari.username=root spring.datasource.hikari.password=123456 spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver #Thing group spring.cloud.alibaba.seata.tx-service-group=prex_tx_group #Address of nacos Registration Center spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847 logging.level.com.tuling.seata.mapper=debug
Copy configuration from server
Copy the file.conf and registry.conf files from the server to the resources directory. Do not modify anything.
Business layer code
Only the core code, and finally the source code
Impl minus stock code
@Override public boolean reduceCount(Integer productId, Integer amount) throws Exception { log.info("commodity Id:{},Quantity of commodities:{}",productId,amount); log.info("current XID: {}", RootContext.getXID()); // Inventory inspection checkStock(productId, amount); log.info("Start deduction {} Stock", productId); Integer record = productMapper.reduceCount(productId,amount); log.info("Closing deduction {} Inventory results:{}", productId, record > 0 ? "Successful operation" : "Inventory deduction failed"); return false; }
Build order items
Citation dependency
Same as inventory deduction
Write notes
Same as inventory deduction
Write configuration add agent data source configuration
Same as inventory deduction
Copy configuration from server
Same as inventory deduction
Project configuration
spring.application.name=nacos-seata-order-server server.port=8081 spring.datasource.type=com.zaxxer.hikari.HikariDataSource spring.datasource.hikari.auto-commit=true spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.idle-timeout=60000 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.max-lifetime=1800000 spring.datasource.hikari.pool-name=DatebookHikariCP spring.datasource.hikari.maximum-pool-size=30 spring.datasource.hikari.jdbc-url=jdbc:mysql://127.0.0.1:3306/seata-order?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false spring.datasource.hikari.username=root spring.datasource.hikari.password=123456 spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver spring.cloud.alibaba.seata.tx-service-group=prex_tx_group spring.cloud.nacos.discovery.server-addr=47.112.217.177:8847 logging.level.com.tuling.seata.mapper=debug ribbon.eager-load.enabled=true feign.client.config.default.connectTimeout=5000 feign.client.config.default.readTimeout=5000
Core business code
The microservice initiator needs to write a global transaction annotation (in this case, the order service is the initiator). The parameter value of name should not be duplicate
//@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)
//@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class) @Transactional @Override public void createOrder(Order order) { log.info("current XID: {}", RootContext.getXID()); log.info("Order begins,user:{},commodity:{},Number:{},Amount of money:{}", order.getUserId(), order.getProductId(), order.getCount(), order.getPayMoney()); //Create order order.setStatus(0); orderMapper.saveOrder(order); log.info("Save order{}", order); //Remote call inventory service deduct inventory log.info("Start of inventory deduction"); remoteStorageService.reduceCount(order.getProductId(), order.getCount()); log.info("End of deducted inventory"); /* //Remote call account service deduction balance log.info("Deduction balance start "); remoteAccountService.reduceBalance(order.getUserId(), order.getPayMoney()); log.info("End of deduction balance);*/ //Make an exception List<String> l = new ArrayList<>(); l.get(100); //Modify order status to completed log.info("Modify order status start"); orderMapper.updateOrderStatusById(order.getId(),1); log.info("Modify order status end"); log.info("End of order"); }
Here, we use spring's common things Management @ Transactional to test the effect and add an exception code to the code.
Start project test
Original data:
Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
The exception causes the order data to be rolled back, but the inventory has been reduced.
Now remove the @ GlobalTransactional annotation of the order item and restart the order item
@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)
Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
As a result, the order was not added and the inventory was not reduced.
Remove the order item exception code and restart the order item
Access test: http://127.0.0.1:8081/order/create?userId=1&productId=1&count=1&payMoney=50
Source: https://gitee.com/hekang_admin/tulingvip06-ms-alibaba-nacos-seata.git