Spring configures multiple data sources (possible)

Source: Internet
Author: User
Tags db2

You can see that abstractroutingdatasource gets the data source before it calls the Determinecurrentlookupkey method to find the current LookupKey, the LookupKey is the data source identity.
So by overriding this method of finding the identity of the data source, you can have spring switch to the specified data source.
The first step: Create a Dynamicdatasource class, inherit the Abstractroutingdatasource and override the Determinecurrentlookupkey method, with the following code:

1 public class Dynamicdatasource extends Abstractroutingdatasource {2 3     @Override4     protected Object Determinecurrentlookupkey () {5         //Get Data source identification from a custom location 6         return Dynamicdatasourceholder.getdatasource (); 7     }8 9}

Step two: Create Dynamicdatasourceholder to hold the data source identity used in the current thread, with the following code:

1 public class Dynamicdatasourceholder {2     /** 3      * Note: The data source identity is stored in the thread variable to avoid interfering with each other when the data source is multithreaded 4      */5     Private Static final threadlocal<string> Thread_data_source = new threadlocal<string> (); 6  7 public     static String Getdatasource () {8         return thread_data_source.get (); 9     }10     static void Setdatasource (String dataSource) {         thread_data_source.set (dataSource);     }14     static void Cleardatasource () {         thread_data_source.remove ();     }18 19}

Step Three: Configure multiple data sources and dynamicdatasource beans created in the first step, with the following simplified configuration:

 1 <!--Create a data source 1, connect to a database db1 2 <bean id= "DataSource1" class= "Org.apache.commons.dbcp.BasicDataSource" destroy-method= "Close" > 3 <property name= "Driverclassname" value= "${db1.driver}"/> 4 <property name= " URL "value=" ${db1.url} "/> 5 <property name=" username "value=" ${db1.username} "/> 6 <property name=" PA ssWOrd "value=" ${db1.password} "/> 7 </bean> 8 <!--Create data source 2, connect to database DB2-9 <bean id=" DataSource2 "class=" Org.apache.commons.dbcp.BasicDataSource "destroy-method=" Close ">10 <property name=" Driverclassname "value=" $ {db2.driver} "/>11 <property name=" url "value=" ${db2.url} "/>12 <property name=" username "value=" ${db2 . Username} "/>13 <property name=" password "value=" ${db2.password} "/>14 </bean>15 <!--Create data source 3, connect to database D B3-->16 <bean id= "DataSource3" class= "Org.apache.commons.dbcp.BasicDataSource" destroy-method= "Close" >17 & Lt;property name= "Driverclassname" value= "${dB3.driver} "/>18 <property name=" url "value=" ${db3.url} "/>19 <property name=" username "value=" ${db3.u Sername} "/>20 <property name=" password "value=" ${db3.password} "/>21 </bean>22 all <bean id=" dynamic  DataSource "class=" Com.test.context.datasource.DynamicDataSource "> <property name=" targetdatasources "> <map key-type= "java.lang.String" >26 <!--specify lookupkey and corresponding data source-->27 <ent Ry key= "DataSource1" value-ref= "DataSource1" ></entry> <entry key= "DataSource2" value-ref= "data         Source2 "></entry> <entry key=" DataSource3 "value-ref=" DataSource3 "></entry> 30 </map> </property> <!--Here you can specify the default data source-->33 <property name= "Defaulttargetdatas   Ource "ref=" DataSource1 "/> </bean>

You can already use a multi-data source here, and as long as Dynamicdatasourceholder.setdatasource ("DataSource2") can switch to data source 2 and manipulate the database DB2 before manipulating the database.

The sample code is as follows:

1 @Service 2 public class Dataserviceimpl implements DataService {3     @Autowired 4     private Datamapper datamapper; 5  6     @Override 7 public     list<map<string, object>> getList1 () {8         //not specified, the data source is used by default 1 9         return Datamapper.getlist1 ();     }11 @Override13 public     list<map<string, object>> GetList2 () {+         //Specify switch to Data source 215         dynamicdatasourceholder.setdatasource ("DataSource2");         Datamapper.getlist2 ();     }18 @Override20 public     list<map<string, object>> getList3 () {+/         /Specify switch to Data source 322         Dynamicdatasourceholder.setdatasource ("DataSource3");         Datamapper.getlist3 ();     }25}

--------------------------------------------------------------------------------------Gorgeous split-line----------------------------- ---------------------------------------------------------------------

But the problem comes, if each time you switch the data source calls Dynamicdatasourceholder.setdatasource ("xxx") is very cumbersome, and the code is very large, it is easy to omit, later maintenance is also more troublesome. Can you specify the data source you want to access directly through annotations, such as using @datasource ("XXX") on the DAO layer to specify access to the data source XXX? Of course! The premise is, add a little extra configuration ^_^.
First, we have to define a note named DataSource, which reads:

1 @Target ({TYPE, METHOD}) 2 @Retention (RUNTIME) 3 public @interface DataSource {4     String value (); 5}

Then, define the AOP facets to intercept all the methods with annotated @datasource, and remove the value of the annotation as the data source identifier in the Dynamicdatasourceholder thread variable:

 1 public class Datasourceaspect {2 3/** 4 * intercept target method, get data source identity specified by @datasource, set to thread store to toggle data Source 5 * 6 *         @param point 7 * @throws Exception 8 */9 public void intercept (Joinpoint point) throws Exception {10 Class<?> target = Point.gettarget (). GetClass (); Methodsignature signature = (methodsignature) point.gets             Ignature (); 12//default use annotation of the target type, if not, use it to implement the interface annotation for (class<?> clazz:target.getInterfaces ()) {14 Resolvedatasource (Clazz, Signature.getmethod ());}16 Resolvedatasource (Target, Signature.getmet      Hod ()); 17}18 19/**20 * Extract target object method annotations and data source identifiers in type annotations * * @param clazz23 * @param method24 */25 private void Resolvedatasource (Class<?> clazz, method) {# try {CLASS&LT;?&G T [] types = Method.getparametertypes (); 28//Default usage type Note if (Clazz.isannotationpresent (datasource.cl             ) {30    DataSource Source = clazz.getannotation (Datasource.class); Dynamicdatasourceholder.setdatasource (sou Rce.value ()); 32}33//method annotations can override the type annotations in method m = Clazz.getmethod (Method.getname (), Typ  ES); + if (m! = null && m.isannotationpresent (Datasource.class)) {DataSource source             = M.getannotation (Datasource.class), PNs Dynamicdatasourceholder.setdatasource (Source.value ()); 38 }39} catch (Exception e) {System.out.println (Clazz + ":" + e.getmessage ()); 41}42} 43 44}

Finally, it is possible to configure interception rules in the spring configuration file, such as blocking all methods of the service layer or the DAO Layer:

1 <bean id= "Datasourceaspect" class= "Com.test.context.datasource.DataSourceAspect"/>2 <aop:config     >3         <aop:aspect ref= "Datasourceaspect" >4             <!--intercept all service methods-->5 <aop:pointcut id=             " Datasourcepointcut "expression=" Execution (* com.test.*.dao.*.* (..)) " />6             <aop:before pointcut-ref= "datasourcepointcut" method= "intercept"/>7         </aop:aspect>8     </aop:config>

OK, so you can use annotation @datasource directly on the class or method to specify the data source, without having to manually set it every time.

The sample code is as follows:

1 @Service 2//Default Dataserviceimpl All methods Access data source 1 3 @DataSource ("DataSource1") 4 public class Dataserviceimpl implements data Service {5     @Autowired 6     private datamapper datamapper; 7  8     @Override 9 public     list<map< String, object>> GetList1 () {Ten         //not specified, the data source is used by default 111         return Datamapper.getlist1 ();     OVERRIDE15     //overrides specified on the class, using the data source 216     @DataSource ("DataSource2") of the public     list<map<string, Object >> GetList2 () {         datamapper.getlist2 ();     }20     @Override22     //override specified on class, use data source 323     @DataSource ("DataSource3")     , public list<map<string, object>> GetList3 () {         return Datamapper.getlist3 ();     }27}

Tip: Note @datasource can be either added to the method or added to an interface or an interface's implementation class, with precedence: Method > Implementation Class > interface. That is, if the interface, the interface implementation class, and the method add @datasource annotations to specify the data source, the precedence is specified on the method.

Spring configures multiple data sources (possible)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.