Java Spring Data MongoDB在部署期间更改数据库(多租户)

sqserrrh  于 2023-09-29  发布在  Spring
关注(0)|答案(1)|浏览(95)

我希望在多个数据库中具有相同的集合结构,如下所示

Database A:
 CollectionA
 CollectionB

Database B:
 CollectionA
 CollectionB

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

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

我怎样才能做一个配置来强制spring在操作之间交换数据库的repository beans?我已经搜索过了,我发现我需要创建一个mongoTemplate bean,它似乎没有工作,这个方法只在仓库自动连接时调用一次,但在插入的地方不调用。
Eg.

# 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

查看了Multi-tenant mongodb database based with spring data及其旧版本的spring data mongodb im using 3.1.3
如果任何人可以建议我如何调试在插入文档期间使用的bean,那么欢迎我自己弄清楚。不知道我将如何手动弄清楚。Thanks in advance

z4bn682m

z4bn682m1#

将通过一起放弃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
编辑:在进行更改之后,生成的存储库也是多租户的。不需要直接使用mongo模板

相关问题