原文: https://howtodoinjava.com/spring-orm/spring-3-2-5-abstractroutingdatasource-example/

AbstractRoutingDataSourceSpring 框架中非常有用的功能,如果您确实设计允许基于某些条件的多个数据库, 可能会针对每个用户请求更改。 一个例子可以是数据库的使用。 当用户属于某个语言环境时,可以使用特定的数据库;如果用户属于另一个语言环境,则可以切换到另一个语言环境。

根据定义,AbstractRoutingDataSource是一种抽象数据源实现,它基于查找键将getConnection()调用路由到各种目标DataSource之一。 后者通常(但不是必须)通过某些线程绑定的事务上下文来确定。

在这篇文章中,我将提供此功能的示例。 我正在为使用路由数据源,根据用户的语言环境为用户选择适当的数据源。 在此示例中,我仅配置了两个语言环境“en”和“es”。 您可以配置任意多个。 此外,语言环境不仅是更改数据源的唯一标准。 如果您有其他要求,请随时添加自己的逻辑。

还请注意,我使用的是开发的源代码,例如在 Hibernate 4 和 Spring 3.2.5 之间集成

下载源码

生成示例应用程序

步骤 1)扩展AbstractRoutingDataSource

这是必需的,因为在这里您将决定要使用的数据源。

  1. package com.howtodoinjava.controller;
  2. import org.springframework.context.i18n.LocaleContextHolder;
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  4. public class MyRoutingDataSource extends AbstractRoutingDataSource{
  5. @Override
  6. protected Object determineCurrentLookupKey() {
  7. String language = LocaleContextHolder.getLocale().getLanguage();
  8. System.out.println("Language obtained: "+ language);
  9. return language;
  10. }
  11. }

步骤 2)在jdbc.properties中配置两个数据源

这不是必需的,在您的实现中可能不是必需的。

  1. jdbc.databaseurlOne=jdbc:mysql://127.0.0.1:3306/test
  2. jdbc.databaseurlTwo=jdbc:mysql://127.0.0.1:3306/testTwo

步骤 3)在 spring 配置文件中配置多种类型的数据源

首先配置各种数据源,而不必担心如何访问它们。

  1. <bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
  2. destroy-method="close"
  3. p:driverClassName="${jdbc.driverClassName}"
  4. p:username="${jdbc.username}"
  5. p:password="${jdbc.password}" />
  6. <bean id="concreteDataSourceOne"
  7. parent="abstractDataSource"
  8. p:url="${jdbc.databaseurlOne}"/>
  9. <bean id="concreteDataSourceTwo"
  10. parent="abstractDataSource"
  11. p:url="${jdbc.databaseurlTwo}"/>

步骤 4)设置数据源的路由

在这里,您实际上将在上述步骤中为所有配置的数据源定义键值对targetDataSources。 该值将是数据源 bean 名称,而键将是来自MyRoutingDataSource中的defineCurrentLookupKey()方法的结果。

如果对于任何用户请求都找不到任何内容,那么您还可以提及默认数据源。 如果将是默认值,并防止出现异常。

  1. <bean id="dataSource" class="com.howtodoinjava.controller.MyRoutingDataSource">
  2. <property name="targetDataSources">
  3. <map key-type="java.lang.String">
  4. <entry key="en" value-ref="concreteDataSourceOne"/>
  5. <entry key="es" value-ref="concreteDataSourceTwo"/>
  6. </map>
  7. </property>
  8. <!-- <property name="defaultTargetDataSource" ref="concreteDataSourceOne"/> -->
  9. </bean>

步骤 5)现在使用数据源

这简单。 对?

  1. <bean id="sessionFactory"
  2. class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  3. <property name="dataSource" ref="dataSource" /> <!-- HERE -->
  4. <property name="configLocation">
  5. <value>classpath:hibernate.cfg.xml</value>
  6. </property>
  7. <property name="hibernateProperties">
  8. <props>
  9. <prop key="hibernate.dialect">${jdbc.dialect}</prop>
  10. <prop key="hibernate.show_sql">true</prop>
  11. </props>
  12. </property>
  13. </bean>

