spring mvc中使用动态数据源

来源:互联网 发布:返利网淘宝返利比例 编辑:程序博客网 时间:2024/05/22 00:07

实际应用中有这么个需求:根据用户所属的租户,来切换到租户对应的数据库(一个租户一套数据库,数据表都一样,存放的schema不同而已)

下面是配置的代码

1.动态数据源的切换,使用spring提供的AbstractRoutingDataSource接口,实现determineCurrentLookupKey()方法

public class DynamicDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static String getCurrentLookupKey() {return (String) contextHolder.get();}public static void setCurrentLookupKey(String currentLookupKey) {contextHolder.set(currentLookupKey);}@Overrideprotected Object determineCurrentLookupKey() {return getCurrentLookupKey();}}

2.在applicationContext.xml中为每个租户写一个对应的数据源

<bean id="baseDataSource"class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driver}" /><property name="url" value="${jdbc.urla}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><bean id="aDataSource" parent="baseDataSource"><property name="url" value="${jdbc.urla}" /></bean><bean id="bDataSource" parent="baseDataSource"><property name="url" value="${jdbc.urlb}" /></bean><bean id="dataSource" class="com.baosight.iframework.common.DynamicDataSource"><property name="targetDataSources"><map key-type="java.lang.String"><entry key="a" value-ref="aDataSource" /><entry key="b" value-ref="bDataSource" /></map></property><property name="defaultTargetDataSource" ref="aDataSource" /> </bean>
配置中使用了属性继承,因为唯一变动的只有url连接,dataSource具体使用哪个是根据传来的key来决定,下面我们看下如何传key


3.调用数据访问层前,都先传dataSource的key

public class DynamicDataSourceInterceptor {public void setdataSource(JoinPoint jp) {HttpSession s = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();String t = (String) s.getAttribute("tenant");if (t.equals("tenant1")) {DynamicDataSource.setCurrentLookupKey("a");}if (t.equals("tenant2")) {DynamicDataSource.setCurrentLookupKey("b");}}}

上述的代码就是指定key的过程,因为用户的信息保存在session中,所以使用了session,下面的工作就是把上述的代码织入到service层的所有方法


    <aop:config>          <aop:aspect id="dsAspect" ref="dsInterceptor">              <aop:pointcut id="businessService"                  expression="execution(* com.jacky.service.*.*(..))" />              <aop:before pointcut-ref="businessService" method="setdataSource"/>          </aop:aspect>      </aop:config>            <bean id="dsInterceptor" class="com.jacky.DynamicDataSourceInterceptor" />  
aop的作用就是每次调用service的方法前都先切换数据库


这样就完成了。我刚开始把aop切面放到controller层没有效果,要放到service层才行。

0 0