Spring data neo4j在运行时改变数据库上下文。

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

我真的很难使用 spring-data-neo4j 在Neo4j 4.0.0版本中,可以在数据库之间进行切换,而我需要这样做来实现一个多租户系统,每个租户都有自己的数据库。

然而,我需要实现多租户系统,每个租户都有自己的数据库。spring-data-neo4j 似乎没有提供任何方法在运行时用最新版本切换数据库(这也必须是线程安全的)。

我想的是在运行时创建一个单一的 sessionFactory 每位承租人

    val sessionFactory = createTenantSessionFactory()
    val session = sessionFactory.openSession()
    val factory = Neo4jRepositoryFactory(session, mappingContext)
    val repository = factory.getRepository(DesiredNeo4jRepository::class.java)

但是,我在使用这个方案时,遇到了并发性的问题,即使在一个线程中一个接一个地执行请求,但数据库中的结果却不对。

我想,这可能是因为我在这个方案中没有使用spring提供的事务管理支持,因为 Neo4jTransactionManager 是没有创造。

我意识到这是一个非常复杂的问题,但我是否针对这个问题采取了一个好的方法,还是有更好的替代方案我没有看到?

如果没有更好的方法,我怎么能用这个方案支持事务管理呢?

谢谢您的帮助!

neo4j spring-data-neo4j neo4j-ogm
1个回答
0
投票

我终于知道如何解决这个问题,目前使用了来自 https:/community.neo4j.comtspring-data-neo4j-4-0-release-multi-tenant12920。

所以我实现了一个 TenantContext

object ApplicationContext {

    private val currentTenant = ThreadLocal<String>()

    fun setCurrentTenant(tenantName: String) {
        currentTenant.set(tenantName)
    }

    fun getCurrentTenant(): String? {
        return currentTenant.get()
    }

    // Just a utility function
    fun withTenantContext(tenant: String, handler: (() -> Unit)) {
        val currentTenant = getCurrentTenant()
        setCurrentTenant(tenant)
        handler()

        if (currentTenant != null) {
            setCurrentTenant(currentTenant)
        } else {
            clearTenant()
        }
    }

    fun clearTenant() {
        currentTenant.remove()
    }
}

并覆盖会话工厂。不幸的是, SessionFactory 没有暴露接口,所以我不得不重写它,并传递一个假的包和一个空的驱动程序(只要实现了 org.neo4j.ogm.driver.Driver 类,并将其留空)。)

这与其说是一个真正的解决方案,不如说是一个热补丁,同时也是在等待 spring-data-neo4j但它在多线程上下文中工作,适合我的需求。

class MultiTenantSessionFactory(private val sessionFactories: ConcurrentMap<String, SessionFactory>) : SessionFactory(EmptyDriver(), "fake.package") {


    override fun openSession(): Session {
        return getSessionFactory(getCurrentTenantOrThrow()).openSession()
    }

    override fun close() {
        getSessionFactory(getCurrentTenantOrThrow()).close()
    }

    private fun getSessionFactory(tenantName: String): SessionFactory {
        return sessionFactories[tenantName] ?: throw UnregisteredSessionFactoryException(tenantName)
    }

    private fun getCurrentTenantOrThrow(): String {
        return ApplicationContext.getCurrentTenant() ?: throw EmptyTenantContextException()
    }
}

最后,只要为这个会话工厂创建一个Bean,同时使用 @EnableNeo4jRepositories

    @Bean
    fun sessionFactory(tenantRepository: TenantRepository): SessionFactory {
        return MultiTenantSessionFactory(
                tenantRepository.findAll()
                        .map { tenant -> tenant.name to initTenantSessionFactory(tenant) }
                        .toMap(ConcurrentHashMap())
        )
    }

你现在应该可以使用你所有的 Neo4jRepository 在多租户的情况下透明地。希望对大家有所帮助!

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