spring+mybatis实现数据库读写分离

读写分离是为了减少数据库的负荷,当用户高并发访问时,绝大部分都是用户查询,少部分用户是写入到数据库的。这些我们把数据库拆分成主从两个数据库,主数据库用高性能


服务器承载高并发的用户访问并加redis缓存。在这里我不讲mysql的主从同步配置,大家可以去查下资料,我接下来重点讲怎么动态的给每个sql注入数据源。


首先大家需要先了解AbstractRoutingDataSource这个抽象类,这是spring-jdbc下的一个底层类,当sqlsession去获得一个数据库连接时会调用这个类的determineTargetDataSource()方法获得数据源。

我们先从配置文件开始说起,这是楼主实验时自己写的spring配置文件


    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
   
     //注册handdlemapping
    //引入注解
   // 扫描该包下所有bean
     //引入aspectj切面编程


   
    //定义spring上下文的全局变量即数据库的配置
   
   

   
       //配置一个抽象数据源
   
   
   
   
   
   
   
   
   

   
      //配置一个从数据源即写数据源
     
   

   
       //配置一个主数据源即读数据源
     
   

   
     //核心类,自己写的,需继承AbstractRoutingDataSource,每当获得数据源的时候就                                                                                                                                                                                      会执行DynamicDataSource.determineTargetDataSource()
       
       
       

       
   

   
      // 装配所有的mapping映射文件
         
           
   

    
         //装配所有的map文件
         
           
   
 
    
       //事务管理配置,这是楼主为了测事务配置,大家可以                                                                                                                                                                                                                      不用写
         
   
 
    
       //定义事务管理的属性
         
           
           
           
           
           
           
           
           
       
 
   
 
    
         //定义切面
       
   

    
        //楼主自己测事务的时候想用注解事务,大家可以不用写


jdbc.properties文件

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.writeUrl=jdbc:mysql://***********:3306/数据库名
jdbc.readUrl=jdbc:mysql://**********:3306/数据库名l
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=30
jdbc.maxActive=100
jdbc.maxIdle=100
jdbc.minIdle=5
jdbc.maxWait=1000



首先定义一个枚举类

package com.hbut.inspiration.system;

//用户定义读写两个数据源的key值
public enum DynamicDataSourceGlobal {
     READ,WRITE;
}


定义一个设置数据源的key的类,因为servlet是单例多线程的,所有定义变量的时候需考率多线程安全问题,这里楼主用到了threadlocal,用于把当前线程的用到的数据源的key存储在threadlocal里,大家想要了解threadlocal可以看楼主的 另一条博客http://blog.csdn.net/csdnzhangtao5/article/details/52932275

package com.hbut.inspiration.system;


public class DynamicDataSourceHolder {     //dynamicDataSourceGlobal必须是静态的,禁止实例化多个,只能单例或者直接使用静态方法。大家可以加个恶汉单例模式
    
static ThreadLocal dynamicDataSourceGlobal=new ThreadLocal();

static public  DynamicDataSourceGlobal getDataSource(){
return dynamicDataSourceGlobal.get();
}
    static public void putDataSource(DynamicDataSourceGlobal t){
    dynamicDataSourceGlobal.set(t);
    }
    
    static public void clearDataSource(){
    dynamicDataSourceGlobal.remove();
    }
}

核心类,上面在spring的配置文件里已经配过

public class DynamicDataSource extends AbstractRoutingDataSource{

public Object writeDataSource;
public Object readDataSource;
public Object defaultTargetDataSource;

@Override
protected Object determineCurrentLookupKey() {         //必须实现的类,用于给determineTargetDataSource()提供key值

DynamicDataSourceGlobal targetDataSource=DynamicDataSourceHolder.getDataSource();   //从DynamicDataSourceGlobal得到数据源的key值
if(targetDataSource==null || targetDataSource == DynamicDataSourceGlobal.WRITE){       //默认key值为write
return DynamicDataSourceGlobal.WRITE.name(); 
}else{
return DynamicDataSourceGlobal.READ.name();
}
}

public void afterPropertiesSet(){      //用于在该类所有属性都初始化完成后执行,把我们在spring定义的readdatasource,writedatasource装配到targetDataSources中
                                                         因为determineTargetDataSource()是根据determineCurrentLookupKey()它返回的key值在targetDataSources这个map中去取

                                                                          对应的数据源
setDefaultTargetDataSource(writeDataSource);
Map targetDataSources = new HashMap();
targetDataSources.put(DynamicDataSourceGlobal.READ.name(), readDataSource);
targetDataSources.put(DynamicDataSourceGlobal.WRITE.name(), writeDataSource);
setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}


public Object getWriteDataSource() {
return writeDataSource;
}


public void setWriteDataSource(Object writeDataSource) {
this.writeDataSource = writeDataSource;
}


public Object getReadDataSource() {
return readDataSource;
}


public void setReadDataSource(Object readDataSource) {
this.readDataSource = readDataSource;
}


public Object getDefaultTargetDataSource() {
return defaultTargetDataSource;
}


public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
this.defaultTargetDataSource = defaultTargetDataSource;
}

}


这样一个基本的动态获取数据源的功能就好了


要用的时候只需在每个controller调service的方法前设置就好了,例如


DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.WRITE); //设置数据源为写数据源
mainService.getUserInfo();

或者

DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.READ); //设置数据源为读数据源
mainService.getUserInfo();


你可能感兴趣的:(spring+mybatis实现数据库读写分离)