完成所有更改后,您的 spring 配置将如下所示:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xmlns:context="http://www.springframework.org/schema/context"
  6. xmlns:jee="http://www.springframework.org/schema/jee"
  7. xmlns:lang="http://www.springframework.org/schema/lang"
  8. xmlns:mvc="http://www.springframework.org/schema/mvc"
  9. xmlns:p="http://www.springframework.org/schema/p"
  10. xmlns:tx="http://www.springframework.org/schema/tx"
  11. xmlns:util="http://www.springframework.org/schema/util"
  12. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  13. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
  14. http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
  15. http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
  16. http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
  17. http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
  18. http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  19. http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
  20. <context:annotation-config />
  21. <context:component-scan base-package="com.howtodoinjava.controller" />
  22. <bean id="jspViewResolver"
  23. class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  24. <property name="viewClass"
  25. value="org.springframework.web.servlet.view.JstlView" />
  26. <property name="prefix" value="/WEB-INF/view/" />
  27. <property name="suffix" value=".jsp" />
  28. </bean>
  29. <bean id="propertyConfigurer"
  30. class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
  31. p:location="/WEB-INF/jdbc.properties" />
  32. <!-- Step 3 -->
  33. <bean id="abstractDataSource" class="org.apache.commons.dbcp.BasicDataSource"
  34. destroy-method="close"
  35. p:driverClassName="${jdbc.driverClassName}"
  36. p:username="${jdbc.username}"
  37. p:password="${jdbc.password}" />
  38. <bean id="concreteDataSourceOne"
  39. parent="abstractDataSource"
  40. p:url="${jdbc.databaseurlOne}"/>
  41. <bean id="concreteDataSourceTwo"
  42. parent="abstractDataSource"
  43. p:url="${jdbc.databaseurlTwo}"/>
  44. <!-- Step 4 -->
  45. <bean id="dataSource" class="com.howtodoinjava.controller.MyRoutingDataSource">
  46. <property name="targetDataSources">
  47. <map key-type="java.lang.String">
  48. <entry key="en" value-ref="concreteDataSourceOne"/>
  49. <entry key="es" value-ref="concreteDataSourceTwo"/>
  50. </map>
  51. </property>
  52. <!-- <property name="defaultTargetDataSource" ref="concreteDataSourceOne"/> -->
  53. </bean>
  54. <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
  55. p:basenames="messages" />
  56. <!-- Declare the Interceptor -->
  57. <mvc:interceptors>
  58. <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"
  59. p:paramName="locale" />
  60. </mvc:interceptors>
  61. <!-- Declare the Resolver -->
  62. <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" />
  63. <bean id="sessionFactory"
  64. class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  65. <property name="dataSource" ref="dataSource" />
  66. <property name="configLocation">
  67. <value>classpath:hibernate.cfg.xml</value>
  68. </property>
  69. <property name="hibernateProperties">
  70. <props>
  71. <prop key="hibernate.dialect">${jdbc.dialect}</prop>
  72. <prop key="hibernate.show_sql">true</prop>
  73. </props>
  74. </property>
  75. </bean>
  76. <bean id="employeeDAO" class="com.howtodoinjava.dao.EmployeeDaoImpl"></bean>
  77. <bean id="employeeManager" class="com.howtodoinjava.service.EmployeeManagerImpl"></bean>
  78. <tx:annotation-driven transaction-manager="transactionManager"/>
  79. <bean id="transactionManager"
  80. class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  81. <property name="sessionFactory" ref="sessionFactory" />
  82. </bean>
  83. </beans>

测试应用程序

在测试应用程序之前,我已经在其中创建了两个数据库模式和两个相似的表。 除了其中的数据外,这两个表完全相同。 我有意让它保持不变,以证明两个不同的请求实际上正在访问不同的数据库。

  1. delimiter $$
  2. CREATE DATABASE 'test' /*!40100 DEFAULT CHARACTER SET latin1 */$$
  3. USE test$$
  4. CREATE TABLE 'employee' (
  5. 'ID' int(11) NOT NULL AUTO_INCREMENT,
  6. 'FIRSTNAME' varchar(30) DEFAULT NULL,
  7. 'LASTNAME' varchar(30) DEFAULT NULL,
  8. 'TELEPHONE' varchar(15) DEFAULT NULL,
  9. 'EMAIL' varchar(30) DEFAULT NULL,
  10. 'CREATED' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  11. PRIMARY KEY ('ID')
  12. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1$$
  13. INSERT INTO 'test'.'employee' ('ID','FIRSTNAME','LASTNAME','TELEPHONE','EMAIL','CREATED')
  14. VALUES (4,'Lokesh','Gupta','9811111111','howtodoinjava@gmail.com',CURRENT_TIMESTAMP);
  15. CREATE DATABASE 'testtwo' /*!40100 DEFAULT CHARACTER SET latin1 */$$
  16. USE testtwo$$
  17. CREATE TABLE 'employee' (
  18. 'ID' int(11) NOT NULL AUTO_INCREMENT,
  19. 'FIRSTNAME' varchar(30) DEFAULT NULL,
  20. 'LASTNAME' varchar(30) DEFAULT NULL,
  21. 'TELEPHONE' varchar(15) DEFAULT NULL,
  22. 'EMAIL' varchar(30) DEFAULT NULL,
  23. 'CREATED' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  24. PRIMARY KEY ('ID')
  25. ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1$$
  26. INSERT INTO 'testtwo'.'employee' ('ID','FIRSTNAME','LASTNAME','TELEPHONE','EMAIL','CREATED')
  27. VALUES (1,'Rakesh','Shukla','1234','email',CURRENT_TIMESTAMP);

1)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=zh-CN

Spring 3.2.5 `AbstractRoutingDataSource`示例 - 图1

2)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=es

Spring 3.2.5 `AbstractRoutingDataSource`示例 - 图2

3)点击网址:http://localhost:8080/Spring3.2.5Hibernate4.0.1Integration/?locale=fr

这将导致异常,因为我们既没有为此语言环境设置任何数据源,也没有任何默认数据源。

  1. HTTP Status 500 - Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [fr]
  2. type Exception report
  3. message Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open Hibernate Session for transaction; nested exception is java.lang.IllegalStateException: Cannot determine target DataSource for lookup key [fr]

下载源码

如果需要更多说明,请给我评论。

学习愉快!