Java Spring Data MongoDB 在运行时更改数据库(多租户)

问题描述 投票:0回答:1

我想在多个数据库中拥有相同的集合结构,如下

Database A:
 CollectionA
 CollectionB

Database B:
 CollectionA
 CollectionB

假设我使用各自的存储库创建文档 例如。

@Document(collection = collectionA)
@Document(collection = collectionB)

我如何进行配置以强制 spring 在操作之间交换存储库 bean 的数据库?我进行了搜索,发现我需要创建一个 mongoTemplate bean,它似乎不起作用,此方法仅在存储库自动连接时调用一次,但在插入时不会调用。

例如。

# Inside a service
@Autowired
DocumentARepository documentARepository;

documentARepository.insert(documentA); # Inserts in Database A Collection A
TenantContext.setTenant("Database B"); # Changes MongoDb Repository Tenant (Database) thread safe class i use for jdbc multitenancy
docucmentARepository.insert(documentB); #Insert in Database B Collection A

已经查看了基于 Spring Data 的多租户 mongodb 数据库及其旧版本的 spring data mongodb 我使用 3.1.3

如果有人可以建议我如何调试在插入文档期间使用了哪些bean,那么欢迎我自己解决它。不知道我将如何使用它来手动解决它。预先感谢

java spring mongodb multi-tenant
1个回答
0
投票

将通过放弃 MongoRepositorie 接口并使用 MongoTemplate bean 来解决这个问题。像这样配置:

Mongo 配置文件

@Configuration
public class MongoConfiguration {

    @Bean
    public MongoClient mongoClient() {
        return MongoClients.create();
    }

    @Bean
    public MultiTenantMongoDbFactory multiTenantMongoDatabaseFactory(MongoClient mongoClient) {
        return new MultiTenantMongoDbFactory(mongoClient);
    }

    @Bean
    public MongoTemplate mongoTemplate(MultiTenantMongoDbFactory multiTenantMongoDatabaseFactory) {
        return new MongoTemplate(multiTenantMongoDatabaseFactory);
    }
}

MultiTenantMongoDbFactory 文件(这是用于创建 mongo 模板的工厂,似乎用于确定每次保存的数据库,尽管我尚未测试后台批处理)

public class MultiTenantMongoDbFactory implements MongoDatabaseFactory {

    private final MongoClient mongoClient;
    private final PersistenceExceptionTranslator exceptionTranslator;

    public MultiTenantMongoDbFactory(MongoClient mongoClient) {
        this.mongoClient = mongoClient;
        this.exceptionTranslator = new MongoExceptionTranslator();
    }

    public MultiTenantMongoDbFactory(MongoClient mongoClient, ClientSession session) {
        this.mongoClient = mongoClient;
        this.exceptionTranslator = new MongoExceptionTranslator();
    }

    @Override
    public MongoDatabase getMongoDatabase() throws DataAccessException {
        return mongoClient.getDatabase(TenantContext.getCurrentTenant());
    }

