Demand:
There are times when we need to connect to multiple databases, but we don't know exactly which to call before the method call. That is, the connection of multiple databases is maintained at the same time, determined according to the parameters passed in the method.
Single data source invocation and the process of dynamic invocation of multiple data sources, it can be seen in the DAO layer that a DataSource selector is needed to determine exactly which data source to invoke.
Implementation method
Provides a common parent class for the DAO layer, maintaining a connection to multiple data sources (I am based on Ibatis, that is, maintaining multiple sqlsessiontemplate)
/** * Created by Hzlizhou on 2017/2/6. */public Abstract class Multidatasourcedao implements Idaosupport {private map<string, sqlsessiontemplate> Sqlse Ssiontemplatemap; Private Multidatasourceselector Multidatasourceselector; Public Multidatasourcedao (map<string, sqlsessiontemplate> Sqlsessiontemplatemap, Multidatasourceselector Multidatasourceselector) {this.sqlsessiontemplatemap = Sqlsessiontemplatemap; This.multidatasourceselector = Multidatasourceselector; } public map<string, Sqlsessiontemplate> Getsqlsessiontemplatemap () {return sqlsessiontemplatemap; } public void Setsqlsessiontemplatemap (map<string, sqlsessiontemplate> sqlsessiontemplatemap) {this.sqlSe Ssiontemplatemap = Sqlsessiontemplatemap; }//subclasses dynamically acquire Sqlsessiontemplate protected sqlsessiontemplate getsqlsessiontemplate () {String clustername by this method = Multidatasourceselector.getname (); Sqlsessiontemplate result = SqlsessiontemplAtemap.get (clustername); Assert.notnull (result); return result; }}
Multidatasourceselector is an excuse, depending on the current calling environment, to return different parameters, according to which you can determine which sqlsessiontemplate to use, for example, I put the current environment into the threadlocal
Public interface Multidatasourceselector { String getName ();} public class Dubbocontextdatasourceselector implements Multidatasourceselector { private String defaultname; Public Dubbocontextdatasourceselector (String defaultname) { this.defaultname = defaultname; } @Override public String GetName () { //dubbocontextholder is a map that maintains a threadlocal string res = Dubbocontextholder.getcontext (). get (Dubbocontextconstants.cluster_name); if (res = = null) { res = getdefaultname (); } return res; } Public String Getdefaultname () { return defaultname; }}
Then it is dynamic to get sqlsessiontemplate in the DAO layer.
Dynamic transactions
In fact, this is good to do, and then we face a slightly more complex problem, that DataSource is dynamic, the transaction must be dynamic. And there is no intrusion into the original code (such as the @transactional annotation in spring), then implement a method similar to @transactional. Names are called @dynamictransactional.
@Documented @target ({METHOD, TYPE}) @Retention (RUNTIME) public @interface dynamictransactional { propagation Propagation () default propagation.required; class<? Extends throwable>[] Rollbackfor () default {};}
The basic idea is to intercept @dynamictransactional annotations through AOP slices, invoke them, and then programmatically implement the transaction
The core method in the plane is
Private Object invokewithintransaction (final proceedingjoinpoint PJP, final dynamictransactional dynamictransaction) { Create Transactiontemplate final Platformtransactionmanager tran = Multitransactionmanagerholder.gettransactionmanager (); Transactiontemplate transactiontemplate = new Transactiontemplate (); Transactiontemplate.setpropagationbehavior (Dynamictransaction.propagation (). value ()); Transactiontemplate.settransactionmanager (Tran); Execution of Return Transactiontemplate.execute (new transactioncallback<object> () {@Override public Ob in a transaction Ject dointransaction (transactionstatus status) {Object result = null; try {result = Pjp.proceed (); } catch (Throwable throwable) {class<? extends throwable>[] C = dynamictransaction.rollbackfor (); for (class< extends throwable> tmp:c) {if (Tmp.isassignablefrom (THROWABLE.GETCL ()) { Status.setrollbackonly (); }}} return result; } });}
Where Multitransactionmanagerholder and the above Dynamic Data source selection principle, by taking the variable from the threadlocal, select the corresponding TransactionManager return
Configuration of Facets: The focus is on how to slice the specified annotations
<aop:config>
<aop:aspect id= "Multitransactionmanageraspect" ref= "MULTITRANSACTIONMANAGERAOP" >
<aop:around method= "Invokewithintransaction"
Arg-names= "Dynamictransaction"
Pointcut= "@annotation (dynamictransaction)"/>
</aop:aspect>
</aop:config>
Of course, this only implements the @dynamictransactional usage on the method, if the annotation is also used on the class, add a pointcut to all functions, and determine if there are @dynamictransactional annotations on the class of the Pointcut
Note: Due to the priority of the tangent plane, a little bit of processing is needed if the annotation on the implementation method takes precedence over the class.
Call time series diagram
Its own implementation is based on Abstractroutingdatasource, adding multiple DataSource to Sqlsessionfactory, as in the previous way, by Threadlocal to determine which datasource to use.
On dynamic transactions, the above is the use of slices, custom tags, using transactiontemplate to achieve, if you want more elegant, you can imitate Datasourcetransactionmanager write a,
Dynamic invocation and transaction processing based on spring multi-data source