多数据源配置-使用spring配置多个数据源实现读写分离

news/2024/7/5 19:29:04

我们在很多的项目中经常会有用到多个数据源。比如数据库读写分离,读操作都去从库里读,写操作都往主库里写。那么这里主库和从库就是两个不同的数据源。再比如要做两个数据库之间的数据转换,从一个数据库读取数据写到另一个数据库中,等等这些情况都需要系统使用两个或多个数据源。那么该如何配置多个数据源呢?我这里使用的是spring管理,数据库连接池使用的是阿里的druid。具体步骤如下:
1.修改spring的配置文件, 配置多个不同的数据源。这里不同的数据源就是由spring维护多个DruidDataSource的实例,每个实例都指定不同的数据库链接信息。配置如下:
     <!-- 配置多数据源 ,druid -->
    <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">

        <property name="url" value="${jdbc.master.url}" />
        <property name="username" value="${jdbc.master.username}" />
        <property name="password" value="${jdbc.master.password}" />
        <property name="driverClassName" value="${jdbc.master.driver}" />

        <!-- 最大连接池数量 -->
        <property name="maxActive" value="${master.maxActive}" />
        <!-- 获取连接时最大等待时间,单位毫秒。 配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000" />

        <!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 -->
        <property name="testWhileIdle" value="true"></property>

        <property name="filters" value="stat"></property>
    </bean>

    <!-- 配置多数据源 ,druid -->
    <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close">

        <property name="url" value="${jdbc.slave1.url}" />
        <property name="username" value="${jdbc.slave1.username}" />
        <property name="password" value="${jdbc.slave1.password}" />
        <property name="driverClassName" value="${jdbc.slave1.driver}" />

        <!-- 最大连接池数量 -->
        <property name="maxActive" value="${slave1.maxActive}" />
        <!-- 获取连接时最大等待时间,单位毫秒。 配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000" />

        <!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 -->
        <property name="testWhileIdle" value="true"></property>

        <property name="filters" value="stat"></property>
    </bean>

我这里主要配置了两个数据源,一个是主库的数据源,名字叫masterDataSource,还有一个是从库的数据源,名字叫slaveDataSource

2.选择要使用的数据源。我们有了数据源之后我们要使用哪一个呢,所以这个时候我们需要来定义一个类,指定选择哪一个数据源,这里也很简单,只要继承spring给我们提供的AbstractRoutingDataSource类就可以了,这是一个抽象类,需要重写determineCurrentLookupKey方法,这个方法就是用来选择哪一个数据源的。
我们先给两个数据源分别打个标记,比如,读库的源就叫slave,写库的叫master,在实现这个方法的时候,我们返回一个字符串'slave' 表示要找读库,返回一个master表示要找主库。既然要动态的返回,我们就需要有一个入口能够设置选择的标记。在多线程环境下,为了保证数据安全,可以使用ThreadLocal这个类。代码如下

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDataSource();
    }

}

DbContextHolder的代码如下
public class DbContextHolder {

    public static final String master = "master";

    public static final String slave = "slave";

    private static ThreadLocal<String> ds = new ThreadLocal<>();

    public static void setDataSource (String dataSource) {
        ds.set(dataSource);
    }

    public static String getDataSource() {
        return ds.get();
    }

}

3.继续配置文件,不管我们配置了几个数据源,同一时刻生效的只有一个,于是我们要通过DynamicDataSource这个类来告诉spring我们要使用哪个数据源
    <bean id="dataSource" class="com.hy.base.db.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <!-- write -->
                <entry key="master" value-ref="masterDataSource" />
                <!-- read -->
                <entry key="slave" value-ref="slaveDataSource" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="masterDataSource" />
    </bean>

这里要注意,key就是我们上面说到的标记,需要和DbContextHolder里的常量配置的相同。
到这一步,我们就可以说配置完成了,代码中就可以指定数据源,来进行数据库操作。当然我们为了更加方便,我们可以使用注解的方式来选择使用哪一个数据源,首先定义注解类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    String value();
}

我们希望呢,在方法上打上这个注解,当这个方法执行前,我们就调用setDataSource方法来设置注解中指定的值,这样也就不需要手动设置了。下面是定义切面

public class DataSourceAspect {

    public void before(JoinPoint point)
    {
//        System.out.println("--------------------------");
        Object target = point.getTarget();
        String method = point.getSignature().getName();

        Class<?>[] classz = target.getClass().getInterfaces();

        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);
                DbContextHolder.setDataSource(data.value());
                System.out.println(data.value());
            }

        } catch (Exception e) {
//            e.printStackTrace();
        }
    }
}

下面对切面进行配置
    <aop:aspectj-autoproxy> </aop:aspectj-autoproxy>
    <bean id="manyDataSourceAspect" class="com.hy.base.db.DataSourceAspect" />
    <aop:config>
        <aop:aspect id="c" ref="manyDataSourceAspect">
            <aop:pointcut id="tx1"
                expression="execution(* *(..))" />
            <aop:before pointcut-ref="tx1" method="before" />
        </aop:aspect>
    </aop:config>

