Spring framework learning [multi-data source configuration]

Source: Internet
Author: User
In our project, we encountered the following problem: our project needs to connect multiple databases, and different customers access different databases as needed during each access. We used to configure a data source in spring and hibernate frameworks. Therefore, the dataSource attribute of sessionFactory always points to this data source and remains unchanged.

In our project, we encountered the following problem: our project needs to connect multiple databases, and different customers access different databases as needed during each access. We used to configure a data source in spring and hibernate frameworks. Therefore, the dataSource attribute of sessionFactory always points to this data source and remains unchanged.

In our project, we encountered the following problem: our project needs to connect multiple databases, and different customers access different databases as needed during each access. We have always configured a data source in spring and hibernate frameworks. Therefore, the dataSource attribute of sessionFactory always points to this data source and remains unchanged. All DAO accesses the database through this data source when using sessionFactory. However, due to project requirements, our DAO has to constantly switch among multiple data sources when accessing sessionFactory. The problem arises: how can sessionFactory dynamically switch different data sources based on customer needs when performing data persistence? Can we solve this problem through a few modifications in the spring framework? Is there any design pattern that can be used?

Problem Analysis

First, I want to configure all dataSource in spring applicationContext. These dataSource may be of different types, such as Oracle, SQL Server, MySQL, or different data sources: for example, org. apache. commons. dbcp. org. springframework. jndi. jndiObjectFactoryBean. SessionFactory then sets the dataSource attribute to different data sources based on each request of the customer to achieve the purpose of switching the data source.

However, I soon found a problem: Resource contention occurs when multiple users access the database concurrently. This is a fault caused by the singleton mode. As we all know, when we use the spring framework, the beans registered in beanFactory are basically in the singleton mode, that is, when spring is started, these beans are loaded into the memory, each bean has only one object in the entire project. Because only one object exists, all the attributes of the object are, more accurately, instance variables, it acts like a static variable (in fact, "static" and "Singleton" are often very similar. We often use "static" to implement "Singleton "). In our case, sessionFactory has only one object in the entire project, and its instance variable dataSource has only one, just like a static variable. If different users constantly modify the value of dataSource, the problem of multiple users competing for a variable will inevitably arise, posing a risk to the system.

Through the above analysis, the key to solving the problem of multi-data source access is to focus on the ability of sessionFactory to dynamically switch the data source based on the customer's needs during data persistence, and solve the problem of resource contention.

Problem solving (1) Adopt the Decorator design mode

To solve this problem, I decided to lock the dataSource. If the dataSource pointed to by sessionFactory can connect to the real data source required by the customer according to the customer's needs, that is, it provides the function of dynamically switching the data source, then the problem is solved. So what should we do? Modify the dataSource source code we want to use? This is obviously not a good solution. We hope that our modifications will be separated from the original dataSource code. Based on the above analysis, using the Decorator mode in the GoF design mode should be the best solution we can choose.

What is the "Decorator mode "? To put it simply, when we need to modify the original functions, but we do not want to directly modify the original code, design a Decorator to be embedded outside the original code. When we use Decorator, it is exactly the same as the original class. Some functions of Decorator have been modified to the functions we need to modify. The structure of the Decorator mode.

We originally needed to modify some functions of all the specific Component classes in the diagram, but instead of directly modifying their code, we added a Decorator to them. Both the Decorator and the specific Component class inherit AbstractComponent, so it looks the same as the specific Component class. That is to say, when we use Decorator, we use ConcreteComponentA or ConcreteComponentB, even client programs that use ConcreteComponentA or ConcreteComponentB do not know that their classes have been changed to Decorator, but Decorator has modified some methods of the specific Component class, the execution results of these methods are different.

(2) design the MultiDataSource class

Now back to our problem, we need to change the dataSource function, but do not want to modify any code in dataSource. The dataSource here refers to all implementations of javax. SQL. dataSource interface class, which is commonly used by org. apache. commons. dbcp. org. springframework. jndi. jndiObjectFactoryBean and so on. These classes cannot be modified, but cannot be modified one by one to implement the function of dynamically allocating data sources. At the same time, we also hope that the sessionFactory of dataSource will not feel this change at all. The Decorator mode is the design mode to solve this problem.

First, write a Decorator class named MultiDataSource to dynamically switch the data source. In the configuration file, change the dataSource attribute of sessionFactory from a specific dataSource to MultiDataSource.

Compared with the original Decorator mode, AbstractComponent is an abstract class, but here we can replace this abstract class with an interface, that is, the DataSource interface, and ConcreteComponent is the implementation class of those DataSource, for example, BasicDataSource and JndiObjectFactoryBean. MultiDataSource encapsulates the specific dataSource and implements Dynamic Data Source switching:

