Spring 数据 neo4j 在运行时更改数据库上下文



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

但是,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

我意识到这是一个非常复杂的问题,但我是否采取了一个好的方法来解决这个问题,或者是否有我没有看到的更好的选择?

如果没有更好的方法,我如何使用此解决方案支持事务管理?

感谢您的帮助!

我终于想出了如何使用 https://community.neo4j.com/t/spring-data-neo4j-4-0-release-multi-tenant/12920 的解决方案来解决它

所以我实施了一个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()
}
}

最后,只需在使用 Be3 时为此会话工厂创建一个 bean@EnableNeo4jRepositories

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

现在,您应该能够在多租户上下文中透明地使用所有Neo4jRepository。希望这对某人有所帮助!

最新更新