[Spring] uses Spring's AbstractRoutingDataSource to implement multi-data source switching. spring dynamically switches data sources.
Recently, because the project needs to synchronize data between two projects, specifically, Project 1 data is synchronized to project 2 through the message queue, because this update operation also involves updating data in multiple databases, you need to switch between multiple data sources. The following describes how to switch data sources in Spring. Here, the AbstractRoutingDataSource class is used to complete specific operations. The AbstractRoutingDataSource is added after Spring2.0.
The function of data source switching is to customize a class extension AbstractRoutingDataSource abstract class. In fact, this is equivalent to the routing intermediary of the data source DataSourcer, which can be switched to the corresponding data source DataSource according to the corresponding key value during project running. Let's take a look at the source code of javasactroutingdatasource:
Public abstract class implements actroutingdatasource extends AbstractDataSource implements InitializingBean {/* only lists some codes */private Map <Object, Object> targetDataSources; private Object defaultTargetDataSource; private boolean lenientFallback = true; private DataSourceLookup dataSourceLookup = new CES (); private Map <Object, DataSource> resolvedDataSources; private DataSource resolveddefadatasource DataSource; @ Override public Connection getConnection () throws SQLException {return determineTargetDataSource (). getConnection () ;}@ Override public Connection getConnection (String username, String password) throws SQLException {return determineTargetDataSource (). getConnection (username, password);} protected DataSource determineTargetDataSource () {Assert. notNull (this. resolvedDataSources, "DataSource router not initialized"); Object lookupKey = determineCurrentLookupKey (); DataSource dataSource = this. resolvedDataSources. get (lookupKey); if (dataSource = null & (this. lenientFallback | lookupKey = null) {dataSource = this. resolveddefadatasource dataSource;} if (DataSource = null) {throw new IllegalStateException ("Cannot determine target dataSource for lookup key [" + lookupKey + "]");} return dataSource ;} protected abstract Object determineCurrentLookupKey ();}
From the source code, we can see that AbstractRoutingDataSource inherits AbstractDataSource and implements InitializingBean. The getConnection () method of AbstractRoutingDataSource calls the determineTargetDataSource () method code, the determineCurrentLookupKey () method is used. It is an abstract method of the AbstractRoutingDataSource class and a method to be extended for data source switching. the return value of this method is the key value of the DataSource used in the project, after obtaining the key, you can retrieve the corresponding DataSource from the resolvedDataSource. If the key cannot find the corresponding DataSource, the default data source is used.
The determineCurrentLookupKey () method must be rewritten to implement the data source switching function when the custom class extends the AbstractRoutingDataSource class. The implementation of the custom extended AbstractRoutingDataSource class is as follows:
/*** Obtain the data source */public class MultipleDataSource extends actroutingdatasource {@ Override protected Object determineCurrentLookupKey () {return DynamicDataSourceHolder. getRouteKey ();}}
DynamicDataSourceHolder class:
/*** Data source operation class */public class DynamicDataSourceHolder {private static ThreadLocal <String> routeKey = new ThreadLocal <String> (); /*** get the key of the current thread's data source route */public static String getRouteKey () {String key = routeKey. get (); return key;}/*** the key bound to the current thread data source route * must be called to delete the removeRouteKey () method */public static void setRouteKey (String key) {routeKey. set (key);}/*** Delete the key */public static void removeRouteKey () {routeKey. remove ();}}
Configure multiple data sources in the xml file as follows:
<! -- Data source --> <bean id = "performance1" class = "org. apache. commons. dbcp. basicDataSource "> <property name =" driverClassName "value =" net. sourceforge. jtds. jdbc. driver "> </property> <property name =" url "value =" jdbc: jtds: sqlserver: // 127.0.0.1; databaseName = test "> </property> <property name =" username "value =" *** "> </property> <property name =" password "value = "** * "> </property> </bean> <bean id =" cece2 "class =" or G. apache. commons. dbcp. basicDataSource "> <property name =" driverClassName "value =" net. sourceforge. jtds. jdbc. driver "> </property> <property name =" url "value =" jdbc: jtds: sqlserver: // 127.0.0.2: 1433; databaseName = test "> </property> <property name =" username "value =" *** "> </property> <property name =" password "value = "** * "> </property> </bean> <! -- Configure multi-data source ing --> <bean id = "multipleDataSource" class = "MultipleDataSource"> <property name = "targetDataSources"> <map key-type = "java. lang. string "> <entry value-ref =" performance1 "key =" performance1 "> </entry> <entry value-ref =" performance2 "key =" performance2 "> </entry> </map> </property> <! -- Default data source --> <property name = "defaultTargetDataSource" ref = "performance1"> </property> </bean>
Here, the basic configuration is complete. Next, you only need to call the method where the data source needs to be switched. Generally, the method is switched before the dao layer operates the database, you only need to add the following code before database operations:
DynamicDataSourceHolder.setRouteKey("dataSource2");
The preceding section describes how to manually add the code to switch the data source when you need to switch the data source at the dao layer. You can also use the AOP method to set the configured data source types as annotation labels, in the dao layer, you need to write annotation labels on the methods or classes for switching data source operations, so that the implementation is more operable.
@DataSourceKey("dataSource1")public interface TestEntityMapper extends MSSQLMapper<TestEntity> { public void insertTest(TestEntity testEntity);}
The performancekey annotation code is as follows:
@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface DataSourceKey { String value() default "";}
After the annotation is configured, you need to write a class to implement data source switching, as shown below:
Public class MultipleDataSourceExchange {/*** intercept the target method, obtain the ID of the data source specified by @ DataSource, and set it to the thread store to switch the data source */public void beforeDaoMethod (JoinPoint point) throws Exception {Class <?> Target = point. getTarget (). getClass (); MethodSignature signature = (MethodSignature) point. getSignature (); // the annotation of the target type is used by default. If not, the annotation Class for (Class <?> Cls: target. getInterfaces () {resetDataSource (cls, signature. getMethod ();} resetDataSource (target, signature. getMethod ();}/*** identifies the data source in the method annotation and Class annotation of the target object */private void resetDataSource (Class <?> Cls, Method method) {try {Class <?> [] Types = method. getParameterTypes (); // The class annotation if (cls. isAnnotationPresent (performancekey. class) {performancekey source = cls. getAnnotation (performancekey. class); DynamicDataSourceHolder. setRouteKey (source. value ();} // Method annotation can overwrite class annotation Method m = cls. getMethod (method. getName (), types); if (m! = Null & m. isAnnotationPresent (performancekey. class) {performancekey source = m. getAnnotation (performancekey. class); DynamicDataSourceHolder. setRouteKey (source. value () ;}} catch (Exception e) {System. out. println (cls + ":" + e. getMessage ());}}}
After the code is written, add the configuration in the xml configuration file (only some configurations are listed ):
<Bean id = "multipleDataSourceExchange" class = "MultipleDataSourceExchange"/> <bean id = "txManager" class = "org. springframework. jdbc. datasource. dataSourceTransactionManager "> <property name =" dataSource "ref =" multipleDataSource "/> </bean> <tx: advice id = "txAdvice" transaction-manager = "txManager"> <tx: attributes> <tx: method name = "insert *" propagation = "NESTED" rollback-for = "Exception"/> <tx: method name = "add * "Propagation =" NESTED "rollback-for =" Exception "/>... </tx: attributes> </tx: advice> <aop: config> <aop: pointcut id = "service" expression = "execution (* com. datasource .. *. service. *. *(..)) "/> <! -- Note that the data source switching operation should be performed before the code at the persistent layer --> <aop: advisor advice-ref = "multipleDataSourceExchange" pointcut-ref = "service" order = "1"/> <aop: advisor advice-ref = "txAdvice" pointcut-ref = "service" order = "2"/> </aop: config>
By now, the dynamic switching of multiple data sources is achieved using AOP.