假设条件
由于我尚无信誉在您的问题下发表评论,因此我的回答基于以下假设:
可通过Spring JSR-330提供程序(如)访问用于当前用户的当前模式名称
private javax.inject.Provider<User> user; String schema = user.get().getSchema();
。理想情况下,这是基于ThreadLocal的代理。要构建以
DataSource
您需要的方式完全配置的,需要相同的属性。每次。唯一不同的是架构名称。(也很容易获得其他不同的参数,但这对于这个答案来说太过分了)每个模式都已经使用所需的DDL进行了设置,因此无需hibernate即可创建表或其他内容
除了名称之外,每个数据库架构看起来都完全相同
每当相应的用户向您的应用程序发出请求时,您都需要重用DataSource。但是,您不希望将每个用户的每个数据源都永久存储在内存中。
我的解决方案
结合使用ThreadLocal代理获得模式名称和一个Singleton-
DataSource,它在每个用户请求上的行为都不同。该解决方案的灵感来自您对
AbstractRoutingDataSourceMeherzad的评论和自己的经验的提示。
动态的 DataSource
我建议促进
AbstractDataSourceSpring的发展,并像实施它一样
AbstractRoutingDataSource。代替静态
Map方法,我们使用番石榴缓存来获得易于使用的缓存。
public class UserSchemaAwareRoutingDataSource extends AbstractDataSource { private @Inject javax.inject.Provider<User> user; private @Inject Environment env; private LoadingCache<String, DataSource> dataSources = createCache(); @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } @Override public Connection getConnection(String username, String password) throws SQLException { return determineTargetDataSource().getConnection(username, password); } private DataSource determineTargetDataSource() { String schema = user.get().getSchema(); return dataSources.get(schema); } private LoadingCache<String, DataSource> createCache() { return CacheBuilder.newBuilder().maximumSize(100).expireAfterWrite(10, TimeUnit.MINUTES).build( new CacheLoader<String, DataSource>() { public DataSource load(String key) throws AnyException { return buildDataSourceForSchema(key); } }); } private DataSource buildDataSourceForSchema(String schema) { // e.g. of property: "jdbc:postgresql://localhost:5432/mydatabase?currentSchema=" String url = env.getRequiredProperty("spring.datasource.url") + schema; return DataSourceBuilder.create() .driverClassName(env.getRequiredProperty("spring.datasource.driverClassName")) [...] .url(url) .build(); }}现在,您有了一个“数据源”,它对每个用户的作用都不同。创建数据源后,它将被缓存10分钟。而已。
使应用程序知道我们的动态数据源
集成我们新创建的DataSource的地方是Spring上下文已知的DataSource单例,并在所有bean中使用,例如EntityManagerFactory
因此,我们需要一个等效的方法:
@Primary@Bean(name = "dataSource")@ConfigurationProperties(prefix="spring.datasource")public DataSource dataSource() { return DataSourceBuilder.create().build();}但它必须比基于DataSourceBuilder的普通属性更具动态性:
@Primary@Bean(name = "dataSource")public UserSchemaAwareRoutingDataSource dataSource() { return new UserSchemaAwareRoutingDataSource();}结论
我们有一个透明的动态数据源,它每次都使用正确的数据源。
公开问题
- 没有用户登录时该怎么办?是否没有数据库访问权限?
- 谁制定计划?
免责声明
我尚未测试此代码!
编辑: 要
Provider<CustomUserDetails>使用Spring
实现,您需要将此定义为原型。您可以利用Spring对JSR-330和Spring Securitys SecurityContextHolder的支持:
@Bean @Scope("prototype")public CustomUserDetails customUserDetails() { return return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();}您不再需要
RequestInterceptor,
UserProvider或控制器代码来更新用户。
这有帮助吗?
EDIT2
仅作记录:不要
CustomUserDetails直接引用bean。由于这是一个原型,因此Spring将尝试为该类创建代理,
CustomUserDetails在我们的情况下,这不是一个好主意。因此,只需使用
Provider即可访问此bean。或使其成为接口。



