1.创建数据库表
- 注意:不要给 student 表添加外键约束。如果添加会导致分布式事务执行时 student 新增失败,因为 teacher 没有提交时 student 的 tid 值无法获取。
2.创建项目
- 案例使用聚合项目进行演示。
- 创建父项目,名称为 LcnParent
2.1 配置 pom.xml
- txlcn-tc 是 TX-LCN 的客户端包
- txlcn-txmsg-netty 是 LCN 客户端连接 TxManager 需要的包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<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>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.新建 pojo 项目
- 把实体类提出来
- 新建两个实体类。
- 新建 com.dqcgm.pojo.Teacher
@Data
public class Teacher {
private Long id;
private String name;
}
- 新建 com.dqcgm.pojo.Student
@Data
public class Student {
private Long id;
private String name;
private Long tid;
}
4.创建项目 teacher_insert
4.1 配置 pom.xml
<dependencies>
<dependency>
<artifactId>pojo</artifactId>
<groupId>com.dqcgm</groupId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
4.2 编写配置文件
- 新建 application.yml.
- 数据源连接的是 Teacher 表所在数据库
- eureka 单机版可以省略。
- manager-address 配置 TxManager 项目的 ip 及端口。端口是内部访问端口,而不是可视化页面的端口
spring:
datasource:
url: jdbc:mysql://localhost:3306/microservice
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
application:
name: teacher-insert
server:
port: 8080
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
tx-lcn:
client:
manager-address: 127.0.0.1:8070
4.3 新建 mapper
- 新建 com.dqcgm.mapper.TeacherMapper
@Mapper
public interface TeacherMapper {
@Insert("insert into teacher values(#{id},#{name})")
int insert(Teacher teacher);
}
4.4 新建 service 及实现类
- 新建 com.dqcgm.service.TeacherService 及实现类。
- 方法上@Transactional 一定要有。本地事务控制。
- @LcnTransaction 表示当前方法加入到分布式事务控制。
- @LcnTransaction 属性 propagation 可取值
1、 DTXPropagation.REQUIRED:默认值,表示如果当前没有事务组创建事务组,如果有事务组,加入事务组多用在事务发起方;
DTXPropagation.SUPPORTS:如果当前没有事务组以本地事务运行,如果当前有事务组加入事务组。多用在事务参与方法。
public interface TeacherService {
int insert(Teacher teacher);
}
@Service
public class TeacherServiceImpl implements TeacherService {
@Autowired
private TeacherMapper teacherMapper;
@Override
@LcnTransaction
@Transactional
public int insert(Teacher teacher) {
return teacherMapper.insert(teacher);
}
}
4.5 新建控制器
- 新建 com.dqcgm.controller.TeacherController。
- 由于在 student_insert 中通过 OpenFeign 进行条件,参数使用请求体数据,所以控制器方法的参数需要添加@RequestBody
@Controller
public class TeacherController {
@Autowired
private TeacherService teacherService;
@RequestMapping("/insert")
@ResponseBody
public int insert(@RequestBody Teacher teacher){
System.out.println("taecher"+teacher);
return teacherService.insert(teacher);
}
}
4.6 新建启动器
- 新建 com.dqcgm.TeacherInsertApplication。
- 一定要有注解@EnableDistributedTransaction 表示启动分布式事务
@SpringBootApplication
@EnableDistributedTransaction
public class TeacherInsertApplication {
public static void main(String[] args) {
SpringApplication.run(TeacherInsertApplication.class,args);
}
}
5.新建项目 student_insert
5.1 编写 pom.xml
<dependencies>
<dependency>
<artifactId>pojo</artifactId>
<groupId>com.dqcgm</groupId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
5.2 创建配置文件
spring:
datasource:
url: jdbc:mysql://localhost:3306/microservice
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
application:
name: student-insert
server:
port: 8081
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
tx-lcn:
client:
manager-address: 127.0.0.1:8070
5.3 新建 Feign 接口
- 新建 com.dqcgm.feign.TeacherInsertFeign
@FeignClient("teacher-insert")
public interface TeacherInsertFeign {
@RequestMapping("/insert") int insert(Teacher teacher);
}
5.4 新建 Mapper
- 新建 com.dqcgm.mapper.StudentMapper
@Mapper
public interface StudentMapper {
@Insert("insert into student values(#{id},#{name},#{tid})")
int insert(Student student);
}
5.5 新建 service 及实现类
- 新建 com.dqcgm.service.StudentService
- 实现类中对 Teacher 和 Student 的主键都是随机数,为了测试时多次测试方便,所以没有给固定值
- Student 的姓名通过客户端请求参数传递的,其他都不需要通过参数设置
public interface StudentService {
int insert(Student student);
}
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
private StudentMapper studentMapper;
@Autowired
private TeacherInsertFeign teacherInsertFeign;
@Override
@LcnTransaction
@Transactional
public int insert(Student student) {
Teacher teacher = new Teacher();
Random random = new Random();
teacher.setId((long)random.nextInt(10000));
teacher.setName("随意的名称");
student.setTid(teacher.getId());
student.setId((long)random.nextInt(10000));
teacherInsertFeign.insert(teacher);
return studentMapper.insert(student);
}
}
5.6 新建控制器
- 新建 com.dqcgm.controller.StudentController
@Controller
public class StudentController {
@Autowired
private StudentService studentService;
@RequestMapping("/insert")
@ResponseBody
public int insert(Student student){
return studentService.insert(student);
}
}
5.7 新建启动类
- 新建 com.dqcgm.StudentInsertApplication
@SpringBootApplication
@EnableDistributedTransaction
@EnableFeignClients
public class StudentInsertApplication {
public static void main(String[] args) {
SpringApplication.run(StudentInsertApplication.class,args);
}
}
6.测试结果
- 在浏览器中输入 http://localhost:8081/insert?name=dqcgm后,如果页面显示 1 并且数据库 teacher 表和 student 表各增加一条数据表示新增成功。
- 为了测试分布式事务效果,在 student_insert 项目实现类方法 return 上面添加 int i =5/0; 的算术异常,再次访问 url 页面会报 500,并且数据库中没有新增数据,说明分布式事务成功了。