Skip to content

Commit

Permalink
添加事务使用实例
Browse files Browse the repository at this point in the history
  • Loading branch information
liuyueyi committed Jun 14, 2023
1 parent 833f39d commit 0b836ba
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
34 changes: 34 additions & 0 deletions spring-boot/100-transaction-manager/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-boot</artifactId>
<groupId>com.git.hui.boot</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>transaction-manager</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.git.hui.boot.trans;

import com.git.hui.boot.trans.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author YiHui
* @date 2023/6/14
*/
@RestController
@SpringBootApplication
public class Application {
@Autowired
private DemoService demoService;

@GetMapping(path = "/")
public String preExecute() {
try {
demoService.transExecute(true);
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
return "ok";
}

@GetMapping(path = "/out")
public String outExecute() {
try {
demoService.outTransExecute();
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
return "ok";
}


public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.git.hui.boot.trans.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

import java.util.concurrent.atomic.AtomicInteger;

/**
* @author YiHui
* @date 2023/6/14
*/
@Slf4j
@Service
public class DemoService {
@Autowired
private JdbcTemplate jdbcTemplate;

private ThreadLocal<String> cache = new ThreadLocal<>();
private AtomicInteger cnt = new AtomicInteger(1);

@Autowired
private DemoService demoService;

@Transactional(rollbackFor = Exception.class)
public void outTransExecute() throws InterruptedException {
String name = "2灰灰" + cnt.getAndAdd(1);
log.info("{}-out写入缓存: {}", Thread.currentThread(), name);
jdbcTemplate.execute("insert into money (`name`, `money`) values ('" + name + "', " + cnt.get() + ");");

new Thread(new Runnable() {
@Override
public void run() {
demoService.transExecute(false);
}
}).start();

// 验证外部事务回滚,子线程的事务是否也会回滚(判断两个事务是否是同一个)
log.info("外部执行完毕!");
Thread.sleep(2000);
Assert.isTrue(Math.random() > 0.5, "外部事务回滚");
}

/**
* 事务提交前执行某些操作
* - 再同一个线程中执行,不过注册的事务提交前执行任务,会后置到这个方法执行完,再然后在相同的线程中执行
*/
@Transactional(rollbackFor = Exception.class)
public void transExecute(boolean hasException) {
String name = "一灰灰" + cnt.getAndAdd(1);
log.info("{}-外部写入缓存: {}", Thread.currentThread(), name);
cache.set(name);
jdbcTemplate.execute("insert into money (`name`, `money`) values ('" + name + "', " + cnt.get() + ");");
registryBeforeCommitOrImmediatelyRun(new Runnable() {
@Override
public void run() {
String last = cache.get();
log.info("{}-缓存中的新增数据: {}", Thread.currentThread(), last);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}

if (hasException) {
// 让这里概率性失败,从而模拟事务回滚的场景
Assert.isTrue(Math.random() > 0.5f, "模拟事务回滚!");
}
}
});

log.info("transExecute 执行完毕!");
}


/**
* 注册事务回调-事务提交前执行,如果没在事务中就立即执行
*
* @param runnable
*/
public static void registryBeforeCommitOrImmediatelyRun(Runnable runnable) {
if (runnable == null) {
return;
}
// 处于事务中
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// 等事务提交前执行,发生错误会回滚事务
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void beforeCommit(boolean readOnly) {
runnable.run();
}
});
} else {
// 马上执行
runnable.run();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## log
logging.file=logs/info.log
logging.file.max-history=2
logging.file.max-size=1GB

## DataSource
spring.datasource.url=jdbc:mysql://localhost:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2b8
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.username=root
#spring.datasource.password=
1 change: 1 addition & 0 deletions spring-boot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<module>014-spel-aop</module>

<module>100-mysql</module>
<module>100-transaction-manager</module>
<module>101-jdbctemplate</module>
<module>101-jdbctemplate-transaction</module>
<module>102-jpa</module>
Expand Down

0 comments on commit 0b836ba

Please sign in to comment.