Java code: public class MultiDataSource implements DataSource {private DataSource dataSource = null; public MultiDataSource (DataSource dataSource) {this. dataSource = dataSource;}/* (non-Javadoc) * @ see javax. SQL. dataSource # getConnection () */public Connection getConnection () throws SQLException {return getDataSource (). getConnection ();} // The method that other DataSource interfaces should implement public DataSource getDataSource () {return this. dataSource;} public void setDataSource (DataSource dataSource) {this. dataSource = dataSource ;}}

When sending a request, the customer puts CENAME in the request, and then sends the data source name in the request to the customer's data source by calling the new MultiDataSource (dataSource, you can switch the data source dynamically. But careful friends will find that this is a problem in the case of Singleton, because MultiDataSource has only one object in the system, and its instance variable dataSource also has only one, it is like a static variable. Because of this, the singleton mode makes many design patterns have to be changed, which will be discussed in detail in my Singleton change our design patterns. So how can we design it in the singleton mode?

(3) MultiDataSource in singleton Mode

In Singleton mode, dataSource may be different every time we call the MultiDataSource method, so we cannot place dataSource In the instance variable dataSource, the simplest way is to add a parameter to the getDataSource () method to tell the MultiDataSource which dataSource I actually call:

Java code public DataSource getDataSource (String dataSourceName) {log. debug ("CENAME:" + CENAME); try {if (CENAME = null | CENAME. equals ("") {return this. dataSource;} return (DataSource) this. applicationContext. getBean (pipeline CENAME);} catch (NoSuchBeanDefinitionException ex) {throw new DaoException ("There is not the dataSource }}

It is worth mentioning that all the data sources I need have been registered in the spring configuration file, and the dataSourceName is its corresponding id.

Xml Code Oracle. jdbc. driver. OracleDrivervalue> property>... bean> Oracle. jdbc. driver. OracleDrivervalue> property>... bean>

To obtain spring ApplicationContext, The MultiDataSource class must implement the org. springframework. context. ApplicationContextAware interface and implement the following methods:

Java code private ApplicationContext applicationContext = null; public void setApplicationContext (ApplicationContext applicationContext) throws BeansException {this. applicationContext = applicationContext ;}

In this way, I canThis. ApplicationContext. getBean (dataSourceName) gets dataSource.

(4) passing CENAME through a thread

Looking at the above design, the MultiDataSource still cannot run, because when a user sends a request, what database does he need to connect to, and the data source name is placed in the request, the data source name in the request must be passed to the MultiDataSource, we need to go through BUS and DAO, that is to say, to pass the data source name to MultiDataSource, The dataSourceName parameter must be added to all methods of BUS and DAO, which we do not want to see. It is a good design to write a class and skip BUS and DAO and pass it directly to MultiDataSource through a thread:

Java code 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 ();}}

Make a filter and call SpObserver every time the customer sends a request.PetSp(DataSourceName) to pass the dataSourceName in the request to the SpObserver object. Finally, modify the MultiDataSource method getDataSource ():

Java code public DataSource getDataSource () {String sp = SpObserver. getSp (); return getDataSource (sp );}

The complete MultiDataSource code is included in the attachment.

(5) dynamically add data sources

The above solution solves the problem of dynamic data source allocation, but you may ask: The data sources in the solution are all configured in spring ApplicationContext, what if I dynamically Add a data source while the program is running? This is indeed a problem, and we have encountered it in our project. Spring ApplicationContext is loaded when the project is started. After loading, how can we dynamically load new beans into ApplicationContext? I thought it would be nice to solve this problem using spring's own method. Fortunately, after checking the source code of spring, I found this code and compiled the DynamicLoadBean class. If I call the loadBean () method, you can load the beans in one or more configuration files to ApplicationContext (see the attachment ). Objects are directly loaded without using the configuration file. They are also available in spring source code. Interested friends can study them on their own.

(6) Configure in spring

After completing all these designs, I will end up with another question. We should make the following configuration in spring:

Xml Code Bean> Property> bean> Property>... bean>

The dataSource attribute should actually be defaultDataSource, that is, the default data source that should be specified at spring startup and when the customer does not specify the data source.

Advantages of this solution

What are the advantages of the above scheme over other schemes?

First, this solution is completely implemented under the spring framework. The data source is still configured in the spring configuration file, and sessionFactory still configures its dataSource attribute, it does not even know the dataSource change. The only difference is that a MultiDataSource is added between the real dataSource and sessionFactory.

Secondly, the implementation is simple and easy to maintain. Although I have mentioned so many things, this solution is actually an analysis. The code we really need to write is only MultiDataSource and SpObserver. The MultiDataSource class only needs to write the getDataSource () and getDataSource (sp) methods, while the SpObserver class is simpler. The simpler the implementation, the smaller the possibility of errors, and the higher the maintainability.

Finally, this solution can make a single data source compatible with multiple data sources. This solution does not affect the writing of BUS and DAO at all. If our project is developed with a single data source at the beginning and needs to be changed to multiple data sources as the project progresses, you only need to modify the spring configuration, modify the MVC layer to write the required data source name in the request. If your project wants to change the data source, you only need to modify the configuration file. In this way, more flexibility will be added for our projects.

Note: An error occurs when the DynamicLoadBean In the instance runs in the web environment. You need to change the AbstractApplicationContext in the class to org. springframework. context. ConfigurableApplicationContext.

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.