1.springboot树形结构 支持模糊查询,返回匹配节点和父节点,其他节点不返回
java
package com.me.meterdemo.ds;
import java.util.ArrayList;
import java.util.List;
public class TreeNode {
private Long id;
private String name;
private Long parentId;
private List<TreeNode> children = new ArrayList<>();
// 构造方法
public TreeNode(Long id, String name, Long parentId) {
this.id = id;
this.name = name;
this.parentId = parentId;
}
// Getters and Setters
public Long getId() { return id; }
public String getName() { return name; }
public Long getParentId() { return parentId; }
public List<TreeNode> getChildren() { return children; }
public void addChild(TreeNode child) {
// 防止重复添加
if (children.stream().noneMatch(c -> c.id.equals(child.id))) {
children.add(child);
}
}
}
java
package com.me.meterdemo.ds;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class TreeMemoryService {
// 内存数据存储
private final Map<Long, TreeNode> nodeMap = new HashMap<>();
// 初始化测试数据
@PostConstruct
private void initData() {
clearAndInitData();
}
private void clearAndInitData() {
nodeMap.clear();
addNode(1L, "集团总部", null);
addNode(2L, "华东公司", 1L);
addNode(3L, "技术部", 2L);
addNode(4L, "开发组", 3L);
addNode(5L, "测试组", 3L);
addNode(6L, "华南公司", 1L);
}
private void addNode(Long id, String name, Long parentId) {
TreeNode node = new TreeNode(id, name, parentId);
nodeMap.put(id, node);
}
/**
* 执行树结构搜索
* @param keyword 查询关键词(可为空)
* @return 符合条件节点及其父节点组成的树结构
*/
public List<TreeNode> search(String keyword) {
// 空关键词返回空结果
if (keyword != null && keyword.trim().isEmpty()) {
return Collections.emptyList();
}
// 获取所有匹配节点
List<TreeNode> matches = nodeMap.values().stream()
.filter(node -> matchesKeyword(node, keyword))
.collect(Collectors.toList());
// 收集需要展示的节点ID(匹配节点+父链)
Set<Long> visibleIds = new LinkedHashSet<>();
matches.forEach(node -> collectParentChain(node, visibleIds));
// 构建精简树结构
return buildPrunedTree(visibleIds);
}
// 判断节点是否匹配关键词
private boolean matchesKeyword(TreeNode node, String keyword) {
return keyword == null ||
node.getName().contains(keyword);
}
// 收集节点及其所有父节点
private void collectParentChain(TreeNode node, Set<Long> ids) {
Long currentId = node.getId();
while (currentId != null) {
if (!ids.add(currentId)) break; // 防止循环
TreeNode current = nodeMap.get(currentId);
currentId = current.getParentId();
}
}
// 构建修剪后的树结构
private List<TreeNode> buildPrunedTree(Set<Long> visibleIds) {
// 创建节点副本防止污染原始数据
Map<Long, TreeNode> visibleNodes = createNodeCopies(visibleIds);
// 构建层级关系
List<TreeNode> roots = new ArrayList<>();
visibleNodes.values().forEach(node -> {
Long parentId = node.getParentId();
if (parentId == null) {
roots.add(node);
} else {
TreeNode parent = visibleNodes.get(parentId);
if (parent != null) {
parent.addChild(node);
}
}
});
return roots;
}
// 创建节点副本(清空原有子节点)
private Map<Long, TreeNode> createNodeCopies(Set<Long> visibleIds) {
return visibleIds.stream()
.map(id -> nodeMap.get(id))
.filter(Objects::nonNull)
.collect(Collectors.toMap(
TreeNode::getId,
orig -> new TreeNode(orig.getId(),
orig.getName(),
orig.getParentId())
));
}
// 重置测试数据(可选)
public void resetData() {
clearAndInitData();
}
}
java
package com.me.meterdemo.ds;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class TreeDSController {
private final TreeMemoryService treeMemoryService;
public TreeDSController(TreeMemoryService treeService) {
this.treeMemoryService = treeService;
}
@GetMapping("/search")
public List<TreeNode> searchTree(@RequestParam(required = false) String keyword) {
return treeMemoryService.search(keyword);
}
}
2.springboot 返回树形结构 支持模糊匹配
java
package com.me.meterdemo.tree;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.util.List;
@Data
public class TreeNode {
private Long id;
private String name;
private Long parentId;
private List<TreeNode> children;
@JsonIgnore
private boolean matched; // 标记是否匹配搜索条件
}
java
package com.me.meterdemo.tree;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class TreeService {
// 模拟数据库数据
private List<TreeNode> getAllNodes() {
List<TreeNode> nodes = new ArrayList<>();
// 添加一些测试数据
TreeNode root = new TreeNode();
root.setId(1L);
root.setName("目录one");
root.setParentId(0L);
TreeNode child1 = new TreeNode();
child1.setId(2L);
child1.setName("测试函数1");
child1.setParentId(1L);
TreeNode child2 = new TreeNode();
child2.setId(3L);
child2.setName("测试时间函数");
child2.setParentId(1L);
TreeNode grandChild1 = new TreeNode();
grandChild1.setId(4L);
grandChild1.setName("测试date函数");
grandChild1.setParentId(2L);
nodes.add(root);
nodes.add(child1);
nodes.add(child2);
nodes.add(grandChild1);
return nodes;
}
public List<TreeNode> searchTree(String keyword) {
List<TreeNode> allNodes = getAllNodes();
Map<Long, TreeNode> nodeMap = allNodes.stream()
.collect(Collectors.toMap(TreeNode::getId, node -> node));
// 构建树形结构
List<TreeNode> rootNodes = allNodes.stream()
.filter(node -> node.getParentId() == 0)
.collect(Collectors.toList());
// 递归构建树
rootNodes.forEach(root -> buildTree(root, nodeMap));
// 如果没有关键字,返回完整的树形结构
if (keyword == null || keyword.trim().isEmpty()) {
return rootNodes;
}
// 搜索并过滤不匹配的节点
List<TreeNode> filteredRoots = new ArrayList<>();
for (TreeNode root : rootNodes) {
TreeNode filteredRoot = filterTree(root, keyword);
if (filteredRoot != null) {
filteredRoots.add(filteredRoot);
}
}
return filteredRoots;
}
private void buildTree(TreeNode node, Map<Long, TreeNode> nodeMap) {
List<TreeNode> children = nodeMap.values().stream()
.filter(n -> n.getParentId().equals(node.getId()))
.collect(Collectors.toList());
node.setChildren(children);
children.forEach(child -> buildTree(child, nodeMap));
}
private TreeNode filterTree(TreeNode node, String keyword) {
// 如果当前节点匹配,保留该节点
boolean isMatched = node.getName().contains(keyword);
node.setMatched(isMatched);
// 处理子节点
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
List<TreeNode> filteredChildren = new ArrayList<>();
for (TreeNode child : node.getChildren()) {
TreeNode filteredChild = filterTree(child, keyword);
if (filteredChild != null) {
filteredChildren.add(filteredChild);
}
}
// 如果当前节点匹配或者有匹配的子节点,保留该节点
if (isMatched || !filteredChildren.isEmpty()) {
node.setChildren(filteredChildren);
return node;
}
} else {
// 如果是叶子节点,只有匹配时才保留
return isMatched ? node : null;
}
return null;
}
}
java
package com.me.meterdemo.demos.web;
import com.me.meterdemo.ds.TreeNodeDTO;
import com.me.meterdemo.tree.TreeNode;
import com.me.meterdemo.tree.TreeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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;
import java.util.List;
@RestController
@RequestMapping("/api/tree")
public class TreeController {
@Autowired
private TreeService treeService;
@GetMapping("/search")
public List<TreeNode> searchTree(@RequestParam(required = false) String keyword) {
return treeService.searchTree(keyword);
}
}
3.springboot 返回树形结构 从任意节点开始生成,不一定有根节点
java
package com.me.meterdemo.treeList;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class TreeNode {
private Long id;
private String name;
private Long parentId;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<TreeNode> children = new ArrayList<>();
// 构造方法、getter、setter
public TreeNode(Long id, String name, Long parentId) {
this.id = id;
this.name = name;
this.parentId = parentId;
}
// 省略getter和setter...
}
java
package com.me.meterdemo.treeList;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class TreeListService {
public List<TreeNode> buildTree() {
List<TreeNode> allNodes = findAll();
Map<Long, TreeNode> nodeMap = new HashMap<>();
List<TreeNode> topNodes = new ArrayList<>();
// 将所有节点存入Map,并初始化children
for (TreeNode node : allNodes) {
node.setChildren(new ArrayList<>());
nodeMap.put(node.getId(), node);
}
// 构建树结构
for (TreeNode node : allNodes) {
Long parentId = node.getParentId();
if (parentId != null) {
TreeNode parent = nodeMap.get(parentId);
if (parent != null) {
parent.getChildren().add(node);
} else {
// 父节点不存在,当前节点作为顶级节点
topNodes.add(node);
}
} else {
// parentId为null,直接作为顶级节点
topNodes.add(node);
}
}
return topNodes;
}
private List<TreeNode> findAll() {
List<TreeNode> nodes = new ArrayList<>();
// nodes.add(new TreeNode(1L, "集团总部", null));
// nodes.add(new TreeNode(2L, "华东公司", 1L));
nodes.add(new TreeNode(3L, "技术部", 2L));
nodes.add(new TreeNode(4L, "开发组", 3L));
nodes.add(new TreeNode(5L, "测试组", 3L));
nodes.add(new TreeNode(6L, "华南公司", 1L));
nodes.add(new TreeNode(7L, "中国烟草", null));
return nodes;
}
}
java
package com.me.meterdemo.treeList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class TreeListController {
@Autowired
TreeListService treeListService;
@GetMapping("/tree/1")
public ResponseEntity<List<TreeNode>> getTree() {
List<TreeNode> tree = treeListService.buildTree();
return ResponseEntity.ok(tree);
}
}
4 springboot naos seata 集成
4.1 maven 依赖 nacos 1.4.2 seata 1.4.2
客户端的每个数据库 度需要创建一个 undo_log 的表
xml
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.me</groupId>
<artifactId>order-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>order-service</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.12.RELEASE</spring-boot.version>
<spring-cloud.version>Hoxton.SR12</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.6.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.28</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version> <!-- 添加这一行 -->
<configuration>
<mainClass>com.me.OrderApp</mainClass>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
4.2 bootstrap .yml 配置
yml
server:
port: 8080
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
config:
server-addr: 127.0.0.1:8848
file-extension: yml
auto-refresh: true
data-id: ${spring.application.name}
## seata 配置
seata:
## 事务组配置 需要和 seata 的registry.conf 配置文件相同
tx-service-group: my_test_tx_group
service:
vgroup-mapping.my_test_tx_group: default
enabled: true
application-id: ${spring.application.name}
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: ''
## 注意这里有坑 源码中默认的是 SEATA_GROUP 如果不指定 就是这个默认值 会导致无法找到可用的服务
group: 'DEFAULT_GROUP'
username: "nacos"
password: "nacos"
# 事务模式 AT XA
data-source-proxy-mode: AT
4.3 NACOS 配置文件修改 执行SQL脚本 application.properties
properties
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos23?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=root
然后执行sql 脚本 创建 数据库 和表结构
4.4 seat 服务端配置 1.4.2
seata 的数据库需要执行脚本创建三个表 file.conf 修改为数据库存储
## transaction log store, only used in seata-server
store {
## store mode: file、db、redis
mode = "db"
## rsa decryption public key
publicKey = ""
## database store property
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
datasource = "druid"
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = "mysql"
driverClassName = "com.mysql.jdbc.Driver"
## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param
url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true"
user = "root"
password = "root"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}
registry.conf 修改为 nacos 注册中心 和配置中心
registry {
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "DEFAULT_GROUP"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
}
config {
# file、nacos 、apollo、zk、consul、etcd3、springCloudConfig
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "DEFAULT_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}
4.5 nacos 创建配置
在nacos 中创建吗一个 dataId=seataServer.properties 的 配置文件 修改 数据库的配置 和事务组的配置
service.vgroupMapping.my_test_tx_group=default
properties
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
## 这里配置的事务组 值就是 seata cluster=default 在多机房切换用到
service.vgroupMapping.my_test_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=false
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
## 配置 存储为db 并配置响应的用户名和密码 等数据库信息
store.mode=db
store.publicKey=
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
···
然后客户端便可以使用分布式事务进行处理
5. springboot aop 解决幂等 基于数据库表的修改时间字段, 时间戳机制
场景描述 一个表的数据 由于权限控制 导致 数据能够被很多人看到,这些人分布在全国各地,有同一时刻修改同一条数据的可能, 需要控制只能有一个修改成功
接口如下
java
package com.me.demo2.controller;
import com.me.demo2.domain.Emp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
public class JDBCController {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 获取详情接口
* @param id
* @return
*/
@GetMapping("/get/{id}")
public Map<String,Object> get(@PathVariable("id") Integer id){
Map<String,Object> res = jdbcTemplate.queryForMap("select * from tu_emp where id=?",new Object[]{id});
return res;
}
/**
*
* 修改的前提是必须先调用 获取详情接口 查询信息
* 修改接口
* */
@PostMapping("/update")
public Map<String,Object> update(@RequestBody Emp emp){
int update = jdbcTemplate.update("update tu_emp set name=? ,update_time=now() where id=?",emp.getName(),emp.getId() );
Map<String,Object> res = jdbcTemplate.queryForMap("select * from tu_emp where id=?",emp.getId());
return res;
}
}
解决这个问题 需要对 update这个接口进行切面处理 保证只有有一个人修改成功
java
package com.me.demo2.aspects;
import com.me.demo2.domain.Emp;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;
import java.sql.ResultSet;
import java.sql.SQLException;
@Aspect
@Component
public class RepeatAspect {
@Autowired
JdbcTemplate jdbcTemplate;
/**
获取方法参数
从数据库查询修改时间的这个字段 进行比较 如果不一致则为重复交易 则终止请求
*/
@Around("execution(public * com.me.demo2.controller.JDBCController.update(..))")
public Object repeatHandler(ProceedingJoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
Emp dto = (Emp) args[0];
Emp empDO = jdbcTemplate.queryForObject("select * from tu_emp where id=?", new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int rowNum) throws SQLException {
Emp emp1 = new Emp();
emp1.setCreate_time(rs.getTimestamp("create_time"));
emp1.setId(rs.getInt("id"));
emp1.setName(rs.getString("name"));
emp1.setUpdate_time(rs.getTimestamp("update_time"));
return emp1;
}
}, dto.getId());
if (!dto.getUpdate_time().equals(empDO.getUpdate_time())){
throw new RuntimeException("重复交易 稍后重试");
}else {
try {
return joinPoint.proceed(args);
}catch (Throwable e){
e.printStackTrace();
}
}
return null;
}
}