I wanted to write without writing because the Dynamic Data source switch is resolved, but cannot be added dynamically, and adding a new data source requires restarting the Web server. Later this problem solved, but because the business is busy, today, to write this article for a week without turning off the computer.
The company's business is this, there are currently nearly 150 db, each DB database library structure is consistent. Located under different ports on the same machine. Now you need to make a management system that can manage these db. And the number of these db is growing. Different customers will visit different databases as needed in each visit. We used to always configure a data source in the spring framework to constantly switch between multiple data sources when DAO accesses sessionfactory.
The technical issues to be addressed in the face of this need are:
1. How these DB information is stored and how it is managed with spring data sources.
2. How to do concurrency, different people log in processing different DB data, that is, the dynamic switching of data sources.
3. How to dynamically host to spring when a new data source is added.
Scheme:
1. First of all these DB data, must be stored in the DB, when there is new DB information, you can let the students add operations. If it is in XML, error-prone does not say, the dynamic load of those DB information XML is also possible to do.
2. How to do the dynamic switching scheme is probably the case, first use threadlocal to record the server selected by different people, and then implement the spring Abstractdatasource class, in this class to add a server based on the foreground to dynamically return the data source logic.
3. Dynamically change the data source by first implementing the registered data source of the Abstractdatasource implementation class, identifying the latest data source information from the DB, set to the list of data sources registered in Abstractdatasource.
Practice:
Initially consider using Spring's abstractroutingdatasource to realize the data source switch, can switch but can not change the database dynamically, after the initialization is complete cannot move.
Imagine that if spring's transaction does not care about the changes in the data source in Abstractroutingdatasource, it can directly implement a function similar to Abstractroutingdatasource and can dynamically change the data source. Abstractroutingdatasource.
Build yourself a abstractroutingdatasource (the same name as spring provides)
Spring inherits the Abstractdatasource and implements the interface Initializingbean.
The implementation of the interface Initializingbean is in fact to the user set into the datasource map into their own map. Therefore, it is not possible to set the DataSource Map back to take effect.
Then we will write a abstractroutingdatasource now, but also inherit Abstractdatasource, but do not have to implement Initializingbean, And then to implement the abstractroutingdatasource of the sub-class to achieve initializingbean.
Myabstractroutingdatasource.java
Public abstract class Myabstractroutingdatasource extends abstractdatasource{private Boolean lenientfallback = true;
Private Map<object, datasource> resolveddatasources;
Private DataSource Resolveddefaultdatasource;
Private Org.apache.log4j.Logger Logger = Org.apache.log4j.Logger.getLogger (Myabstractroutingdatasource.class); Public Connection getconnection () throws SQLException {//TODO auto-generated method stub return Determinetargetdata
Source (). getconnection (); } Public Connection getconnection (string Username, string password) throws SQLException {//TODO auto-generated Me
Thod stub return Determinetargetdatasource (). getconnection (username, password); } public Logger Getparentlogger () throws Sqlfeaturenotsupportedexception {//TODO auto-generated method stub return
Null } public void Putnewdatasource (Object key, DataSource DataSource) {if (this.resolveddatasources = = null) {This.reso lveddatasources = new Hashmap<obJect, datasource> ();
} if (This.resolvedDataSources.containsKey (key)) {This.resolvedDataSources.remove (key);
Logger.info ("Remove old key:" + key);
} logger.info ("Add key:" + key + ", value=" + dataSource);
This.resolvedDataSources.put (key, DataSource); }/** * Retrieve the current target DataSource. Determines the * {@link #determineCurrentLookupKey () current lookup key}, performs * a lookup in the {@link #setTarget DataSources targetdatasources} map, * Falls back to the specified * {@link #setDefaultTargetDataSource default target
DataSource} if necessary. * @see #determineCurrentLookupKey () */protected DataSource Determinetargetdatasource () {Assert.notnull (this.resolve
Ddatasources, "DataSource router not initialized");
Object LookupKey = Determinecurrentlookupkey ();
int index = 0; For (Entry<object, datasource> element:resolvedDataSources.entrySet ()) {Logger.debug ("Myabstractds, Index:" + Index + ", key:" + element.getkey () +", Value:" + element.getvalue (). toString ());
index++;
} DataSource DataSource = This.resolvedDataSources.get (LookupKey); if (DataSource = = null && (This.lenientfallback | | lookupkey = = NULL)) {DataSource = This.resolveddefaultdatas
Ource; } if (DataSource = = null) {throw new IllegalStateException ("cannot determine target DataSource for lookup key [" + L
Ookupkey + "]");
} logger.info ("Myabstractds, hit DS is" + datasource.tostring ());
return dataSource;
} protected abstract Object Determinecurrentlookupkey ();
public Boolean islenientfallback () {return lenientfallback;
The public void Setlenientfallback (Boolean lenientfallback) {this.lenientfallback = Lenientfallback;
} public Map<object, Datasource> getresolveddatasources () {return resolveddatasources; } public void Setresolveddatasources (Map<object, datasource> resolveddatasources) {this.resolveddatasources = R
Esolveddatasources; } @Override Public Boolean IsWRapperfor (class<?> iface) throws SQLException {return (Iface.isinstance (this) | | | Determinetargetdatasource ().
Iswrapperfor (Iface)); } @SuppressWarnings ("Unchecked") @Override public <T> T Unwrap (class<t> iface) throws SQLException {if (Iface.isinstance (this))
{return (T) this;
} return Determinetargetdatasource (). Unwrap (Iface);
} public DataSource Getresolveddefaultdatasource () {return resolveddefaultdatasource; } public void Setresolveddefaultdatasource (DataSource resolveddefaultdatasource) {This.resolveddefaultdatasource = re
Solveddefaultdatasource; }
}
Myroutingdatasource.java
public class Myroutingdatasource extends Myabstractroutingdatasource implements Initializingbean,
applicationcontextaware{private static final Logger Logger = Logger.getlogger (Myroutingdatasource.class);
@Resource (name = "Playerserverinfoconstant") private playerserverinfoconstant playerserverinfoconstant;
Private ApplicationContext applicationcontext = null;
@Override protected Object Determinecurrentlookupkey () {String datasourcename = spobserver.getsp ();
Logger.info ("Dynamic Data Source Name:" + datasourcename);
if (datasourcename = = null) {DataSourceName = "dataSource2";
} return datasourcename;
} public DataSource Getdatasource () throws propertyvetoexception{String sp = SPOBSERVER.GETSP ();
return Getdatasource (SP); } private DataSource Getdatasource (String datasourcename) throws Propertyvetoexception {map<object, Datasource> ;
Resolveddatasources = This.getresolveddatasources (); if (resolveddatasources = = null) {Resolveddatasources = Playerserverinfoconstant.getservermap (false); } if (datasourcename = = NULL | |
". Equals (DataSourceName)) {return Getresolveddefaultdatasource ();
} DataSource Source = Resolveddatasources.get (datasourcename);
if (Source = = null) {return getresolveddefaultdatasource ();
} return source; } public void Afterpropertiesset () throws Exception {map<object, datasource> resolveddatasources = Playerserver
Infoconstant.getservermap (false);;
int i = 0; For (Entry<object, datasource> element:resolvedDataSources.entrySet ()) {Logger.debug ("Myabstractds, Index:" +
i + ", key:" + element.getkey () + ", Value:" + element.getvalue (). toString ());
i++;
} this.setresolveddatasources (resolveddatasources);
This.setresolveddefaultdatasource ((DataSource) This.applicationContext.getBean ("DataSource")); } public void Setapplicationcontext (ApplicationContext arg0) throws beansexception {This.applicationcontext = arg0
; }
}
In the code above:
1, make a filter, each time the customer makes a request to call SPOBSERVER.PETSP (DataSourceName), will be in the request DataSourceName passed to the Spobserver object. Finally, modify the method of Multidatasource Getdatasource ().
Myfilter.java
public class Myfilter implements Filter {public
myfilter () {
} public
void init (filterconfig filterconfig) t Hrows servletexception {
} public
void DoFilter (ServletRequest request, servletresponse response,
Filterchain chain) throws IOException, servletexception {
httpservletrequest HttpRequest = (httpservletrequest) request;
Integer datasourceidx = (integer) httprequest.getsession (). getattribute (Appconstant.session_deault_server_idx_key) ;
if (Datasourceidx = = NULL | | datasourceidx = =-1) {
//the test server ' idx is 2
datasourceidx = 2;
}
SPOBSERVER.PUTSP ("DataSource" + datasourceidx);
Chain.dofilter (request, response);
}
public void Destroy () {
}
}
Spovserver.java
public class Spobserver {
private static ThreadLocal local = new ThreadLocal ();
public static void putsp (String sp) {
local.set (sp);
}
public static String getsp () {
return (String) local.get ();
}
}
2, constructs a playerserverconstant from the library to take the DB information.
Playerserverconstant.java
public class Playerserverinfoconstant implements applicationlistener{@Autowired private Gameserverinfoservice Gamese
Rverservice;
Private Concurrenthashmap<object, datasource> alldatasources = null;
Private Logger Logger = Logger.getlogger (Playerserverinfoconstant.class); Private Playerserverinfoconstant () {} public map<object, Datasource> Getservermap (Boolean refreshflag) throws Pro
pertyvetoexception{if (alldatasources = = NULL | | refreshflag = TRUE) {//synchronized (Servermap) {
if (Servermap = = null) {alldatasources = new concurrenthashmap<object, datasource> (); For (Gameserverinfo info:gameServerService.getGameServers ()) {Combopooleddatasource CPDs = new Combopooleddat
Asource ();
Cpds.setdriverclass ("Com.mysql.jdbc.Driver"); Cpds.setjdbcurl ("jdbc:mysql://" + info.getip () + ":" + info.getport () + "/" + info.getdb () + "? Useunicode=true&charact
Erencoding=utf-8 "); Cpds.setuser (Info.getusername ());
Cpds.setpassword (Info.getpassword ());
Cpds.settestconnectiononcheckout (TRUE);
Logger.info ("Init datasouce:" + info.getip () + "" + Info.getport ());
Alldatasources.put ("DataSource" + info.getidx (), CPDs);
}//}//}} return alldatasources; } public void Empytdatasources () {} public void Onapplicationevent (Applicationevent arg0) {try {getserverm
AP (FALSE);
} catch (Propertyvetoexception e) {e.printstacktrace (); }
}
}
How does the DataSource of Seesionfactory in DAO be constructed?
<bean id= "Gameserverdatasource" class= "Com.youai.gamemis.model.dao.MyRoutingDataSource" >
<property Name= "Resolveddatasources" >
<map key-type= "java.lang.String" >
<entry key= "DataSource2" value-ref= "DataSource"/>
</map>
</property>
<property name= " Resolveddefaultdatasource "ref=" DataSource "/>
</bean>
How to dynamically update a data source:
concurrenthashmap<object, datasource> newdatasource = new Concurrenthashmap<object,
Datasource> (Playerserverinfoconstant.getservermap (true)); Modify DataSource at datasourceconstant beandefinitionbuilder userbeandefinitionbuilder = Beandefinitio
NBuilder. Genericbeandefinition (Playerserverinfoconstant.class);
Userbeandefinitionbuilder.addpropertyvalue ("Gameserverservice", Gameserverservice);
Userbeandefinitionbuilder.addpropertyvalue ("Alldatasources", Newdatasource);
beandefinition BD = Userbeandefinitionbuilder.getrawbeandefinition ();
Bd.setscope ("singleton");
Beanfactory.registerbeandefinition ("Playerserverinfoconstant", BD); Refresh data at Router for (Entry<object, datasource> element:newDataSource.entrySet ()) {Games
Erverdatasource.putnewdatasource (Element.getkey (), Element.getvalue ()); }
It seems like a mess, there are problems, welcome to discuss.
ref:http://www.wzero.net/?p=62
http://www.iteye.com/topic/72486
http://westerly.blog.51cto.com/1077430/638818
http://blog.csdn.net/buyaore_wo/article/details/8119577