java - datasources - spring boot multiple datasource
cambiar dinĂ¡micamente fuente de datos de primavera (2)
Tengo una aplicación Spring, quiero cambiar la fuente de datos de forma dinámica, es decir. cuando ingresamos una URL de DS, los beans de Spring y todas las dependencias se actualizarán automáticamente. Sé que esto es algo extraño, pero de todos modos quiero lograr eso. La configuración de My Spring es la siguiente:
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.serverName}" />
<property name="portNumber" value="${jdbc.portNumber}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="databaseName" value="${jdbc.databaseName}" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="majorDataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
Las preguntas son:
La URL de JDBC se almacena en propiedades, que podrían modificarse en tiempo de ejecución.
Una vez que se cambia la URL, necesito volver a crear la fuente de datos y tal vez los objetos dependientes. No pude entender cómo hacerlo elegantemente en primavera?
He sabido que Spring did podría enrutar dinámicamente la fuente de datos basada en una clave, pero la fuente de datos URL está predefinida en Spring y no cambiará el tiempo de ejecución. No es mi caso.
No estoy seguro de si esta es la forma correcta de hacerlo, pero lo que puede hacer es algo como esto.
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="dummydata" />
<property name="portNumber" value="dummydata" />
<property name="user" value="dummydata" />
<property name="password" value="dummydata" />
<property name="databaseName" value="dummydata" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
y en Java Class
public class TestTransaction {
@Autowired
private DataSourceTransactionManager manager;
private PlatformTransactionManager transactionManager;
public testExecution(DataSource ds) {
manager.setDataSource(ds);
transactionManager = manager;
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
jdbcTemplate.update();
transactionManager.commit(status);
} catch (Exception ex) {
transactionManager.rollback(status);
}
}
}
Por favor sugiera si este enfoque podría funcionar ya que todavía soy nuevo en Spring
Puede usar AbstractRoutingDataSource de Spring extendiéndolo y reemplazando el método determineCurrentLookupKey()
que debe devolver la clave que hace referencia al bean de primavera del origen de datos que se utilizará.
Eche un vistazo a este artículo de blog en el blog de Spring Source que le mostrará un ejemplo de cómo usar esa característica.
Básicamente para responder a sus preguntas, lo que deberá hacer es definir las dos fuentes de datos como diferentes bean de primavera en su configuración XML. No es necesario crear uno de forma dinámica, Spring cargará ambos y utilizará uno u otro de forma dinámica según sus criterios en el método determineCurrentLookupKey()
.
Esto llevaría a algo así como:
Configuración XML
<!-- first data source -->
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.major.serverName}" />
<property name="portNumber" value="${jdbc.major.portNumber}" />
<property name="user" value="${jdbc.major.username}" />
<property name="password" value="${jdbc.major.password}" />
<property name="databaseName" value="${jdbc.major.databaseName}" />
</bean>
<!-- second data source -->
<bean id="minorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.minor.serverName}" />
<property name="portNumber" value="${jdbc.minor.portNumber}" />
<property name="user" value="${jdbc.minor.username}" />
<property name="password" value="${jdbc.minor.password}" />
<property name="databaseName" value="${jdbc.minor.databaseName}" />
</bean>
<!-- facade data source -->
<bean id="dataSource" class="blog.datasource.CustomerRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="MINOR" value-ref="minorDataSource"/>
<entry key="MAJOR" value-ref="majorDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="majorDataSource"/>
</bean>
<!-- wiring up -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
Java
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// get the current url
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if (request.getRequestURL().toString().endsWith("/minor"))
return "MINOR";
else
return "MAJOR";
}
}