chr*_*con 27 java spring hibernate jpa spring-mvc
我已经阅读了许多关于动态数据源路由的问题和答案,并使用了AbstractRoutingDataSource另一个解决方案(见下文).这很好,但需要所有数据源的硬编码属性.随着使用该应用程序的用户数量的增加,这不再是一种合适的路由选择方式.每次新用户注册时,还需要在属性中添加一个条目.情况如下
我和... spring boot 1.4.0一起使用hibernate 5.1spring data jpa
我无法找到一种完全动态更改架构的方法.有人知道春天怎么做吗?
编辑:
感谢@Johannes Leimer的回答,我得到了一个有效的实现.
这是代码:
用户提供者:
@Component
public class UserDetailsProvider {
@Bean
@Scope("prototype")
public CustomUserDetails customUserDetails() {
return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
Run Code Online (Sandbox Code Playgroud)
UserSchemaAwareRoutingDatasource:
public class UserSchemaAwareRoutingDataSource extends AbstractDataSource {
@Inject
Provider<CustomUserDetails> customUserDetails;
@Inject
Environment env;
private LoadingCache<String, DataSource> dataSources = createCache();
@Override
public Connection getConnection() throws SQLException {
try {
return determineTargetDataSource().getConnection();
} catch (ExecutionException e){
e.printStackTrace();
return null;
}
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
System.out.println("getConnection" + username);
System.out.println("getConnection2" + password);
try {
return determineTargetDataSource().getConnection(username, password);
} catch (ExecutionException e) {
e.printStackTrace();
return null;
}
}
private DataSource determineTargetDataSource() throws SQLException, ExecutionException {
try {
String schema = customUserDetails.get().getUserDatabase();
return dataSources.get(schema);
} catch (NullPointerException e) {
e.printStackTrace();
return dataSources.get("fooooo");
}
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*mer 23
因为我还没有在你的问题下发表评论的声誉,我的答案基于以下几点:
用于当前用户的当前模式名称可通过Spring JSR-330 Provider访问private javax.inject.Provider<User> user; String schema = user.get().getSchema();.理想情况下,这是一个基于ThreadLocal的代理.
要构建一个DataSource以您需要的方式完全配置的,需要相同的属性.每次.唯一不同的是模式名称.(很容易获得其他不同的参数,但这对于这个答案来说太过分了)
每个模式都已使用所需的DDL进行设置,因此不需要hibernate来创建表或其他东西
除了名称之外,每个数据库模式看起来完全相同
每次相应的用户向您的应用程序发出请求时,您都需要重用DataSource.但是您不希望将每个用户的每个DataSource永久保存在内存中.
使用ThreadLocal代理的组合来获取模式名称和Singleton-DataSource,它在每个用户请求中的行为都不同.这个解决方案的灵感来自于您对AbstractRoutingDataSourceMeherzad的评论和自己的经验的暗示.
DataSource我建议为AbstractDataSourceSpring 提供便利并实现它AbstractRoutingDataSource.Map我们使用Guava Cache来获得易于使用的缓存,而不是类似静态的方法.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在你有一个`DataSource',它对每个用户都有不同的作用.一旦创建了DataSource,它将被缓存10分钟.而已.
集成我们新创建的DataSource的地方是Spring上下文已知的DataSource单例,并在所有bean中使用,例如EntityManagerFactory
所以我们需要一个等价物:
@Primary
@Bean(name = "dataSource")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
Run Code Online (Sandbox Code Playgroud)
但它必须比基于普通属性的DataSourceBuilder更具动态性:
@Primary
@Bean(name = "dataSource")
public UserSchemaAwareRoutingDataSource dataSource() {
return new UserSchemaAwareRoutingDataSource();
}
Run Code Online (Sandbox Code Playgroud)
我们有一个透明的动态DataSource,每次都使用正确的DataSource.
我还没有测试过这段代码!
编辑:
要Provider<CustomUserDetails>使用Spring 实现,您需要将其定义为原型.您可以使用JSR-330和Spring Securitys SecurityContextHolder的Springs支持:
@Bean @Scope("prototype")
public CustomUserDetails customUserDetails() {
return return (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
Run Code Online (Sandbox Code Playgroud)
您不再需要a RequestInterceptor,UserProvider或控制器代码来更新用户.
这有帮助吗?
EDIT2
仅供记录:不要CustomUserDetails直接引用bean.由于这是一个原型,Spring将尝试为该类创建一个代理CustomUserDetails,这在我们的案例中不是一个好主意.所以只需使用Providers来访问这个bean.或者使它成为一个界面.
| 归档时间: |
|
| 查看次数: |
18188 次 |
| 最近记录: |