All connection pools in Java are implemented in accordance with the specifications of the DataSource interface, the connection can be obtained by getconnection () to get the connection without the relationship between the underlying database connection pool.
1 Public Interface DataSource extends Commondatasource, Wrapper {23 throws SQLException; 4 5 throws SQLException; 6 }
In most systems we only need one data source, and now the web system is usually the cornerstone of spring. Whether you're an XML configuration, a JavaBean configuration, or a yml,properties configuration file configuration, the core is to inject a data source into the management of spring.
In some systems we may face a number of situations, the connection of multiple tables, master and slave, and even a number of different libraries and so on, the core requirement is that we may need to configure multiple connection pools.
In MyBatis systems we use multiple data sources to configure multiple datasource,sqlsessionfactory,sqlsessiontemplate and then manage them separately in XML and mapper. Refer to 61202084 this blog for details.
This scheme is sufficient for small systems, and the author thinks it is more suitable for many different databases.
Back to the point, in spring from the 2.0.1 version of the default provides Abstractroutingdatasource, we inherit it to implement related methods, all the required data sources can be dynamically switched data source. We can look at the core method of the source code.
Public abstract class Abstractroutingdatasource extends Abstractdatasource implements Initializingbean {? Set all data sources private map<object, object> targetdatasources; Sets the default data source and returns the default data source when no relevant data source is found private Object defaulttargetdatasource; Fast failure, can ignore private boolean lenientfallback = true; Jndi-related, can be ignored private datasourcelookup datasourcelookup = new Jndidatasourcelookup (); After parsing all the data sources, the core private map<object, datasource> resolveddatasources; After parsing the default data source, the core private DataSource Resolveddefaultdatasource; To set the correlation parameter method public void Settargetdatasources (Map<object, object> targetdatasources) {This.targetdatasource s = targetdatasources; } public void Setdefaulttargetdatasource (Object defaulttargetdatasource) {This.defaulttargetdatasource = Defaul Ttargetdatasource; The public void Setlenientfallback (Boolean lenientfallback) {this.lenientfallback = Lenientfallback; } public void Setdatasourcelookup (Datasourcelookup datasourcelookup) {this.datasourcelookup = (Datasourcelookup! = null? datasourcelookup:new Jnd Idatasourcelookup ()); }? @Override public void Afterpropertiesset () {//detects whether to set all data sources if (this.targetdatasources = = null) { throw new IllegalArgumentException ("Property ' targetdatasources ' is required"); }//parsing all data sources is generally useless, mostly if map<object, object> Targetdatasources's value is string, it is looked up from the Jndi data source This.resolvedda Tasources = new Hashmap<object, datasource> (This.targetDataSources.size ()); For (Map.entry<object, object> entry:this.targetDataSources.entrySet ()) {Object LookupKey = Resolvespe Cifiedlookupkey (Entry.getkey ()); DataSource DataSource = Resolvespecifieddatasource (Entry.getvalue ()); This.resolvedDataSources.put (LookupKey, DataSource); }//Ibid. parse default data source if (This.defaulttargetdatasource! = null) {This.resolveddefaultdatasource =Resolvespecifieddatasource (This.defaulttargetdatasource); } }? @Override public Connection getconnection () throws SQLException {///core, get data source first find the current connection pool and get the data source return Determi Netargetdatasource (). getconnection (); }? @Override Public Connection getconnection (string Username, string password) throws SQLException {return Determi Netargetdatasource (). getconnection (username, password); }? Protected DataSource Determinetargetdatasource () {//Call Determinecurrentlookupkey, then go to resolveddefaultdatasource find, there is Returns the corresponding data source without returning the default data source Assert.notnull (this.resolveddatasources, "DataSource router not initialized"); Object LookupKey = Determinecurrentlookupkey (); DataSource DataSource = This.resolvedDataSources.get (LookupKey); if (DataSource = = null && (This.lenientfallback | | lookupkey = = NULL)) {DataSource = This.resolveddefa Ultdatasource; } if (DataSource = = null) {throw new IllegalstAteexception ("Cannot determine target DataSource for lookup key [" + LookupKey + "]"); } return DataSource; }//And Determinecurrentlookupkey need our own to achieve. It is often necessary to combine AOP and threadlocal. We AOP gets the data source that the current user wants from the annotation, and then sets it to the current thread. The Determinecurrentlookupkey is then taken out of the current thread and returned to Determinetargetdatasource to determine the final data source protected abstract Object Determinecurrentlookupkey ();?}
The above specific implementation can refer to this blog 77449710 It is important to note that the order of the AOP must precede the order of things.
Advantages: Convenient configuration, convenient use. Cons: The default implementation has some limitations, and most people are enough to use it. If you have more complex usage scenarios, multi-Library data sources, grouped data sources, multi-master and many more, more complex scenarios can be tried
Https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter
A springboot-based quick-integration launcher for multiple data sources.
A standard master-slave configuration is as follows, which can be used with the introduction of related configurations. More ways to view related documents.
Spring: DataSource: dynamic: primary:master #设置默认的数据源或者数据源组, the default value is master, if you master the main library by default the name is master does not define this item. DataSource: Master: username:root password:123456 driver-class-name:com.mysql.jdbc.driver url:jdbc:mysql://47.100.20.186:3306/dynamic?characterencoding=utf8&usessl=false slave_1: Username:root password:123456 driver-class-name:com.mysql.jdbc.driver url:jdbc:mysql:// 47.100.20.186:3307/dynamic?characterencoding=utf8&usessl=false slave_2: username:root password:123456 driver-class-name:com.mysql.jdbc.driver url:jdbc:mysql://47.100.20.186:3308/dynamic? Characterencoding=utf8&usessl=false
Realize the core source code as follows
public class Dynamicroutingdatasource extends Abstractroutingdatasource {/** * all libraries */private map<string , datasource> Datasourcemap; /** * Packet Database */private map<string, dynamicgroupdatasource> groupdatasources = new hashmap<> (); @Setter private Dynamicdatasourceprovider Dynamicdatasourceprovider; @Setter private class<? Extends dynamicdatasourcestrategy> Dynamicdatasourcestrategyclass; /** * Default data source name, default master, can be a group data source name, can be a single data source name */@Setter private String primary; @Override protected Object Determinecurrentlookupkey () {return Dynamicdatasourcecontextholder.getdatasourcelook Upkey (); } @Override protected DataSource Determinetargetdatasource () {String lookupkey = (string) determinecurrentlo Okupkey (); if (Groupdatasources.containskey (LookupKey)) {Log.debug ("return data source from {} Group data source", LookupKey); Return Groupdatasources.get (LookupKey). Determinedatasource ();} else if (Datasourcemap.containskey (LookupKey)) {Log.debug ("return data source from {} Single data source", LookupKey); Return Datasourcemap.get (LookupKey); } log.debug ("Return data from the default data source"); return Groupdatasources.containskey (primary)? Groupdatasources.get (LookupKey). Determinedatasource (): Datasourcemap.get (primary); } @Override public void Afterpropertiesset () {this.datasourcemap = Dynamicdatasourceprovider.loaddatasources (); Log.debug ("co-loading {} Data Sources", datasourcemap.size ()); Packet data source for (map.entry<string, datasource> dsItem:dataSourceMap.entrySet ()) {String DSName = ds Item.getkey (); if (Dsname.contains ("_")) {string[] Groupds = Dsname.split ("_"); String groupName = groupds[0]; DataSource DataSource = Dsitem.getvalue (); if (Groupdatasources.containskey (GroupName)) {Groupdatasources.get (groupName). AddDatasource (DataSource ); } else {try {dynamicgroupdatasource groupdatasource = new Dynamicgro Updatasource (GroupName, Dynamicdatasourcestrategyclass.newinstance ()); Groupdatasource.adddatasource (DataSource); Groupdatasources.put (GroupName, Groupdatasource); } catch (Exception e) {e.printstacktrace (); }}}}//detection group data source settings iterator<map.entry<string, dynamicgroupdatasource& gt;> groupiterator = Groupdatasources.entryset (). iterator (); while (Groupiterator.hasnext ()) {map.entry<string, dynamicgroupdatasource> item = Groupiterator.next (); Log.debug (there are {} data sources under Group {}, Item.getkey (), Item.getvalue (). Size ()); if (Item.getvalue (). Size () = = 1) {Log.warn ("Be careful not to set up a group with only one data source, {} Group will be removed", item.getkey ()); Groupiterator.remove ();}}//detect default data source setting if (Groupdatasources.containskey (primary)) {Log.debug ("The current default data source is a group data source, the group name is {}, there are {} data sources under it, primary, groupdatasources.size ()); } else if (Datasourcemap.containskey (primary)) {Log.debug ("The current default data source is a single data source, the data source name is {}", primary); } else {throw new RuntimeException ("Please check primary default database settings, not currently found" + primary + "data source"); } }}
Spring Multi-data source, dynamic Data stream code parsing