mybatis 实现接口和xml的绑定。sql的执行等一系列处理。当我们在使用过程中需要从A服务器数据库查询某张表。B服务器数据库查询另一张表又该怎么做呢?
实现思路
1、配置多个dataSource。
2、设置主从关系
3、定义切面监控所有 mybatis接口。。
4、利用@around注解完成对执行接口类方法的数据源切换。
5、写工具类完成切换
第一步、applicationContext.xml 的基本配置、及mybatis的注册配置
1、我将 jdbc配置文件写在properties 文件内。方便后期管理配置。。采用 库名.Jdbc.字段名的形式。。。 其实怎么弄都可以。。只要自己能看懂。。不引入出错即可。
至于${} 怎么用请自行百度。
<!-- 引入配置文件 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:spring/properties/jdbc.properties"/> </bean> <!--配置A库数据源--> <bean id="A" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${a.jdbc.driver}"/> <property name="url" value="${a.jdbc.url}"/> <property name="username" value="${a.jdbc.username}"/> <property name="password" value="${a.jdbc.password}"/> <!-- 初始化连接大小 --> <property name="initialSize" value="${a.jdbc.initialSize}"/> <!-- 连接池最大数量 --> <property name="maxActive" value="${a.jdbc.maxActive}"/> <!-- 连接池最大空闲 --> <property name="maxIdle" value="${a.jdbc.maxIdle}"/> <!-- 连接池最小空闲 --> <property name="minIdle" value="${a.jdbc.minIdle}"/> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="${a.jdbc.maxWait}"/> </bean> <!--配置B库数据源--> <bean id="B" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${b.jdbc.driver}"/> <property name="url" value="${b.jdbc.url}"/> <property name="username" value="${b.jdbc.username}"/> <property name="password" value="${b.jdbc.password}"/> <!-- 初始化连接大小 --> <property name="initialSize" value="${b.jdbc.initialSize}"/> <!-- 连接池最大数量 --> <property name="maxActive" value="${b.jdbc.maxActive}"/> <!-- 连接池最大空闲 --> <property name="maxIdle" value="${b.jdbc.maxIdle}"/> <!-- 连接池最小空闲 --> <property name="minIdle" value="${b.jdbc.minIdle}"/> <!-- 获取连接最大等待时间 --> <property name="maxWait" value="${b.jdbc.maxWait}"/> </bean>
利用dataSource 设置主从 对数据库进行引用。
<bean id="dataSource" class="self.yzx.util.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <entry value-ref="B" key="B"></entry> </map> </property> <property name="defaultTargetDataSource" ref="A"></property> <!-- 默认使用A的数据源 --> </bean>
<!--基于注解的事务管理--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--引入dataSource--> <property name="dataSource" ref="dataSource"/> <!--引入mybatis配置文件--> <property name="configLocation" value="classpath:spring/mybatis-config.xml"/> <!--下面这句话可以使用可以不使用。如果不使用 默认的xml 寻找模式为对应的接口目录下。即接口和xml必须名称统一,且在同一目录 此文采用是单独放置其他位置则实现如下配置即可--> <property name="mapperLocations" value="classpath:self/yzx/xml/*.xml"/> </bean> <!-- 扫描mybatis映射接口类 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="self"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
第二步、继承abstractroutingDataSource 重写determineCurrentLookUpkey方法
package self.yzx.util; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDbType(); } }
第三步、写切换方法
package self.yzx.util; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDbType(String dbType) { contextHolder.set(dbType); } public static String getDbType() { return ((String) contextHolder.get()); } public static void clearDbType() { contextHolder.remove(); } }第四步、在接口上添加声明注解
唯独需要修改的就是 接口。。需要 加上注解方便 @around 时候进行判断、这里最好是 直接 定义成 在xml文件里 配置的 数据源名称。。
package self.yzx.itface; import org.aspectj.lang.annotation.DeclareAnnotation; import java.util.List; import java.util.Map; public interface svlt02 { @DeclareAnnotation("A") public List<Map<String, Object>> getLog(String msg_ids); @DeclareAnnotation("B") public List<Map<String, Object>> getCheck(String userName); }第五步、利用aop编程实现对接口的监听
execution(* self.yzx.itface.*.*(..))
第一个*代表接受任意形式的返回值
self.yzx.itface 是包。。我把所有的接口当放在 这个目录下
第二个*代表的是接口名称
第三个*代表的是接口方法
括号里的两个..代表的意思是接受任何形式的参数
及 整句话意思是 self.yzx.itface 目录下 所有 接口 里的任何一个方法 都会被 监听到
返回的obj 就是 mybatis 执行的结果。。
@Aspect @Component public class swithDataSourceAspectJ { @Around("execution(* self.yzx.itface.*.*(..))") public Object process(ProceedingJoinPoint point) throws Throwable { //获得目标方法标签 MethodSignature methodSignature = (MethodSignature) point.getSignature(); //获得目标方法标签里的值 String sigName = methodSignature.getMethod().getAnnotation(DeclareAnnotation.class).value(); //切换dataSource DataSourceContextHolder.setDbType(sigName); //用改变后的参数执行目标方法 Object obj = point.proceed(); //清空dataSource 方便下次拦截的时候传入 DataSourceContextHolder.clearDbType(); return obj; } }