【问题标题】:How to use Spring AbstractRoutingDataSource with dynamic datasources?如何将 Spring AbstractRoutingDataSource 与动态数据源一起使用?
【发布时间】:2014-11-28 08:44:07
【问题描述】:

我正在一个使用 Spring、Spring Data JPA、Spring Security、Primefaces 的项目中工作......

我正在关注 this tutorial 使用 spring 进行动态数据源路由。

在本教程中,您只能在预定义的数据源之间实现动态数据源切换。

这是我的代码的 sn-p :

springContext-jpa.xml

<bean id="dsCgWeb1" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName.Cargest_web}"></property>
    <property name="url" value="${jdbc.url.Cargest_web}"></property>
    <property name="username" value="${jdbc.username.Cargest_web}"></property>
    <property name="password" value="${jdbc.password.Cargest_web}"></property>
</bean>

<bean id="dsCgWeb2" class="org.apache.commons.dbcp.BasicDataSource">
    // same properties, different values ..
</bean>

<!--  Generic Datasource [Default : dsCargestWeb1]  -->
<bean id="dsCgWeb" class="com.cargest.custom.CargestRoutingDataSource">
    <property name="targetDataSources">
        <map>
            <entry key="1" value-ref="dsCgWeb1" />
            <entry key="2" value-ref="dsCgWeb2" />
        </map>
    </property>
    <property name="defaultTargetDataSource" ref="dsCgWeb1" />
</bean>

我想要做的是使 targetDataSources 地图也与其元素动态相同。

换句话说,我想获取某个数据库表,使用存储在该表中的属性来创建我的数据源,然后将它们放在像 targetDataSources 这样的地图中。

有没有办法做到这一点?

【问题讨论】:

    标签: spring jpa spring-security spring-data-jpa


    【解决方案1】:

    AbstractRoutingDataSource 中的任何内容都不会强制您使用DataSourceS 的静态映射。由您来构建实现Map&lt;Object, Object&gt; 的bean,其中key 是您用来选择DataSource 的内容,value 是DataSource 或(默认情况下)引用JNDI 定义的数据源的字符串。您甚至可以动态修改它,因为地图存储在内存中,AbstractRoutingDataSource 不进行缓存。

    我没有完整的示例代码。但这是我能想象的。在 Web 应用程序中,每个客户端都有一个数据库,所有数据库都具有相同的结构 - 好吧,这将是一个奇怪的设计,假设它只是用于示例。在登录时,应用程序为客户端创建数据源并将其存储在由 sessionId 索引的映射中 - 该映射是根上下文中名为 dataSources 的 bean

    @Autowired
    @Qualifier("dataSources");
    Map<String, DataSource> sources;
    
    // I assume url, user and password have been found from connected user
    // I use DriverManagerDataSource for the example because it is simple to setup
    DataSource dataSource = new DriverManagerDataSource(url, user, password);
    sources.put(request.getSession.getId(), dataSource);
    

    您还需要一个会话侦听器在其destroy 方法中清理dataSources

    @Autowired
    @Qualifier("dataSources");
    Map<String, DataSource> sources;
    
    public void sessionDestroyed(HttpSessionEvent se)  {
        // eventually cleanup the DataSource if appropriate (nothing to do for DriverManagerDataSource ...)
        sources.remove(se.getSession.getId());
    }
    

    路由数据源可能是这样的:

    public class SessionRoutingDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            HttpServletRequest request = ((ServletRequestAttributes)
                    RequestContextHolder.getRequestAttributes()).getRequest();
            return request.getSession().getId();
        }
    
        @Autowired
        @Qualifier("dataSources")
        public void setDataSources(Map<String, DataSource> dataSources) {
            setTargetDataSources(dataSources);
    }
    

    我没有测试任何东西,因为设置不同的数据库需要做很多工作,但我认为应该没问题。在现实世界中,每个会话不会有不同的数据源,而是每个用户一个,每个用户有一个会话计数,但正如我所说,这是一个过于简化的示例。

    【讨论】:

      【解决方案2】:

      线程使用的数据源可能会不时更改。
      注意并发,应用在并发环境下可能会出现并发问题。
      thread-bound AbstractRoutingDataSource sample

      【讨论】:

        【解决方案3】:

        可以通过 AbstractRoutingDataSource 实现,并将信息保存在线程局部变量中。这是一个漂亮的工作示例,您可以参考: Multi-tenancy: Managing multiple datasources with Spring Data JPA

        【讨论】:

          猜你喜欢
          • 2014-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-09
          • 1970-01-01
          • 2011-11-22
          • 2016-07-28
          相关资源
          最近更新 更多