springboot mongodb分片集群事务

前置

mongodb分片集群想要使用事务,需要对应分片没有仲裁节点

代码

复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
              <version>2.1.0.RELEASE</version>
        </dependency>

如果是单个mongos

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.MongoTransactionManager;
import org.springframework.stereotype.Component;

/**
 * @author kittlen
 * @date 2024-04-09 17:20
 * @description
 */

@Component
public class MongodbConfig {

    

    @Bean
    public MongoTransactionManager transactionManager(MongoDbFactory factory) {
        return new MongoTransactionManager(factory);
    }
}

使用

java 复制代码
	@Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoTransactionManager mongoTransactionManager;

	public int dbFunc(){
        TransactionTemplate transactionTemplate = new TransactionTemplate(mongoTransactionManager);
		return transactionTemplate.execute(status -> {
                    try {
                        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, collection1);
                        long l = updateResult.getUpsertedId() == null ? updateResult.getModifiedCount() : 1;
                        if (l > 0) {
                            mongoTemplate.insert(saveEntity, collection2);
                        }
                        return 1;
                    } catch (Exception e) {
                        // 如果发生异常,事务将在此处回滚,通过status.setRollbackOnly();或者抛出异常都可回滚
                       status.setRollbackOnly();
                       return 0;
                    }
                });
}

如果连接是多mongos,则需要重写BaseCluster类

多mongos时使用的是随机获取的方式获取mongosClient,通过记录第一次调用的client使后续事务内的请求都通过同一个client请求,防止出现不同mongos导致事务失败情况

事务记录类

java 复制代码
import com.mongodb.connection.Server;

import java.util.function.Supplier;

/**
 * @author kittlen
 * @date 2024-04-29 12:08
 * @description
 */

public class MultiServiceTransactionConfig {

    /**
     * mongodb多实例事务使用
     */
    private static ThreadLocal<Server> mongoMultiServerTransactionUserService = new ThreadLocal<>();

    /**
     * 是否开启多实例事务
     */
    private static ThreadLocal<Boolean> mongoMultiServerTransactionCanUser = new ThreadLocal<>();

    /**
     * 获取service
     *
     * @param supplier 如果该service不存在,则获取新service的方法
     * @return
     */
    public static Server getService(Supplier<Server> supplier) {
        Server server = mongoMultiServerTransactionUserService.get();
        if (server != null) {
            return server;
        } else {
            Server saveServer = supplier.get();
            mongoMultiServerTransactionUserService.set(saveServer);
            return saveServer;
        }
    }

    /**
     * 开启事务记录
     */

    public static void openMultiServerTransaction() {
        mongoMultiServerTransactionCanUser.set(true);
    }

    /**
     * 是否开启多实例事务
     *
     * @return
     */
    public static boolean canOpenMultiServerTransaction() {
        Boolean b = mongoMultiServerTransactionCanUser.get();
        return Boolean.TRUE.equals(b);
    }

    /**
     * 清除事务配置信息
     */
    public static void clean() {
        mongoMultiServerTransactionCanUser.remove();
        mongoMultiServerTransactionUserService.remove();
    }
}

重写mongodb的类com.mongodb.internal.connection.BaseCluster的selectServer方法

java 复制代码
	@Override
    public Server selectServer(final ServerSelector serverSelector) {
        isTrue("open", !isClosed());

        try {
            CountDownLatch currentPhase = phase.get();
            ClusterDescription curDescription = description;
            ServerSelector compositeServerSelector = getCompositeServerSelector(serverSelector);
            Server server;
            if (this instanceof MultiServerCluster) {
                server = MultiServiceTransactionConfig.canOpenMultiServerTransaction() ? MultiServiceTransactionConfig.getService(() -> selectRandomServer(compositeServerSelector, description)) : selectRandomServer(compositeServerSelector, curDescription);
            } else {
                server = selectRandomServer(compositeServerSelector, curDescription);
            }
            boolean selectionFailureLogged = false;

            long startTimeNanos = System.nanoTime();
            long curTimeNanos = startTimeNanos;
            long maxWaitTimeNanos = getMaxWaitTimeNanos();

            while (true) {
                throwIfIncompatible(curDescription);

                if (server != null) {
                    return server;
                }

                if (curTimeNanos - startTimeNanos > maxWaitTimeNanos) {
                    throw createTimeoutException(serverSelector, curDescription);
                }

                if (!selectionFailureLogged) {
                    logServerSelectionFailure(serverSelector, curDescription);
                    selectionFailureLogged = true;
                }

                connect();

                currentPhase.await(Math.min(maxWaitTimeNanos - (curTimeNanos - startTimeNanos), getMinWaitTimeNanos()), NANOSECONDS);

                curTimeNanos = System.nanoTime();

                currentPhase = phase.get();
                curDescription = description;
                server = selectRandomServer(compositeServerSelector, curDescription);
            }

        } catch (InterruptedException e) {
            throw new MongoInterruptedException(format("Interrupted while waiting for a server that matches %s", serverSelector), e);
        }
    }

重点为:

java 复制代码
			Server server;
            if (this instanceof MultiServerCluster) {
                server = MultiServiceTransactionConfig.canOpenMultiServerTransaction() ? MultiServiceTransactionConfig.getService(() -> selectRandomServer(compositeServerSelector, description)) : selectRandomServer(compositeServerSelector, curDescription);
            } else {
                server = selectRandomServer(compositeServerSelector, curDescription);
            }

使用

java 复制代码
			try {
                TransactionTemplate transactionTemplate = new TransactionTemplate(mongoTransactionManager);
                MultiServiceTransactionConfig.openMultiServerTransaction();
                return transactionTemplate.execute(status -> {
                    try {
                        UpdateResult updateResult = mongoTemplate.updateFirst(query, update,ollection1);
                        long l = updateResult.getUpsertedId() == null ? updateResult.getModifiedCount() : 1;
                        if (l > 0) {
                            mongoTemplate.insert(historyDetailsEntity, collection2);
                        }
                        return 1;
                    } catch (Exception e) {
                        // 如果发生异常,事务将在此处回滚,通过status.setRollbackOnly();或者抛出异常都可回滚
                       status.setRollbackOnly();
                       return 0;
                    }
                });
            } finally {
                MultiServiceTransactionConfig.clean();
            }
相关推荐
llz_1121 小时前
web-第二次课后作业
前端·后端·web
红尘散仙7 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
来杯@Java8 小时前
图书管理系统(基于springboot+vue前后端分离的项目)计算机毕业设计java
java·spring boot·spring·vue·毕业设计·mybatis·课程设计
卷毛的技术笔记9 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆9 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
喵个咪9 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball61610 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_25183645710 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao10 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
IT_陈寒11 小时前
Vite打包时遇到的坑,原来问题出在这里
前端·人工智能·后端