How to solve problems with multiple data sources in the spring framework

Source: Internet
Author: User

In our project, we encountered the problem of connecting multiple databases to our projects, and different clients accessing different databases as needed in each visit. We used to always configure a data source in the spring and hibernate frameworks, so the DataSource property of Sessionfactory always points to this data source and remains constant, All DAO uses this data source to access the database when Sessionfactory is used. But now, because of the needs of the project, our DAO in Access to Sessionfactory in the time of the various data sources have to constantly switch, the problem arises: how to let Sessionfactory in the implementation of data persistence, according to the needs of customers can dynamically switch different data sources? Can we fix this with a few changes in the spring framework? Are there any design patterns that can be used?

Analysis of the problem

I first thought of configuring all the DataSource in spring's applicationcontext. These datasource may be of different types, such as different databases: Oracle, SQL Server, MySQL, and possibly different data sources: Apache, for example. Provided by Org.apache.commons.dbcp.BasicDataSource, spring-provided org.springframework.jndi.JndiObjectFactoryBean and so on. Sessionfactory then sets the DataSource property to a different data source based on each request of the customer to reach the purpose of switching the data source.

However, I quickly found a problem: resource contention occurs when multiple users concurrently access the database concurrently. This is the "singleton model" to blame. As we all know, when we use the spring framework, the beans registered in Beanfactory are basically in singleton mode, that is, when spring starts, the beans are loaded into memory, and each bean has only one object in the entire project. Just because there is only one object, all of the properties of the object, more precisely the instance variable, behaves as if it were a static variable (actually "static" and "singleton" are often very similar to two things, we often use "static" to implement "singleton"). For our problem, Sessionfactory has only one object in the entire project, and its instance variable datasource is just one, just like a static variable. If different users are constantly modifying the value of the DataSource, there will inevitably be a problem of multi-user contention for a variable, which creates a hidden danger to the system.

Through the above analysis, solve the problem of multi-data source access, focus on sessionfactory in the implementation of data persistence, the ability to use a piece of code to dynamically switch data sources according to customer needs, and solve the problem of resource contention.

Solution of the problem (i) Adopt decorator design pattern

To solve this problem, my train of thought locked in this datasource. If sessionfactory points to the DataSource can be based on customer needs to connect the real data source that the customer needs, that is, to provide the ability to dynamically switch the data source, then the problem is solved. So what do we do? To modify the datasource source code that we want to use? This is obviously not a good solution, and we want our changes to be separate from the original datasource code. Based on the above analysis, using the decorator pattern (decorator mode) in the GOF design pattern should be the best option we can choose.

What is "decorator mode"? Simply put, when we need to modify the original function, but we do not want to directly modify the original code, design a decorator nested outside the original code. When we use decorator in the same way as the original class, some functions of decorator have been modified for the functions we need to modify. The structure of the decorator pattern.

We would have needed to modify some of the features of all the specific component classes in the diagram, but not to modify their code directly, but to add a decorator outside of them. Decorator and the specific component class are inherited abstractcomponent, so it looks like the specific component class, That means we're using decorator as if we were using Concretecomponenta or CONCRETECOMPONENTB. Even those client programs that use Concretecomponenta or concretecomponentb do not know that the classes they use have been changed to decorator, but decorator have modified some of the methods of the specific component class. , the results of executing these methods are different.

(ii) Design of the Multidatasource class

Now back to our question, we need to change the functionality of DataSource, but we don't want to modify any of the code in datasource. I mean the DataSource is all classes that implement the Javax.sql.DataSource interface, which we commonly use to include Apache-provided Org.apache.commons.dbcp.BasicDataSource, Spring provides Org.springframework.jndi.JndiObjectFactoryBean and so on, these classes we can not modify themselves, it is not possible to modify them individually to achieve the function of dynamically allocating data sources, and we want to use DataSource s Essionfactory can't feel such a change at all. Decorator mode is the design pattern to solve this problem.

First write a decorator class, I take the name Multidatasource, through it to dynamically switch the data source. At the same time, in the configuration file, the Sessionfactory DataSource property is changed from a specific datasource to Multidatasource.

Compared to the original decorator mode, Abstractcomponent is an abstract class, but here we can use this abstract class interface instead, that is DataSource interface, and Concretecomponent is those DataSource implementation class , such as Basicdatasource, Jndiobjectfactorybean and so on. The Multidatasource encapsulates the specific datasource and enables dynamic switching of the data source:

