myBatis多数据源连接
1、首先得有一个Springmvc + Spring + Mybatis maven项目
2、编辑一个扩展AbstractRoutingDataSource类,DynamicDataSource.java重写determineCurrentLookupKey()方法。
3、 封装一个的对数据源进行操作的类,DbContextUtils.java
使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
ThreadLocal的接口方法
ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
① void set(Object value)设置当前线程的线程局部变量的值。
② public Object get()该方法返回当前线程所对应的线程局部变量。
③ public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
④ protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
4、应用spring aop来设置,把配置的数据源类型都设置成为注解标签,在service层中需要切换数据源的方法上,写上注解标签,调用相应方法切换数据源.
4.1、编辑注解标签TargetDataSource.java
注解说明
RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
Documented 注解表明这个注解应该被 javadoc工具记录. 默认情况下,javadoc是不包括注解的. 但如果声明注解时指定了 @Documented,则它会被 javadoc 之类的工具处理, 所以注解类型信息也会被包括在生成的文档中。
@Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
4.2、编辑切面的Bean,AspectTargetDataSource.java
@Pointcut("@annotation(com.tynet.service.TargetDataSource)")
拦截带有@TargetDataSource注解的方法。
@Before(value="setTargetDataSource() && @annotation(dataSource)")
前置任务中dataSource 指的是自定义切面注解上所附带的参数。
5、配置配置文件
我这只显示两个,实际上可以连接很多数据源,只需要增加bean然后把id加入map中,我的默认的数据库是db_plat。
6、记得声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。
7、在方法上加上注解即可使用。
8、实现原理
扩展Spring的AbstractRoutingDataSource抽象类。
该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。
看AbstractRoutingDataSource的源码:
它继承了AbstractDataSource,而AbstractDataSource正是javax.sql.DataSource的子类。
Java中数据库有很多连接方式,在众多方式中除了JDBC外都有DataSource对象。
DataSource可以看作数据源,它封装了数据库参数,连接数据库,程序中操作DataSource对象即可对数据库进行增删改查操作。
分析下AbstractRoutingDataSource实现DataSource的getConnection方法:
看来重点是determineTargetDataSource()方法,上源码:
看注解,检索当前目标的数据源。
重点在于determineCurrentLookupKey()方法。
可以看到如果当前线程变量没有值,它首先被初始化为默认的值。
determineCurrentLookupKey()是AbstractRoutingDataSource类中的一个抽象方法,而它的返回值是当前线程要用的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置文件中设置好后存入的)就从中取出对应的DataSource,如果找不到,就用配置默认的数据源。
而我们上面的所有操作都是要扩展AbstractRoutingDataSource类,并重写其中的determineCurrentLookupKey()方法,来实现数据源的切换。