Using the Abstractroutingdatasource implementation provided by spring
Create the Abstractroutingdatasource implementation class, which is responsible for saving all data sources and switching data source policies:
public class Dynamicdatasource extends Abstractroutingdatasource {
@Override
Protected Object Determinecurrentlookupkey () {
Return Datasourcecontextholder.getdatasourcekey ( );
}
}
XML configuration data source and transaction:
<?xml version= "1.0" encoding= "UTF-8"?
<beans xmlns= "/http Www.springframework.org/schema/beans "
xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "
xmlns:aop=" HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP "
xmlns:tx=" Http://www.springframework.org/schema/tx "
xsi: schemalocation= "
Http://www.springframework.org/schema/beans
Http://www.springframework.org/schema/beans /spring-beans.xsd
HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP
http://www.springframework.org/schema/aop/ Spring-aop.xsd
Http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/ Spring-tx.xsd ";
<bean id= "Dynamicdatasource" class= "Spring.dbs.DynamicDataSource" >
<property name= "Defaulttargetdatasource" ref= "Primarydatasource"/>
<property name= "Targetdatasources" >
<map>
<entry key= "Primarydatasource" value-ref= "Primarydatasource" ></entry>
<entry key= "Secondarydatasource" value-ref= "Secondarydatasource" ></entry>
<entry key= "Thirdlydatasource" value-ref= "Thirdlydatasource" ></entry>
</map>
</property>
</bean>
<bean id= "TransactionManager" class= "Org.springframework.jdbc.datasource.DataSourceTransactionManager" >
<property name= "DataSource" ref= "Dynamicdatasource"/>
</bean>
<tx:advice id= "Txadvice" transaction-manager= "TransactionManager" >
<tx:attributes>
<tx:method name= "batch*" propagation= "REQUIRED"/>
<tx:method name= "add*" propagation= "REQUIRED"/>
<tx:method name= "insert*" propagation= "REQUIRED"/>
<tx:method name= "persist*" propagation= "REQUIRED"/>
<tx:method name= "delete*" propagation= "REQUIRED"/>
<tx:method name= "remove*" propagation= "REQUIRED"/>
<tx:method name= "merge*" propagation= "REQUIRED"/>
<tx:method name= "update*" propagation= "REQUIRED"/>
<tx:method name= "select*" read-only= "true" propagation= "SUPPORTS"/>
<tx:method name= "query*" read-only= "true" propagation= "SUPPORTS"/>
<tx:method name= "get*" read-only= "true" propagation= "SUPPORTS"/>
<tx:method name= "load*" read-only= "true" propagation= "SUPPORTS"/>
<tx:method name= "find*" read-only= "true" propagation= "SUPPORTS"/>
<tx:method name= "*" read-only= "true" propagation= "SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id= "txpointcut" expression= "Execution (* spring.service). *(..))" />
<aop:advisor pointcut-ref= "Txpointcut" advice-ref= "Txadvice"/>
</aop:config>
</beans>
Look at the Datasourcecontextholder implementation, which is responsible for handling key switching data source policies, using threadlocal to ensure thread isolation:
public class Datasourcecontextholder {
private static final threadlocal<string> Datasource_key = new threadlocal<> ();
/**
* Set Datasourcekey
*
* @param datasourcekey
*/
public static void Setdatasourcekey (String datasourcekey) {
Datasource_key.set (Datasourcekey);
}
/**
* Get Datasourcekey
*
* @return
*/
public static String Getdatasourcekey () {
return Datasource_key.get ();
}
/**
* Delete Datasourcekey
*/
public static void Cleardatasourcekey () {
Datasource_key.remove ();
}
}
Implement policies using AOP to dynamically switch data sources:
@Component
@Aspect
public class Datasourcehandler {
private static final Logger Logger = Loggerfactory.getlogger (Datasourcehandler.class);
private static final string[] datasources = new string[]{"Primarydatasource", "Secondarydatasource", "Thirdlydatasource "};
① @Pointcut ("Execution (* spring.service). *(..))")
public void Datasourcepoint () {
}
@Around ("Datasourcepoint ()")
Public Object handler (Proceedingjoinpoint point) throws Throwable {
Logger.info ("Begin get DataSource ...");
object[] args = Point.getargs ();
if (null! = args && args.length > 0) {
②string key = String.valueof (Args[0]);
//Deposit key
③dataso Urcecontextholder.setdatasourcekey (GetKey (key));
}
Object result = Point.proceed ();
//Clear Key
④datasourcecontextholder.cleardatasourcekey ();
Logger.info ("End Get DataSource ...");
return result;
}
/**
* Get the Key method
*
* @param key
* @return
*/
Private String GetKey (String key) {
Logger.info ( "This key is: [" + Key + "]!");
CRC32 CRC = New CRC32 ();
Crc.update (Key.getbytes (standardcharsets.utf_8));
Long result = Crc.getvalue ();
Logger.info ("This key ' s CRC32 is: [" + result + "]!");
int index = (int) (result% 3);
Logger.info ("This key ' s module is: [" + Index + "]!");
int len = datasources.length;
if (Index > (len-1)) {
index = len-1;
}
Logger.info ("This key ' s result module is: [" + Index + "]!");
String datasourcekey = Datasources[index];
Logger.info ("This DataSource key is: [" + Datasourcekey + "]!");
return datasourcekey;
}
}
① the method execution policy under all service packages,
②demo uses the first parameter as key by default,
③ uses CRC32 modulo to get the data source key, Save to DataSourceContextHolder.ThreadLocal.DATASOURCE_KEY to ensure that different thread policies are correct;
④ the business logic after processing, clear KEY;
Using JdbcTemplate, configure the following:
@Configuration
public class Jdbctemplatebean {
@Autowired
@Qualifier ("Dynamicdatasource")
Private DataSource Dynamicdatasource;
@Bean (name = "Dynamicjdbctemplate")
@Qualifier ("Dynamicjdbctemplate")
Public Namedparameterjdbctemplate primaryjdbctemplate () {
return new Namedparameterjdbctemplate (Dynamicdatasource);
}
}
A problem was found during use, when the transaction was configured to Propagation= "REQUIRED", because the AOP order problem resulted in the discovery of a data source to open a transaction, followed by a data source switching strategy, using <aop:advisor order= "2"/> The property is configured with a transaction order of 2 and uses the Org.springframework.core.Ordered interface to set the policy execution order to 1, ensuring that policy execution is preceded by the search for a data source, as follows:
Transaction plane configuration:
<aop:config>
<aop:pointcut id= "txpointcut" expression= "Execution (* spring.service). *(..))" />
<aop:advisor pointcut-ref= "Txpointcut" advice-ref= "Txadvice" order= "2"/>
</aop:config>
Policy execution Facets:
@Component
@Aspect
public class Datasourcehandler implements Ordered {
@Pointcut ("Execution (* spring.service). *(..))")
public void Datasourcepoint () {
}
@Around ("Datasourcepoint ()")
Public Object Handler (Proceedingjoinpoint point) throws Throwable {
Policy implementation
}
@Override
public int GetOrder () {
return 1;
}
}
Spring Abstractroutingdatasource enables Dynamic Data source switching