OK,到现在就大功告成。下面进行测试。我们使用的是mybatis,这里的具体配置就不给大家贴了。执行两个操作,分别针对两个库,如果两个库都能正常的响应,就说明我们的配置成功了。下面的例子就是两个接口,一个去找主库,一个去找从库,分别查询在某个数据库中唯一的一张表,主库的t1表只有主库有,从库的t2表只有从库有,这样如果都能查出来数据,说名两个库都是可以连接的。下面是具体的代码。

1.mapper,两个接口,一个使用主库,一个使用从库

public interface AppMapper {

    @DataSource("master")
    List<Map<String, Object>> executeQuery(@Param("sql")String sql);

    @DataSource("slave")
    List<Map<String, Object>> executeQuery2(@Param("sql")String sql);

}

2.测试方法

     @Test
    public void testDatasource() throws Exception {

        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");

        AppMapper mapper = (AppMapper) ac.getBean("appMapper");
        String sql = "select * from t1 limit 0,10";
        System.out.println(mapper.executeQuery(sql));

        sql = "select * from t2";

        System.out.println(mapper.executeQuery2(sql));

    }

这两个表在主库和从库都是唯一的。我们发现都能查出数据,说明我们配置正确了。当然了,这里数据库并没有做主从。



http://www.niftyadmin.cn/n/1896819.html

相关文章

mysql数据库测试工具_mydbtest测试工具下载

mysql数据库测试工具mydbtest如名字一样就是用来进行mysql数据库测试的辅助工具,小编今日为用户们带来的是64位版本,有需要的朋友们快来下载吧!使用说明:1.随机数据生成器我们在配置文件中指定随机数据的类型,取值范围 比如a int 10 30000 ,随机生成从10-30000的整数,注意 a 必…

02.05 Day 17 - 重温 Day 8

大家好&#xff0c;我是 Snow Hide&#xff0c;作为《MySQL 实战》这个专栏的学员之一&#xff0c;这是我打卡的第 17 天&#xff0c;也是我第 69 次进行这种操作。 今天我温习了该专栏里一篇叫《幻读是什么&#xff0c;幻读有什么问题&#xff1f;》的文章。 关键词总结&…

微信小程序 MinUI 组件库系列之 loadmore 页底组件

MinUI 是基于微信小程序自定义组件特性开发而成的一套简洁、易用、高效的组件库&#xff0c;适用场景广&#xff0c;覆盖小程序原生框架、各种小程序组件主流框架等&#xff0c;并且提供了高效的命令行工具。MinUI 组件库包含了很多基础的组件&#xff0c;其中 loadmore 页底组…

Vmware创建Linux虚拟机

很多情况下我们想要一个Linux环境&#xff0c;却很难有条件单独使用一台机器来部署Linux系统&#xff0c;因此经常会选择使用虚拟机的形式或者是选择安装双系统。这里我主要记录一个我在虚拟机中安装Linux系统的过程。 我这里你使用的虚拟机软件是VMware。下面多图预警~~~~~~ …

mysql 5.7.17 win10_mysql 5.7.17 安装配置方法图文教程(windows10)

mysql 5.7.17 安装配置方法整理第一步到MySQL官网下载mysql-5.7.17-winx64.zip第二步 解压缩 mysql-5.7.17-winx64.zip第三步 新建一个my.ini文件&#xff0c;然后用记事本打开输入mysql的基本配置&#xff1a;[mysql]; 设置mysql客户端默认字符集default-character-setutf8[my…

生成纯ASCII的图像

我们经常见到在一些项目的启动的过程中有一些特殊的字符来标识这个项目&#xff0c;比如Redis&#xff0c;又或者SpringBoot&#xff0c;那么这些字符是怎么做到的呢&#xff0c;如果你认为是一个一个敲的&#xff0c;那就out了&#xff0c;有一个网站可以非常轻松的来处理这件…

vue-piczoom:基于vue2.x的电商图片放大镜插件

最近在撸一个电商网站&#xff0c;有一个需求是要像淘宝商品详情页那样&#xff0c;鼠标放在主图上&#xff0c;显示图片放大镜效果&#xff0c;找了一下貌似没有什么合适的vue插件&#xff0c;于是自己撸了一个&#xff0c;分享一下。小白第一次分享&#xff0c;各位大神莫见笑…

mysql total语法_Mysql基础语法

Mysql数据库三种基本操作&#xff1a;DDL--数据定义语言(create&#xff0c;alter&#xff0c;drop&#xff0c;declare)DML--数据操纵语言(select,delete,update,insert)DCL--数据控制语言(grant,revoke,commit,rollback)Mysql基础语句1、创建数据库create database database_…