原文出处:http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
Spring 2.0.1 introduced an AbstractRoutingDataSource. I believe that it deserves attention, since (based on frequent questions from clients) I have a hunch that there are quite a few 'home-grown' solutions to this problem floating around. That combined with the fact that it is trivial to implement yet easy to overlook, and now I have several reasons to dust off my corner of the Interface21 team blog.
The general idea is that a routing DataSource acts as an intermediary – while the 'real' DataSource can be determined dynamically at runtime based upon a lookup key. One potential use-case is for ensuring transaction-specific isolation levels which are not supported by standard JTA. For that, Spring provides an implementation: IsolationLevelDataSourceRouter. Consult its JavaDoc for a detailed description including configuration examples. Another interesting use-case is determination of the DataSource based on some attribute of the current user's context. What follows is a rather contrived example to demonstrate this idea.
First, I created a Catalog that extends Spring 2.0's SimpleJdbcDaoSupport. That base class only requires an instance of any implementation of javax.sql.DataSource, and then it creates a SimpleJdbcTemplate for you. Since it extends JdbcDaoSupport, the JdbcTemplate is also available. However, the "simple" version provides many nice Java 5 conveniences. You can read more detail about that in this blog by Ben Hale.
Anyways, here's the code for my Catalog:
01 |
package blog.datasource;
|
03 |
import java.sql.ResultSet;
|
04 |
import java.sql.SQLException;
|
05 |
import java.util.List;
|
07 |
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
|
08 |
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
|
10 |
public class Catalog extends SimpleJdbcDaoSupport {
|
12 |
public List<Item> getItems() {
|
13 |
String query = "select name, price from item" ;
|
14 |
return getSimpleJdbcTemplate().query(query, new ParameterizedRowMapper<Item>() {
|
15 |
public Item mapRow(ResultSet rs, int row) throws SQLException {
|
16 |
String name = rs.getString( 1 );
|
17 |
double price = rs.getDouble( 2 );
|
18 |
return new Item(name, price);
|
As you can see, the Catalog simply returns a list of Item objects. The Item just contains name and price properties:
01 |
package blog.datasource;
|
08 |
public Item(String name, double price) {
|
13 |
public String getName() {
|
17 |
public double getPrice() {
|
21 |
public String toString() {
|
22 |
return name + " (" + price + ")" ;
|
Now, in order to demonstrate multiple DataSources, I created an enum for different Customer types (representing "levels" of membership I guess), and I created three different databases – so that each type of customer would get a distinct item list (I did mention that this would be a contrived example didn't I?). The important thing is that each of the databases are equivalent in terms of the schema. That way the Catalog's query will work against any of them – just returning different results. In this case, it's just the "item" table with 2 columns: name and price. And… here is the enum:
1 |
public enum CustomerType {
|
It's time to create some bean definitions. Since I have 3 datasources where everything is the same except for the port number, I created a parent bean so that the shared properties can be inherited. Then, I added the 3 bean definitions to represent the per-CustomerType DataSources:
01 |
< bean id = "parentDataSource"
|
02 |
class = "org.springframework.jdbc.datasource.DriverManagerDataSource"
|
04 |
< property name = "driverClassName" value = "org.hsqldb.jdbcDriver" />
|
05 |
< property name = "username" value = "sa" />
|
08 |
< bean id = "goldDataSource" parent = "parentDataSource" >
|
12 |
< bean id = "silverDataSource" parent = "parentDataSource" >
|
13 |
< property name = "url" value = "jdbc:hsqldb:hsql://localhost:${db.port.silver}/blog" />
|
16 |
< bean id = "bronzeDataSource" parent = "parentDataSource" >
|
17 |
< property name = "url" value = "jdbc:hsqldb:hsql://localhost:${db.port.bronze}/blog" />
|
20 |
< bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >
|
21 |
< property name = "location" value = "classpath:/blog/datasource/db.properties" />
|
Notice that I added a PropertyPlaceholderConfigurer so that I could externalize the port numbers in a "db.properties" file, like so:
Now things start to get interesting. I need to supply the "routing" DataSource to my Catalog so that it can dynamically get connections from the 3 different databases at runtime based on the current customer's type. As I mentioned, the AbstractRoutingDataSource can be rather simple to implement. Here is my implementation:
01 |
package blog.datasource;
|
03 |
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
05 |
public class CustomerRoutingDataSource extends AbstractRoutingDataSource {
|
08 |
protected Object determineCurrentLookupKey() {
|
09 |
return CustomerContextHolder.getCustomerType();
|
…and the CustomerContextHolder simply provides access to a thread-bound CustomerType. In reality, the 'context' would likely hold more information about the customer. Also note that if you are using Acegi, then you could retrieve some information from the userDetails. For this example, it's just the customer "type":
01 |
public class CustomerContextHolder {
|
03 |
private static final ThreadLocal<CustomerType> contextHolder =
|
04 |
new ThreadLocal<CustomerType>();
|
06 |
public static void setCustomerType(CustomerType customerType) {
|
07 |
Assert.notNull(customerType, "customerType cannot be null" );
|
08 |
contextHolder.set(customerType);
|
11 |
public static CustomerType getCustomerType() {
|
12 |
return (CustomerType) contextHolder.get();
|
15 |
public static void clearCustomerType() {
|
16 |
contextHolder.remove();
|
Finally, I just need to configure the catalog and routing DataSource beans. As you can see, the "real" DataSource references are provided in a Map. If you provide Strings, they can be resolved as JNDI names (or any custom resolution strategy can be provided – see the JavaDoc). Also, I've simply set the 'bronzeDataSource' as the default:
01 |
< bean id = "catalog" class = "blog.datasource.Catalog" >
|
02 |
< property name = "dataSource" ref = "dataSource" />
|
05 |
< bean id = "dataSource" class = "blog.datasource.CustomerRoutingDataSource" >
|
06 |
< property name = "targetDataSources" >
|
07 |
< map key-type = "blog.datasource.CustomerType" >
|
08 |
< entry key = "GOLD" value-ref = "goldDataSource" />
|
09 |
< entry key = "SILVER" value-ref = "silverDataSource" />
|
12 |
< property name = "defaultTargetDataSource" ref = "bronzeDataSource" />
|
Of course I'd like to see this working, so I've created a simple test (extending one of Spring's integration test support classes). I added 3 items to the "gold" database, 2 items to the "silver" database, and only 1 item to the "bronze" database. This is the test:
public class CatalogTests extends AbstractDependencyInjectionSpringContextTests {
|
03 |
private Catalog catalog;
|
05 |
public void setCatalog(Catalog catalog) {
|
06 |
this .catalog = catalog;
|
09 |
public void testDataSourceRouting() {
|
10 |
CustomerContextHolder.setCustomerType(CustomerType.GOLD);
|
11 |
List<Item> goldItems = catalog.getItems();
|
12 |
assertEquals( 3 , goldItems.size());
|
13 |
System.out.println( "gold items: " + goldItems);
|
15 |
CustomerContextHolder.setCustomerType(CustomerType.SILVER);
|
16 |
List<Item> silverItems = catalog.getItems();
|
17 |
assertEquals( 2 , silverItems.size());
|
18 |
System.out.println( "silver items: " + silverItems);
|
20 |
CustomerContextHolder.clearCustomerType();
|
21 |
List<Item> bronzeItems = catalog.getItems();
|
22 |
assertEquals( 1 , bronzeItems.size());
|
23 |
System.out.println( "bronze items: " + bronzeItems);
|
26 |
protected String[] getConfigLocations() {
|
27 |
return new String[] { "/blog/datasource/beans.xml" };
|
…and rather than simply taking a screenshot of the green bar, you'll notice I've provided some console output – the results!:
1 |
gold items: [gold item # 1 ( 250.0 ), gold item # 2 ( 325.45 ), gold item # 3 ( 55.6 )]
|
2 |
silver items: [silver item # 1 ( 25.0 ), silver item # 2 ( 15.3 )]
|
3 |
bronze items: [bronze item # 1 ( 23.75 )]
|
As you can see, the configuration is simple. Better still, the data-access code is not concerned with looking up different DataSources. For more information, consult the JavaDoc for AbstractRoutingDataSource.
分享到:
相关推荐
在Spring框架中,数据库连接池的管理是一项关键任务,特别是在多数据库环境下的应用。"Spring动态切换datasource实现mysql多主多从"是针对大型分布式系统中数据读写分离、负载均衡的一种常见策略。该技术使得应用...
在Spring Boot中,我们通常通过自动配置和依赖注入来轻松地管理`DataSource`。 首先,我们需要理解`bean`这个概念。在Spring框架中,`bean`是一个由Spring IoC(Inversion of Control,控制反转)容器管理的对象。...
通过阅读《学习Spring笔记_DataSource》这篇博文,可以深入理解Spring框架如何管理和使用DataSource,以及在实际项目中如何配置和优化,为构建高效稳定的数据库应用打下坚实基础。在实践中,不断探索和学习,掌握更...
在Spring框架中,`dataSource`是用于管理数据库连接的核心组件,它是`DataSource`接口的一个实现,通常由Apache的`DBCP`、`C3P0`或HikariCP等库提供。`dataSource`的配置和与IOC(Inversion of Control,控制反转)...
通过Spring的配置,我们可以轻松地管理多个数据源,并根据业务逻辑进行动态切换。这通常涉及到使用`@Qualifier`注解来指定特定的数据源。 2. **Druid数据源** Druid是一个高性能、监控和扩展性极强的数据库连接池...
"oesoft_spring_datasource" 提示我们关注的是Spring框架如何处理数据源(DataSource)的配置和管理。数据源是连接数据库的关键组件,它负责管理数据库连接,提供高效的连接池功能,以支持多个并发请求。在Spring...
总的来说,`dynamic-datasource-spring-boot-starter`是一个强大的工具,能够帮助开发者高效地管理和使用多数据源,提高系统的可扩展性和可用性。如果你正在开发一个需要处理多个数据库的应用,那么这个项目将是一个...
在现代企业级应用程序开发中,数据源管理是一个关键部分,特别是在需要处理来自多个数据库的数据时。Spring Boot结合JPA(Java Persistence API)和JdbcTemplate,为开发者提供了灵活且强大的多数据源配置能力。本...
3. **Spring框架的支持**: 在Java应用中,Spring框架提供了强大的数据源管理能力,包括对多种数据源的支持和动态切换的功能。通过Spring的`AbstractRoutingDataSource`,我们可以自定义规则来决定在运行时应该使用...
在IT行业中,Spring框架是Java企业级应用开发的首选,而WebLogic则是一款知名的Java EE应用服务器,常用于部署和管理企业级应用。本篇将深入讲解如何在WebLogic服务器中配置Spring的数据源(Datasource),以便应用...
在Spring Boot应用中,我们经常需要处理来自多个数据源的数据,比如在开发微服务架构时,每个服务可能对应一个特定的数据源。`JdbcTemplate`是Spring框架提供的一个用于简化数据库操作的工具,它使得数据库访问更为...
在Spring Boot应用中,多数据源配置是一项关键的技术,它允许我们同时管理多个数据库,比如主库和从库,或者不同类型的数据库。本教程将详细阐述如何在Spring Boot项目中实现这一功能,从数据源配置、实体管理到...
在Spring Boot中,多数据源配置意味着应用程序需要连接并管理多个数据库。这通常涉及以下几个步骤: 1. **引入依赖**:在`pom.xml`或`build.gradle`文件中,添加Spring Boot的MyBatis starter以及额外的数据源依赖...
在Spring框架中,数据源(DataSource)是连接数据库的关键组件,它管理数据库连接的创建、分配和释放。DBCP(Basic Database Connection Pool)是Apache提供的一款开源的数据库连接池实现,它为Java应用提供了高效的...
标题中的“Dynamic datasource”指的是动态数据源,这是一个在软件开发中用于管理多个数据库连接的技术。在Spring Boot框架中,动态数据源允许应用根据业务需求在运行时切换不同的数据源,提高了系统的灵活性和可...
动态数据源启动器(Dynamic DataSource Starter)是为Spring Boot框架设计的一个组件,用于简化多数据源配置和管理。在这个v4.3.0版本的压缩包中,包含了一个说明文档(说明.htm)以及动态数据源启动器的源代码...
在Spring Boot应用中,多数据源的配置与管理是一项重要的任务,特别是在大型系统中,可能需要连接到不同的数据库以满足不同业务的需求。本教程将详细讲解如何在Spring Boot项目中集成Druid连接池,并利用AOP注解实现...
总结来说,"深度解析spring容器管理bean"涵盖了Spring如何使用DOM4J解析XML,通过反射创建和管理Bean,实现依赖注入,以及Bean的生命周期管理等多个关键知识点。理解这些概念对于深入掌握Spring框架和构建高质量的...
Spring Cloud Config是一款基于分布式配置中心的解决方案,它可以帮助我们管理和维护微服务架构中的配置信息。今天,我们将讨论如何使用Spring Cloud Config实现Datasource的热部署。 什么是热部署 热部署是指在不...
这个压缩包"dynamic-datasource-spring-boot-starter-master.zip"提供了一个快速启动的解决方案,使得开发者能够便捷地在SpringBoot应用中配置和管理多个数据源。 首先,我们要理解什么是SpringBoot。SpringBoot是...