The most recent project database to be integrated into one, so the multi-source database switching the writing to be cleared away. So the following records how far the use of database switching and personal understanding of the source code.
Framework: SPRING+MYBATIS+VERTX, (the use of multi-source database switching does not involve VERTX, so, applies to ssh,sm,ssh ... )。
Database: MySQL
Two key APIs:
One: ThreadLocal,
Two: Abstractroutingdatasource.
I have always insisted on first learn to use, to explore the source code and principles.
Part One (Implementation code):
The following is the implementation code:
Databasesource.xml:
<bean id= "DataSource" class= "Com.alibaba.druid.pool.DruidDataSource" > <property name= "username" value= " 1111111 "/> <property name=" password "value=" 1111111 "/> <property name=" connectionproperties "value=" com. Mysql.jdbc.Driver "/> <property name=" InitialSize "value=" 1 "/> <property name=" Minidle "value=" 1 "/> <property name= "Maxactive" value= "2"/> <property name= "maxwait" value= "60000"/> <property name= "time Betweenevictionrunsmillis "value=" 60000 "/> <property name=" Minevictableidletimemillis "value=" 300000 "/> &L T;property name= "Validationquery" value= "select ' X '"/> <property name= "Testwhileidle" value= "true"/> <pr Operty name= "Testonborrow" value= "false"/> <property name= "Testonreturn" value= "false"/> <property name= "Poolpreparedstatements" value= "true"/> <property name= "maxpoolpreparedstatementperconnectionsize" value= "20 "/> <property name=" Filters " Value= "stat"/></bean><bean id= "manager" class= "Com.alibaba.druid.pool.DruidDataSource" init-method= " Init "destroy-method=" Close "parent=" DataSource "> <property name=" url "value=" jdbc:mysql://localhost:33308/ Manager?useunicode=true&characterencoding=utf-8&allowmultiqueries=true "/></bean><bean id=" Lawsh "class=" Com.alibaba.druid.pool.DruidDataSource "init-method=" Init "destroy-method=" Close "parent=" DataSource "> <property name=" url "value=" jdbc:mysql://localhost:33308/law_hz?useunicode=true&characterencoding= Utf-8&allowmultiqueries=true "/></bean><bean id=" Multipledatasource "class=" Com.rayeye.law.app.dao.base.MultipleDataSource "> <property name=" defaulttargetdatasource "ref=" Lawsh "/> & Lt;property name= "Targetdatasources" > <map> <entry key= "D_1" value-ref= "manager"/> <entry key= "D_0" value-ref= "lawsh"/> </map> </property></bean><!--define transaction manager--><bean id= "TransactionManager" class= " Org.springframework.jdbc.datasource.DataSourceTransactionManager "> <property name=" DataSource "ref=" Multipledatasource "/></bean><!--define transactions using annotation--><tx:annotation-driven transaction-manager= "TransactionManager" proxy-target-class= "true" order= "" "/>
...
The above is the database settings,
Explanation:
DataSource is a common parameter setting for two databases;
The URL portion of the manager and LAWSH is set separately;
Multipledatasource is a database used by all other beans, encapsulates two database connections, sets the default database connection pool: Defaulttargetdatasource property value: lawsh;
Part Two:
Dynamic Switching section:
public class Multipledatasource extends Abstractroutingdatasource {public static Logger logger= Loggerfactory.getlogge R (Multipledatasource.class); private static final threadlocal<string> Datasourcekey =new threadlocal<string> (); public static void Setdatasourcekey (String dataSource) {logger.debug ("Data source toggle =====" +datasource); Datasourcekey.set (DataSource); } @Override protected Object Determinecurrentlookupkey () {Logger.debug ("Datasource====key:" +datasourcekey.ge T ()); return Datasourcekey.get (); The public static void Setdatasourcebybid (String db) {if (Db.equals (CONSTANT.DB1)) {Setdatasourcekey (Co Nstant. DB1); }else{Setdatasourcekey (CONSTANT.DB0); }}} @Component @aspect@order (2) public class Multiplemailsourceaspectadvice {Logger Logger =logger.getlogger (multiple Mailsourceaspectadvice.class); @Around ("Execution (* com.rayeye.law.app.service.*.* (..))") Public Object Doaround (Proceedingjoinpoint JP) throws Throwable {Logger.info ("Doaround ==================datasourse ========="); Multipledatasource.setdatasourcekey (CONSTANT.DB0); return Jp.proceed (); }} @Component @aspect@order (3) public class Multiplemanagersourceaspectadvice {Logger Logger =logger.getlogger ( Multiplemanagersourceaspectadvice.class); @Around ("Execution (* com.rayeye.law.app.service_manager). dingservice.* (..)) ") Public Object Doaround (Proceedingjoinpoint JP) throws Throwable {Logger.info ("Doaround 2 ==================datasou RSE ========= "); Multipledatasource.setdatasourcekey (CONSTANT.DB1); return Jp.proceed (); }}
The above is the Code section,
This way, your code can implement a method in the specified Java class to use a database connection.
The following is the code and source parsing section:
Key API one: Abstractroutingdatasource:
The full path of the class: Org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
Originally I want to write threadlocal first, but just write a few lines, feel first write abstractroutingdatasource easier to understand, so will abstractroutingdatasource forward platoon.
The following is the source section:
The Abstractroutingdatasource class can be understood as a routing mediator for DataSource, which can be used to switch databases.
In the previous code, I rewritten the Determinecurrentlookupkey method of the Abstractroutingdatasource class and switched the database in the method.
The reason is because abstarctroutingdatrasource the following section of code (source):
@Override
Public Connection getconnection () throws SQLException {
Return Determinetargetdatasource (). getconnection ();
}
Protected DataSource Determinetargetdatasource () {
Determinecurrentlookupkey ();D Atasource DataSource = This.resolvedDataSources.get (LookupKey); if (DataSource = = null && ( This.lenientfallback | | LookupKey = = null)) {dataSource = This.resolveddefaultdatasource;} if (DataSource = = null) {throw new IllegalStateException ("cannot determine target DataSource for lookup key [" + LookupKey + "]");} return dataSource;}
whereDeterminecurrentlookupkey () is the method overridden in the previous code, specifying which database to use in our overridden method,
The variable resolveddatasources (which is a map) here stores the two databases and their key values that we set in the configuration file;
The next section of the source shows the source of the resolveddatasources:
@Override
public void Afterpropertiesset () { if (this.targetdatasources = = null) { throw new IllegalArgumentException (" Property ' Targetdatasources ' is required "); } This. resolveddatasources = new Hashmap<object, datasource> (This.targetDataSources.size ()); For (Map.entry<object, object> entry:this.targetDataSources.entrySet ()) { Object LookupKey = Resolvespecifiedlookupkey (Entry.getkey ()); DataSource DataSource = Resolvespecifieddatasource (Entry.getvalue ()); This.resolvedDataSources.put (LookupKey, DataSource); } if (This.defaulttargetdatasource! = null) { This.resolveddefaultdatasource = Resolvespecifieddatasource ( This.defaulttargetdatasource);} }
One of the targetdatasources is that we're in the config file.
<property name= "Targetdatasources" >
<map>
<entry key= "D_1" value-ref= "manager"/>
<entry key= "D_0" value-ref= "Lawsh"/>
</map>
</property>
, It is a map, which stores two database connection pools and corresponding key;
In the For loop, Spring places the database connection pool and the corresponding key value in resolveddatasources(a map);
The source code for two of these methods is as follows:
Protected object Resolvespecifiedlookupkey (object LookupKey) { return lookupkey;} Protected DataSource Resolvespecifieddatasource (Object DataSource) throws IllegalArgumentException { if ( DataSource instanceof DataSource) { return (dataSource) DataSource; } else if (DataSource instanceof string) { return This.dataSourceLookup.getDataSource ((String) dataSource); } else { throw new illegalargumentexception ( "illegal data source value-only [Javax.sql.DataSource] and S Tring Supported: "+ DataSource);} }
At this point, you can clearly rewrite the purpose and meaning of the method Determinecurrentlookupkey (switch database).
Key API two: ThreadLocal:
The full path of the class:java.lang.threadlocal<t>;
The primary role of this class is to create separate copies of local variables for each thread, thradlocal between threads (different databases used by different threads, complementary effects, thread safety).
The following is the source section:
Public T get () { Thread T = Thread.CurrentThread (); Getmap (t); if (map! = null) { Threadlocalmap.entry e = Map.getentry (this); if (E! = null) { @SuppressWarnings ("unchecked") T result = (t) e.value; return result; } } return Setinitialvalue (); }
Threadlocalmap Getmap (Thread t) {
t.threadlocals;
}
void Createmap (Thread T, T firstvalue) {
T.threadlocals = new Threadlocalmap (this, firstvalue);
}
As you can see, all the variables are stored in a static hash map (threadlocalmap), and this variable is saved in thread , that is, Each thread holds a reference to Threadlocalmap (Threadlocalmap is initialized and then threadlocal).
At this point, you can understand the role of the Threadlocal class in switching databases (save the database connection flags for each thread for use and switching).
At this point, the majority of the database switch is finished, but also left the code in the annotations and Databasesource.xml in the transaction manager of the Order property did not say, this part of the transaction management and cutting programming, today a bit of something, this part of the next talk.
Why do you suddenly remember to switch the database to write down because you notice that spring and MyBatis are integrated:
<bean class= "Org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name= "Basepackage" value= "Com.rayeye.bill.api.dao"/> </bean>
The properties of this class are sqlsessionfactory,
The following is the source code:
/** * Specifies which {@code sqlsessionfactory} to use of the case, that there was usually this was only needed W hen you * has more than one datasource. * <p> * @deprecated use {@link #setSqlSessionFactoryBeanName (String)} instead. * * @param sqlsessionfactory * /@Deprecated public void Setsqlsessionfactory (sqlsessionfactory Sqlsessionfactory) { this.sqlsessionfactory = sqlsessionfactory; }
As you can see, this property can be used when configuring a single database connection, but this property does not apply when configuring multiple database connections, and some of the properties of this class seem to have been deprecated.
Above.
End
Switch Database +threadlocal+abstractroutingdatasource