欢迎光临
这是我的个人博客

mybatis之配置多个数据源

有时候我们会配置多个数据源来减轻数据库的压力,以便服务能正常运行。在此就不介绍数据库读写分离的具体实现

只是在代码方面实现数据库的动态切换,如有感兴趣的小伙伴可以看看数据库中间件mycat来实现数据库的读写分离

首先我们需要新建这几个类,用来实现数据库的动态切换,我们需要创建四个类,如下:

分别用来处理不同的业务需求。

DataSource.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**  
 * RUNTIME  
 * 编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。  
 *  
 */  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD) 
public @interface DataSource
{  String value();
}

DataSourceAspect.java
public class DataSourceAspect
{  /**   * 在dao层方法获取datasource对象之前,在切面中指定当前线程数据源   */  public void before(JoinPoint point)  {  Object target = point.getTarget();  String method = point.getSignature().getName();  Class<?>[] classz = target.getClass().getInterfaces(); // 获取目标类的接口, 所以@DataSource需要写在接口上  Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())  .getMethod().getParameterTypes();  try  {  Method m = classz[0].getMethod(method, parameterTypes);  if (m != null && m.isAnnotationPresent(DataSource.class))  {  DataSource data = m.getAnnotation(DataSource.class);  //System.out.println("用户选择数据库库类型:" + data.value());  HandleDataSource.putDataSource(data.value()); // 数据源放到当前线程中  }  } catch (Exception e)  {  e.printStackTrace();  }  }  }
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource
{  /**   * 获取与数据源相关的key 此key是Map<String,DataSource> resolvedDataSources 中与数据源绑定的key值   * 在通过determineTargetDataSource获取目标数据源时使用   */  @Override  protected Object determineCurrentLookupKey()  {  return HandleDataSource.getDataSource();  }

}
HandleDataSource.java
public class HandleDataSource
{  public static final ThreadLocal<String> holder = new ThreadLocal<String>();  /**   * 绑定当前线程数据源    * @param key   */  public static void putDataSource(String datasource)  {  holder.set(datasource);  }  /**   * 获取当前线程的数据源    * @return   */  public static String getDataSource()  {  return holder.get();  }
}

db.properties
#main database 主库
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://192.168.103.23\:3306/hotel-proof?useUnicode\=true&characterEncoding\=utf-8&useSSL\=false  jdbc.username=root
jdbc.password=123456
jdbc.initialSize=1
jdbc.minIdle=1
jdbc.maxActive=20
jdbc.maxWait=60000
jdbc.removeAbandoned=true
jdbc.removeAbandonedTimeout=180
jdbc.timeBetweenEvictionRunsMillis=60000
jdbc.minEvictableIdleTimeMillis=300000
jdbc.validationQuery=SELECT 1
jdbc.testWhileIdle=true
jdbc.testOnBorrow=false
jdbc.testOnReturn=false

#slave database 从库
slave.jdbc.driverClassName=com.mysql.jdbc.Driver
slave.jdbc.url=jdbc\:mysql\://ip\:端口/数据库?useUnicode\=true&characterEncoding\=utf-8&useSSL\=false
slave.jdbc.username=root
slave.jdbc.password=123456
slave.jdbc.initialSize=1
slave.jdbc.minIdle=1
slave.jdbc.maxActive=20
slave.jdbc.maxWait=60000
slave.jdbc.removeAbandoned=true
slave.jdbc.removeAbandonedTimeout=180
slave.jdbc.timeBetweenEvictionRunsMillis=60000
slave.jdbc.minEvictableIdleTimeMillis=300000
slave.jdbc.validationQuery=SELECT 1
slave.jdbc.testWhileIdle=true
slave.jdbc.testOnBorrow=false
slave.jdbc.testOnReturn=false  
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration  
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
  "http://mybatis.org/dtd/mybatis-3-config.dtd">  
      
<configuration>     <settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <!-- 全局别名设置,在映射文件中只需写别名,而不必写出整个类路径   只要保证此处取的别名与映射文件中用到的别名不区分大小写后相等,那么就能够对应上-->  
    <typeAliases>    
         <package name="com.gainet.entity" />
    </typeAliases>
    <!-- 配置分页插件 -->  <plugins>  <plugin interceptor="com.github.pagehelper.PageHelper">  <!-- 设置数据库类型 Oracle,Mysql,MariaDB,SQLite,Hsqldb,PostgreSQL六种数据库-->        
         <property name="dialect" value="mysql"/>  </plugin>  </plugins>  
</configuration>  

applicationContext-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  xsi:schemaLocation="http://www.springframework.org/schema/beans 
     http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop.xsd
     http://www.springframework.org/schema/tx 
     http://www.springframework.org/schema/tx/spring-tx.xsd">
     
       <bean id="configProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations"> <!-- 加载配置文件的时候一定要这样子写,不能使用  <context:property-placeholder location="classpath:xxx" /> 这样子不能正常加载-->
            <list>
                <value>classpath:db.properties</value>
              </list>
        </property>
    </bean>  <!-- master数据源 -->  <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource">  <!-- 基本属性 url、user、password -->        <property name="driverClassName" value="${jdbc.driverClassName}" />        <property name="url" value="${jdbc.url}" />        <property name="username" value="${jdbc.username}" />        <property name="password" value="${jdbc.password}" />        <property name="initialSize" value="${jdbc.initialSize}" />        <property name="minIdle" value="${jdbc.minIdle}" />         <property name="maxActive" value="${jdbc.maxActive}" />  
     <property name="maxWait" value="${jdbc.maxWait}" />
     <!-- 超过时间限制是否回收 -->
        <property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
        <!-- 超过时间限制多长; -->
        <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
        <!-- 用来检测连接是否有效的sql,要求是一个查询语句-->
        <property name="validationQuery" value="${jdbc.validationQuery}" />
        <!-- 申请连接的时候检测 -->
        <property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
        <!-- 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 -->
        <property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
        <!-- 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能  -->
        <property name="testOnReturn" value="${jdbc.testOnReturn}" />  </bean>  <!-- slave数据源 -->  <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource">      <property name="driverClassName" value="${slave.jdbc.driverClassName}" />        <property name="url" value="${slave.jdbc.url}" />        <property name="username" value="${slave.jdbc.username}" />        <property name="password" value="${slave.jdbc.password}" />        <property name="initialSize" value="${slave.jdbc.initialSize}" />        <property name="minIdle" value="${slave.jdbc.minIdle}" />         <property name="maxActive" value="${slave.jdbc.maxActive}" />  
     <property name="maxWait" value="${slave.jdbc.maxWait}" />
        <property name="removeAbandoned" value="${slave.jdbc.removeAbandoned}" />
        <property name="removeAbandonedTimeout" value="${slave.jdbc.removeAbandonedTimeout}" />
        <property name="timeBetweenEvictionRunsMillis" value="${slave.jdbc.timeBetweenEvictionRunsMillis}" />
        <property name="minEvictableIdleTimeMillis" value="${slave.jdbc.minEvictableIdleTimeMillis}" />
        <property name="validationQuery" value="${slave.jdbc.validationQuery}" />
        <property name="testWhileIdle" value="${slave.jdbc.testWhileIdle}" />
        <property name="testOnBorrow" value="${slave.jdbc.testOnBorrow}" />
        <property name="testOnReturn" value="${slave.jdbc.testOnReturn}" />  </bean>    <!-- 动态数据源,根据service接口上的注解来决定取哪个数据源 -->  <bean id="dataSource" class="com.gainet.utils.database.DynamicDataSource">  
        <property name="targetDataSources">      
          <map key-type="java.lang.String">  
          <!-- read or master   -->  
             <entry key="master" value-ref="masterDataSource"/>    
              <!-- write or slave -->    
             <entry key="slave" value-ref="slaveDataSource"/>      
                   
          </map>               
        </property>   
        <property name="defaultTargetDataSource" ref="masterDataSource"/>      
      
    </bean>    <!-- Mybatis文件 -->  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  <property name="configLocation" value="classpath:mybatis/mybatis-config.xml" />   <property name="dataSource" ref="dataSource" />  <!-- 映射文件路径 -->  <property name="mapperLocations" value="classpath*:dbmappers/*.xml" />  </bean>  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  <property name="basePackage" value="com.gainet.mapper" />  <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />  </bean>  <!-- 事务管理器-->  <bean id="transactionManager"  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  <property name="dataSource" ref="dataSource" />  </bean>  <!-- 声明式开启 -->  <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="1"/>    <!-- 为业务逻辑层的方法解析@DataSource注解  为当前线程的HandleDataSource注入数据源 -->   
    <bean id="dataSourceAspect" class="com.gainet.utils.database.DataSourceAspect" />    
    <aop:config proxy-target-class="true">    
        <aop:aspect id="dataSourceAspect" ref="dataSourceAspect" order="2">    
            <aop:pointcut id="tx" expression="execution(* com.service.impl..*.*(..)) "/>    
            <aop:before pointcut-ref="tx" method="before" />                
        </aop:aspect>    
    </aop:config>
</beans>  

怎样使用呢?

我们只需要把注解 @DataSource(“slave”) 打在接口上即可,一定要在接口上,不然不起作用。

怎样使用事务呢?只需要在对应的实现类上打上 @Transactional 即可

赞(0) 打赏
未经允许不得转载:好好网 » mybatis之配置多个数据源

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