2012-08-23 12 views
8

ja z powodzeniem udało się wdrożyć dynamiczną zmianę połączeń z bazą danych, wykonując http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/ artykuł.dynamicznie tworzyć fasoli wiosenne i zmieniać właściwości istniejących ziaren

Ale teraz jest problem, mam listę adresów URL bazy danych w pliku konfiguracyjnym, które są zarządzane przez starszych aplikacji.

Czy istnieje sposób na tworzenie fasoli w tym kontekście Spring z listy wartości (np. Year2011DataSource, Year2012DataSource, ...) i zapełnić mapę fasoli źródła danych tymi fasolami, które właśnie zostały utworzone?

<!-- 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

Dodany ostateczna wersja kodu. Dzięki za pomoc ludziom. – Vedran

Odpowiedz

8

odpowiednie dla tego wymogu myślę jest zwyczaj BeanFactoryPostProcessor - czytamy w konfiguracji starszych i generowania źródeł danych w niestandardowych fasoli fabryka postu Procesor:

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()); 
    } 
} 
+0

Great! Teraz po prostu trzeba zrozumieć, jak zapełnić „targetDataSource” własność „na istniejącym źródłem danych” fasoli z odniesieniami do ziarna, które dostałem utworzonych w ten sposób. – Vedran

+0

Got wszystko działa, i położy gotowego przykład w oryginalne pytanie, kiedy trochę posprzątam. Jeszcze raz dziękuję! :) – Vedran

1

Mogę powiedzieć Ci podejście do adnotacji. Dodam, adresy URL i konfiguracji w pliku właściwości i zrobić coś jak następuje:

@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; 
} 
+0

Czy są to mapy java.util.Map na mapie? Ponieważ definiujesz/używasz tylko elementu, a nie dwóch (klucz, wartość) ... – helios

+0

Tak, edytowana odpowiedź. Właściwie Wpisałem kod w notatniku ++ :) –

+0

@helios: dzięki :) –

1

O ile mi wiadomo, nie ma out-of-the-box rozwiązanie wykorzystujące konfiguracyjnym XML. Jednak proste rozwiązanie do osiągnięcia tego jest opisane w this answer przy użyciu abstrakcji na wiosnę.

1

========= ===================================

Stosując końcówkę Biju za mam wszystko działa jak to:

============================================

Sekcja "Adresy bazy danych według roku" w konfiguracji wiosennej już nie jest, fasola jest tworzona w BeanFactoryPostProcessor.

"DataSource" bean ma swoje właściwości ustawione na manekina danych, które są wymieniane w 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> 

I to jest realizacja 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"))); 
     } 
    } 
} 
Powiązane problemy