(1)导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent><dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency><dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tm</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
(2)编写配置文件
spring.application.name=TransactionManager
server.port=7970
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root# redis配置
spring.redis.host=192.168.8.128# TM服务器IP修改, TC访问TM时,使用的IP地址。必须精确匹配。默认127.0.0.1。代表TC和TM必须在同一个主机中。
tx-lcn.manager.host=192.168.41.252# TM服务器日志系统配置,默认关闭日志系统。需要独立配置日志的存储数据库连接
tx-lcn.logger.enabled=false
# 配置TM服务器日志系统数据库连接
tx-lcn.logger.driver-class-name=com.mysql.cj.jdbc.Driver
tx-lcn.logger.jdbc-url=jdbc:mysql://localhost:3306/tx-manager?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
tx-lcn.logger.username=root
tx-lcn.logger.password=root
# 自动创建表格的配置项。可以自动创建日志表格。
spring.jpa.hibernate.ddl-auto=update# 修改TM服务器的WEB控制台登录密码, 默认登录密码是 codingapi
tx-lcn.manager.admin-key=bjsxt# TM事务管理端口。默认 0. 是server.port + 100计算得到。
# tx-lcn.manager.port=7971
(3)编写启动项
/**
* EnableTransactionManagerServer - 开启TM服务器功能。
*
* 必须提供配置文件。注意:配置文件只能是properties格式。
* 原因:txlcn-tm中包含一个配置文件,命名是application.properties。
* 读取优先级高于yml配置文件。所以定义yml配置文件,会被忽略。
*
*/
@SpringBootApplication
@EnableTransactionManagerServer
public class MyTMApp {
public static void main(String[] args) {
SpringApplication.run(MyTMApp.class, args);
}
}
(4)导入SQL
CREATE DATABASE IF NOT EXISTS `tx-manager` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
USE `tx-manager`;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for t_tx_exception
-- ----------------------------
DROP TABLE IF EXISTS `t_tx_exception`;
CREATE TABLE `t_tx_exception` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`transaction_state` tinyint(4) NULL DEFAULT NULL,
`registrar` tinyint(4) NULL DEFAULT NULL,
`ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 待处理 1已处理',
`remark` varchar(10240) NULL DEFAULT NULL COMMENT '备注',
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 967 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;
(5)访问管理平台
通过浏览器访问 http://localhost:7970 。在界面中输入登录密码:bjsxt(默认密码是codingapi)。
(1)导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
</dependency>
</dependencies>
(2)编写配置文件
server:
port: 8001
spring:
application:
name: tx-book-manager
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tx-manager?serverTimezone=Asia/Shanghai
username: root
password: root
# 配置TM服务器地址。格式是 ip:port。 ip和端口查看TM WEB控制台中的IP和端口。
tx-lcn:
client:
manager-address: 192.168.41.252:8070
(3)实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book implements Serializable {
private Long id;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date publishTime;
private Integer studentId; // 书籍所属学生主键
}
(4)数据访问接口
/**
* 书籍数据访问对象
*/
@Mapper
public interface BookMapper {
/**
* 新增书籍
* @param book
* @return
*/
@Insert("insert into tb_book (id, name, publish_time, student_id) " +
"values(#{id}, #{name}, #{publishTime}, #{studentId})")
int insert(Book book);
/**
* 根据学生主键,查询对应的所有书籍
* @param studentId
* @return
*/
@Select("select id, name, publish_time as publishTime, student_id as studentId " +
"from tb_book where student_id = #{studentId}")
List<Book> selectByStudent(Integer studentId);
}
(5)编写服务层
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
/**
* 1. 生成主键
* 2. 新增数据
* @param book
* @return
*/
@Override
@LcnTransaction
@Transactional
public String addBook(Book book) {
book.setId(System.currentTimeMillis());
int rows = bookMapper.insert(book);
if(rows != 1){
throw new RuntimeException("新增书籍失败");
}
return "新增书籍成功";
}
/**
* 根据学生查询书籍
* @param studentId
* @return
*/
@Override
public List<Book> getBooksByStudent(Integer studentId) {
return bookMapper.selectByStudent(studentId);
}
}
(6)编写控制层
/**
* 书籍控制器
*/
@RestController
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping("/addBook")
public String addBook(Book book){
return bookService.addBook(book);
}
@RequestMapping("/getBooksByStudentId")
public List<Book> getBooksByStudentId(Integer studentId){
return bookService.getBooksByStudent(studentId);
}
}
(7)编写启动类
/**
* EnableDistributedTransaction - 开启分布式事务管理逻辑。
* 支持TX-LCN框架的所有分布式事务管理模式。
*/
@SpringBootApplication
@EnableDistributedTransaction
public class TxBooksManagerApp {
public static void main(String[] args) {
SpringApplication.run(TxBooksManagerApp.class, args);
}
}
(1)导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-tc</artifactId>
</dependency>
<dependency>
<groupId>com.codingapi.txlcn</groupId>
<artifactId>txlcn-txmsg-netty</artifactId>
</dependency>
<!-- tc依赖要求,必须连接数据库。
TX-LCN是基于数据库中的表格t_tx_exception,控制事务组
tc依赖,没有数据库连接相关资源。需要独立依赖配置。
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
(2)编写配置文件
server:
port: 8002
spring:
application:
name: tx-phone-manager
redis:
host: 192.168.8.128
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tx-manager?serverTimezone=Asia/Shanghai
username: root
password: root
tx-lcn:
client:
manager-address: 192.168.41.252:8070
phone:
manager:
keyPref: 'student:phone::'
(3)编辑实体类
/**
* 保存在Redis中。
* key是 前缀 + 学生主键。
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Phone implements Serializable {
private String phoneNo; // 电话号码
private Integer studentId; // 电话所属学生主键
}
(4)编辑数据访问实体类
@Repository
public class RedisDaoImpl implements RedisDao {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void del(String key) {
redisTemplate.delete(key);
}
@Override
public void set(String key, Object value) {
redisTemplate.opsForValue()
.set(key, value);
}
@Override
public <T> T get(String key) {
T value = (T) redisTemplate.opsForValue()
.get(key);
return value;
}
}
(5)编写配置类
@Configuration
public class PhoneManagerConfiguration {
/**
* redis连接工厂由spring-boot-starter-data-redis自动创建。
* 根据配置文件,连接服务器。
* @param factory
* @return
*/
protected RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> redisTemplate =
new RedisTemplate<>();
// 配置,访问Redis服务器时,如何序列化键值对数据对象。
// key使用字符串序列化方式。就是字符串原值。
redisTemplate.setKeySerializer(new StringRedisSerializer());
// value使用JSON序列化方式。就是把Java对象,转换成JSON字符串并保存。
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// hash数据的key序列化方案。字符串原值
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// hash数据的value序列化方案,JSON格式字符串
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
// 设置Redis连接工厂
redisTemplate.setConnectionFactory( factory );
return redisTemplate;
}
}
(6)编写服务层
@Service
public class PhoneServiceImpl implements PhoneService {
@Autowired
private RedisDao redisDao;
@Value("${phone.manager.keyPref}")
private String keyPrefix;
/**
* 新增电话
* 1. 拼接key
* 2. 保存到Redis数据库
*
* 分布式事务参与方法,需要使用注解@TccTransaction注解修饰
* 建议:为提示开发者和后期的维护人员,当前方法有事务管理,
* 在方法上增加事务管理注解@Transactional。
*
* 使用TCC事务管理模式,做事务控制。
* 要求定义confirm和cancel方法。
*
* confirm方法定义要求。实现事务提交逻辑。
* 方法名: 是 confirm + try方法名称首字母转大写
* 访问修饰符、参数表、返回值、抛出异常,和try方法一致
*
* cancel方法定义要求。实现事务回滚逻辑
* 方法名: 是 cancel + try方法名称首字母转大写
* 访问修饰符、参数表、返回值、抛出异常,和try方法一致
* @param phone
* @return
*/
@Override
@TccTransaction
@Transactional
public String addPhone(Phone phone) {
String key = keyPrefix + phone.getStudentId();
// 保存到redis
redisDao.set(key, phone);
return "新增电话成功";
}
public String confirmAddPhone(Phone phone){
System.out.println("事务提交,新增电话结束");
return "新增电话成功";
}
public String cancelAddPhone(Phone phone){
// 回滚,就是删除新增的数据
String key = keyPrefix + phone.getStudentId();
redisDao.del(key);
System.out.println("事务回滚,新增的电话已删除");
return "新增电话失败";
}
/**
* 根据学生主键,查询电话
* 1. 拼接key
* 2. 访问redis数据库,查询数据
*/
@Override
public Phone getPhoneByStudent(Integer studentId) {
String key = keyPrefix + studentId;
Phone phone = redisDao.get(key);
return phone;
}
}
(7)编辑控制层
/**
* 电话管理控制器
*/
@RestController
public class PhoneController {
@Autowired
private PhoneService phoneService;
/**
* 新增电话号
* @param phone
* @return
*/
@RequestMapping("/addPhone")
public String addPhone(Phone phone){
return phoneService.addPhone(phone);
}
/**
* 根据学生主键,查询电话
* @param studentId
* @return
*/
@RequestMapping("/getPhoneByStudent")
public Phone getPhoneByStudent(Integer studentId){
return phoneService.getPhoneByStudent(studentId);
}
}
(8)编辑启动类
@SpringBootApplication
@EnableDistributedTransaction
public class TxPhoneManagerApp {
public static void main(String[] args) {
SpringApplication.run(TxPhoneManagerApp.class, args);
}
}