我在使用Spring,Spring Data JPA,Spring Security,Primefaces ......的项目中工作。
我正在关注使用spring的动态数据源路由this tutorial。
在本教程中,您只能在预定义的数据源之间切换动态数据源。
以下是我的代码片段:
springContext-jpa.xml
<bean id="dsCgWeb1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName.Cargest_web}"></property>
<property name="url" value="${jdbc.url.Cargest_web}"></property>
<property name="username" value="${jdbc.username.Cargest_web}"></property>
<property name="password" value="${jdbc.password.Cargest_web}"></property>
</bean>
<bean id="dsCgWeb2" class="org.apache.commons.dbcp.BasicDataSource">
// same properties, different values ..
</bean>
<!-- Generic Datasource [Default : dsCargestWeb1] -->
<bean id="dsCgWeb" class="com.cargest.custom.CargestRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="1" value-ref="dsCgWeb1" />
<entry key="2" value-ref="dsCgWeb2" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dsCgWeb1" />
</bean>
我想要做的是让 targetDataSources 地图与其元素动态相同。
换句话说,我想获取某个数据库表,使用该表中存储的属性来创建我的数据源,然后将它们放在像 targetDataSources 这样的地图中。
有办法做到这一点吗?
答案 0 :(得分:9)
AbstractRoutingDataSource
中没有任何内容强制您使用DataSourceS
的静态地图。您可以构造一个实现Map<Object, Object>
的bean,其中key用于选择DataSource
,值为DataSource
或(默认情况下)引用JNDI的String定义的数据源。你甚至可以动态修改它,因为地图存储在内存中,AbstractRoutingDataSource
没有缓存。
我没有完整的示例代码。但这是我能想象的。在Web应用程序中,每个客户端都有一个数据库,所有数据库都具有相同的结构 - 好吧,这将是一个奇怪的设计,比如它只是用于示例。在登录时,应用程序为客户端创建数据源并将其存储在由sessionId索引的映射中 - 映射是根上下文中的bean dataSources
@Autowired
@Qualifier("dataSources");
Map<String, DataSource> sources;
// I assume url, user and password have been found from connected user
// I use DriverManagerDataSource for the example because it is simple to setup
DataSource dataSource = new DriverManagerDataSource(url, user, password);
sources.put(request.getSession.getId(), dataSource);
您还需要一个会话侦听器来清除其dataSources
方法中的destroy
@Autowired
@Qualifier("dataSources");
Map<String, DataSource> sources;
public void sessionDestroyed(HttpSessionEvent se) {
// eventually cleanup the DataSource if appropriate (nothing to do for DriverManagerDataSource ...)
sources.remove(se.getSession.getId());
}
路由数据源可以是:
public class SessionRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
return request.getSession().getId();
}
@Autowired
@Qualifier("dataSources")
public void setDataSources(Map<String, DataSource> dataSources) {
setTargetDataSources(dataSources);
}
我没有测试过任何东西,因为设置不同的数据库需要做很多工作,但我觉得应该没问题。在现实世界中,每个会话不会有不同的数据源,而是每个用户一个具有每个用户的会话数,但正如我所说,它是一个过于简化的示例。
答案 1 :(得分:0)
线程使用的数据源可能会不时更改。
应注意并发性,应用程序在并发环境中可能会出现并发问题。
thread-bound AbstractRoutingDataSource sample
答案 2 :(得分:0)
可以通过 AbstractRoutingDataSource 实现,并将信息保存在线程局部变量中。这是一个漂亮的工作示例,您可以参考: Multi-tenancy: Managing multiple datasources with Spring Data JPA