Java code
 Public classMultidatasourceImplementsDataSource {PrivateDataSource DataSource =NULL;  PublicMultidatasource (DataSource DataSource) { This. DataSource =DataSource; }       /*(non-javadoc) * @see javax.sql.datasource#getconnection ()*/       PublicConnection getconnection ()throwsSQLException {returnGetdatasource (). getconnection (); }       //other methods that the DataSource interface should implement       PublicDataSource Getdatasource () {return  This. DataSource; }       }        Public voidSetdatasource (DataSource DataSource) { This. DataSource =DataSource; }   }  

When the client makes the request, it puts the datasourcename into request, and then calls the data source name in the request by calling Newmultidatasource (DataSource) When you can tell multidatasource the data source that the customer needs, it can realize the dynamic switch data source. But careful friends will find that this is problematic in singleton cases, because Multidatasource has only one object in the system, and its instance variable datasource has only one, just like a static variable. Because of this, the singleton pattern has forced many design patterns to be changed, which is discussed in detail in my "singleton" changes to our design pattern. So, how do we design in a single-case mode?

(iii) Multidatasource in single-case mode

In singleton mode, since we can call the Multidatasource method every time, DataSource may be different, so we can't put DataSource in the instance variable datasource, The simplest way is to add parameters to the method Getdatasource () and tell Multidatasource which datasource I'm calling:

Java code
 PublicDataSource Getdatasource (String datasourcename) {log.debug ("DataSourceName:" +datasourcename); Try{               if(datasourcename==NULL|| Datasourcename.equals ("")){                   return  This. DataSource; }               return(DataSource) This. Applicationcontext.getbean (DataSourceName); }Catch(Nosuchbeandefinitionexception ex) {Throw NewDaoexception ("There is not the DataSource        }       }   

It is worth mentioning that the data source I need is already registered in Spring's config file, and datasourcename is its corresponding ID.

XML code
<BeanID= "DataSource1"class= "Org.apache.commons.dbcp.BasicDataSource">      < Propertyname= "Driverclassname">          <value>oracle.jdbc.driver.oracledrivervalue> property> ... bean><BeanID= "DataSource2"class= "Org.apache.commons.dbcp.BasicDataSource">      < Propertyname= "Driverclassname">          <value>oracle.jdbc.driver.oracledrivervalue> property> ... bean>

In order to get spring's applicationcontext, The Multidatasource class must implement the interface Org.springframework.context.ApplicationContextAware and implement the method:

Java code
Private NULL ;     Public void throws beansexception {           this. ApplicationContext = applicationcontext;   }    

In this way, I can get DataSource through This.applicationContext.getBean (DataSourceName).

(iv) passing DATASOURCENAME through threads

View the above design, Multidatasource still can't run, because the user when making the request, he needs to connect to what database, its data source name is placed in request, want to send the data source name in request to Multidatasource, You need to go through bus and DAO, that is, all the methods to pass the data source name to Multidatasource,bus and DAO have to add datasourcename parameters, which we do not want to see. It is a good design to write a class that skips the bus and DAO directly to Multidatasource by threading the way:

Java code
 public  class    Spobserver { static  ThreadLocal local = new         ThreadLocal ();  public  static  void   putsp (String sp) {local.set (SP);  public  static   String getsp () { return   (String) Loca       L.get (); }   }  

Make a filter that calls Spobserver every time a client makes a request. petsp (datasourcename) to pass the DataSourceName in request to the Spobserver object. Last modified method of Multidatasource Getdatasource ():

Java code
 Public DataSource Getdatasource () {           = spobserver.getsp ();            return Getdatasource (SP);       }   

The complete Multidatasource code is in the attachment.

(v) Dynamic addition of data sources

With the above scenario, we solved the problem of dynamically allocating data sources, but you might ask: the data sources in the scenario are all configured in spring ApplicationContext, what if I add data sources dynamically while the program is running? This is indeed a problem, and it is also true in our projects. Spring's applicationcontext is loaded when the project is started. After loading, how do we dynamically load new beans into ApplicationContext? I thought it would be nice to solve the problem with spring's own method. Fortunately, after looking at spring's source code, I found this, I wrote the Dynamicloadbean class, just call the Loadbean () method, You can load beans from one or several profiles into ApplicationContext (see attachments). Do not directly load objects through the configuration file, in the spring source code also has, interested friends can study on their own.

(vi) Configuring in spring

After I finished all these designs, I ended up nagging. We should make the following configuration in spring:

XML code
<BeanID= "Dynamicloadbean"class= "Com.htxx.service.dao.DynamicLoadBean">bean><BeanID= "DataSource"class= "Com.htxx.service.dao.MultiDataSource">          < Propertyname= "DataSource">              <refBean= "DataSource1" />property> bean><BeanID= "Sessionfactory"class= "Org.springframework.orm.hibernate3.LocalSessionFactoryBean">          < Propertyname= "DataSource">              <refBean= "DataSource" />property> ... bean>

Where the DataSource property is actually more accurately said to be Defaultdatasource, which is the default data source that should be specified when spring starts and when the customer does not specify a data source.

The benefits of this program

What are the advantages of the above solutions compared to other schemes?

First of all, this solution is entirely in the framework of spring, the data source is still configured in the spring configuration file, Sessionfactory still to configure its DataSource property, it does not even know datasource change. The only difference is the addition of a multidatasource between the real DataSource and the sessionfactory.

Second, the implementation is simple and easy to maintain. This scheme although I said so many things, in fact, are analyzed, really need us to write code only Multidatasource, spobserver two classes. The Multidatasource class really writes only the Getdatasource () and Getdatasource (SP) two methods, and the Spobserver class is simpler. The simpler the implementation, the less likely the error will be, and the higher the maintainability will be.

Finally, this scheme enables single data sources to be compatible with multiple data sources. This scheme does not affect the programming of bus and DAO at all. If our project was developed at the start of a single data source, and as the project progresses and needs to be changed to a multi-data source, you only need to modify the spring configuration and modify the MVC layer a little bit to write the required data source name in the request, and the change is complete. If our project wants to change back to the data source, you simply need to modify the configuration file. In this way, for our project will add more elasticity.

Special Note: Dynamicloadbean in the instance is running in a Web environment with an error, You need to change abstractapplicationcontext in the class to Org.springframework.context.ConfigurableApplicationContext.

Example: Example.rar

Reference article: http://www.iteye.com/topic/72486

How to solve problems with multiple data sources in the spring framework

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.