sping dynamic switching and updating of multiple data sources

Source: Internet
Author: User
Tags stub log4j


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&gt ;
		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




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.