我成功地通过关注http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/文章来实现动态更改数据库连接。
但现在问题是,我在配置文件中有一个由遗留应用程序管理的数据库URL列表。
有没有办法从值列表(即Year2011DataSource,Year2012DataSource,...)在Spring上下文中创建bean,并使用刚刚创建的bean填充dataSource bean的map?
<!-- Property file located in the legacy application's folder -->
<context:property-placeholder location="file:///D:/config.properties" />
<!-- Shared data source properties are read from the config.properties file -->
<bean id="parentDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" abstract="true">
<property name="driverClassName" value="${db.driver}" />
<property name="username" value="${db.user}" />
<property name="password" value="${db.password}" />
</bean>
<!-- Database urls by year -->
<bean id="Year2012DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2012" />
</bean>
<bean id="Year2011DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2011" />
</bean>
<bean id="Year2010DataSource" parent="parentDataSource">
<property name="url" value="jdbc:sqlserver://localhost;databaseName=DbName_v570_2010" />
</bean>
<!-- ... and so on, these should instead be populated dynamically ... -->
<!-- DbConnectionRoutingDataSource extends AbstractRoutingDataSource -->
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
<property name="targetDataSources">
<map key-type="int">
<entry key="2011" value-ref="Year2011DataSource" />
<entry key="2010" value-ref="Year2010DataSource" />
<!-- ... and so on, these also should instead be populated dynamically ... -->
</map>
</property>
<property name="defaultTargetDataSource" ref="Year2012DataSource" />
</bean>
答案 0 :(得分:8)
非常适合这个要求我认为是自定义BeanFactoryPostProcessor - 在遗留配置中读取并在自定义bean工厂后处理器中生成数据源:
class MyDatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//Read in details from legacy properties.. build custom bean definitions and register with bean factory
//for each legacy property...
BeanDefinitionBuilder datasourceDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(BasicDataSource.class).addPropertyValue("url", "jdbc..");
beanFactory.registerBeanDefinition(datasourceDefinitionBuilder.getBeanDefinition());
}
}
答案 1 :(得分:1)
我可以告诉你注释方法。我会在属性文件中添加URL和配置,并执行以下操作:
@Bean(name="dataSourceMap")
public Map<String, DataSource> dataSourceMap(DataSource dataSource2011, DataSource dataSource2012) {
// read properties from properties file and create map of datasource
Map<DataSource> map = new HashMap<>();
map.put("dataSource2011",dataSource2011);
map.put("dataSource2012",dataSource2012);
//map.put("dataSource2013",dataSource2013);
return map;
}
@Bean(name="dataSource2011",destroyMethod="close")
public DataSource dataSource() {
// read properties from properties file and create map of
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url2011);
dataSource.setUsername(username2011);
dataSource.setPassword(password2011);
return dataSource;
}
@Bean(name="dataSource2012",destroyMethod="close")
public DataSource dataSource() {
// read properties from properties file and create map of
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url2012);
dataSource.setUsername(username2012);
dataSource.setPassword(password2012);
return dataSource;
}
答案 2 :(得分:1)
据我所知,没有使用XML配置的开箱即用解决方案。但是,this answer在Spring中使用FactoryBean
抽象描述了实现此目的的简单解决方案。
答案 3 :(得分:1)
============================================
通过遵循Biju的提示,我得到的一切都是这样的:
============================================
弹出配置中的“数据库url by year”部分已不复存在,bean在BeanFactoryPostProcessor中创建。“dataSource”bean将其属性设置为在BeanFactoryPostProcessor中替换的虚拟数据:
<bean id="dataSource" class="someProject.DbConnectionRoutingDataSource">
<property name="targetDataSources">
<map key-type="String">
<!-- Will be filled from the DatasourceRegisteringBeanFactoryPostProcessor -->
</map>
</property>
<property name="defaultTargetDataSource" value="java:jboss/datasources/ExampleDS" />
</bean>
这是BeanFactoryPostProcessor实现:
@Component
class DatasourceRegisteringBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
InitialContext ic = new InitialContext();
// read the list of available JNDI connections
NamingEnumeration<?> list = ic.listBindings(getJndiDSRoot());
HashSet<String> jndiDataSources = new HashSet<String>();
while (list.hasMore()) {
/*... (ommitted for simplicity) ...*/
connectionsList.put(key, value);
}
BeanDefinitionRegistry factory = (BeanDefinitionRegistry) beanFactory;
BeanDefinitionBuilder datasourceDefinitionBuilder;
// Create new beans
for (Entry<Integer, String> e : connectionsList.entrySet()) {
datasourceDefinitionBuilder = BeanDefinitionBuilder
.childBeanDefinition("parentDataSource")
.addPropertyValue("url", e.getValue());
factory.registerBeanDefinition("Year" + e.getKey() + "DataSource",
datasourceDefinitionBuilder.getBeanDefinition());
}
// Configure the dataSource bean properties
MutablePropertyValues mpv = factory.getBeanDefinition("dataSource").getPropertyValues();
// Here you can set the default dataSource
mpv.removePropertyValue("defaultTargetDataSource");
mpv.addPropertyValue("defaultTargetDataSource",
new RuntimeBeanReference("Year" + defaultYear + "DataSource"));
// Set the targetDataSource properties map with the list of connections
ManagedMap<Integer, RuntimeBeanReference> mm = (ManagedMap<Integer, RuntimeBeanReference>) mpv.getPropertyValue("targetDataSources").getValue();
mm.clear();
// Fill the map with bean references to the newly created beans
for (Entry<Integer, String> e : connectionsList.entrySet()) {
mm.put(e.getKey(), new RuntimeBeanReference("Year" + e.getKey() + "DataSource")));
}
}
}