    @Override
    public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
        return mongoClient.getDatabase(dbName);
    }

    @Override
    public PersistenceExceptionTranslator getExceptionTranslator() {
        return exceptionTranslator;
    }

    @Override
    public ClientSession getSession(ClientSessionOptions options) {
        return mongoClient.startSession(options);
    }

    @Override
    public MongoDatabaseFactory withSession(ClientSession session) {
        // Create a new MultiTenantMongoDbFactory instance with the same MongoClient and the provided ClientSession.
        return new MultiTenantMongoDbFactory.ClientSessionBoundMongoDbFactory(session, this);
    }

    static final private class ClientSessionBoundMongoDbFactory implements MongoDatabaseFactory {

        private final ClientSession session;
        private final MongoDatabaseFactory delegate;

        public ClientSessionBoundMongoDbFactory(ClientSession session, MongoDatabaseFactory delegate) {
            this.session = session;
            this.delegate = delegate;
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase()
         */
        @Override
        public MongoDatabase getMongoDatabase() throws DataAccessException {
            return proxyMongoDatabase(delegate.getMongoDatabase());
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#getMongoDatabase(java.lang.String)
         */
        @Override
        public MongoDatabase getMongoDatabase(String dbName) throws DataAccessException {
            return proxyMongoDatabase(delegate.getMongoDatabase(dbName));
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#getExceptionTranslator()
         */
        @Override
        public PersistenceExceptionTranslator getExceptionTranslator() {
            return delegate.getExceptionTranslator();
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#getSession(com.mongodb.ClientSessionOptions)
         */
        @Override
        public ClientSession getSession(ClientSessionOptions options) {
            return delegate.getSession(options);
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#withSession(com.mongodb.session.ClientSession)
         */
        @Override
        public MongoDatabaseFactory withSession(ClientSession session) {
            return delegate.withSession(session);
        }

        /*
         * (non-Javadoc)
         * @see org.springframework.data.mongodb.MongoDbFactory#isTransactionActive()
         */
        @Override
        public boolean isTransactionActive() {
            return session != null && session.hasActiveTransaction();
        }

        private MongoDatabase proxyMongoDatabase(MongoDatabase database) {
            return createProxyInstance(session, database, MongoDatabase.class);
        }

        private MongoDatabase proxyDatabase(com.mongodb.session.ClientSession session, MongoDatabase database) {
            return createProxyInstance(session, database, MongoDatabase.class);
        }

        private MongoCollection<?> proxyCollection(com.mongodb.session.ClientSession session,
                                                   MongoCollection<?> collection) {
            return createProxyInstance(session, collection, MongoCollection.class);
        }

        private <T> T createProxyInstance(com.mongodb.session.ClientSession session, T target, Class<T> targetType) {

            ProxyFactory factory = new ProxyFactory();
            factory.setTarget(target);
            factory.setInterfaces(targetType);
            factory.setOpaque(true);

            factory.addAdvice(new SessionAwareMethodInterceptor<>(session, target, ClientSession.class, MongoDatabase.class,
                    this::proxyDatabase, MongoCollection.class, this::proxyCollection));

            return targetType.cast(factory.getProxy(target.getClass().getClassLoader()));
        }

        public ClientSession getSession() {
            return this.session;
        }

        public MongoDatabaseFactory getDelegate() {
            return this.delegate;
        }

        @Override
        public boolean equals(@Nullable Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            MultiTenantMongoDbFactory.ClientSessionBoundMongoDbFactory that = (MultiTenantMongoDbFactory.ClientSessionBoundMongoDbFactory) o;

            if (!ObjectUtils.nullSafeEquals(this.session, that.session)) {
                return false;
            }
            return ObjectUtils.nullSafeEquals(this.delegate, that.delegate);
        }

        @Override
        public int hashCode() {
            int result = ObjectUtils.nullSafeHashCode(this.session);
            result = 31 * result + ObjectUtils.nullSafeHashCode(this.delegate);
            return result;
        }

        public String toString() {
            return "MongoDatabaseFactorySupport.ClientSessionBoundMongoDbFactory(session=" + this.getSession() + ", delegate="
                    + this.getDelegate() + ")";
        }
    }
}

TenantContext 文件我用来设置 jdbc mariadb 和 mongodb 的租户上下文,因为我两者都有

public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static String getCurrentTenant() {
        if (currentTenant.get() == null){
            currentTenant.set("DEV_TEST");
        }
        return currentTenant.get();
    }

    public static void setCurrentTenant(String tenant) {
        currentTenant.set(tenant);
    }

    public static void clear() {
        currentTenant.remove();
    }
}

如果有人想复制,请在此处添加完整代码

https://github.com/mplein2/spring_data_mongodb_mutlitenancy

© www.soinside.com 2019 - 2024. All rights reserved.