一个Web项目实现多个数据库存储数据并相互切换

1、使用场景

多数据源使用场景一般为:

  1. 主从数据库切换
  2. 读写分离
  3. 兼容旧库

2、具体实现

实现原理

Spring2.x的版本中采用Proxy模式,就是在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文,由虚拟的DynamicDataSource根据Client提供的上下文来实现数据源的选择。

具体的实现是创建一个名为DynamicDataSource的类仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到则用默认的resolvedDefaultDataSource。

具体实现过程

1.修改jdbcConfig.properties数据源配置文件为一下内容(具体数据源可自行修改)

# A 数据库源A
jdbc.a.driver = com.mysql.cj.jdbc.Driver
jdbc.a.url = jdbc:mysql://localhost:3306/test1?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
jdbc.a.username = root
jdbc.a.password = 123456

# B 数据库源B
jdbc.b.driver = com.mysql.cj.jdbc.Driver
jdbc.b.url = jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
jdbc.b.username = root
jdbc.b.password = 123456

2.修改applicationContext.xml配置文件,需要配置多个数据源,修改后配置如下

<!-- 引入jdbc配置文件 -->
    <context:property-placeholder location="classpath:jdbcConfig.properties"></context:property-placeholder>
    <!--    spring 整合 mybatis-->
    <!-- 1.配置数据源 C3P0 1 -->
    <bean id="dataSourceTargetA" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.a.driver}" />
        <property name="jdbcUrl" value="${jdbc.a.url}" />
        <property name="user" value="${jdbc.a.username}" />
        <property name="password" value="${jdbc.a.password}" />
    </bean>
    <!-- 配置数据源 C3P0 2 -->
    <bean id="dataSourceTargetB" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.b.driver}" />
        <property name="jdbcUrl" value="${jdbc.b.url}" />
        <property name="user" value="${jdbc.b.username}" />
        <property name="password" value="${jdbc.b.password}" />
    </bean>
<!--   动态数据源  -->
    <bean id="dataSource" class="com.hk.handler.DynamicDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="dataSourceTargetA" key="dataSourceTargetA"></entry>
                <entry value-ref="dataSourceTargetB" key="dataSourceTargetB"></entry>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSourceTargetA"></property>

    </bean>
    <!--  2.配置SqlSessionFactory工厂  -->
    <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--   注入数据源     -->
        <property name="dataSource" ref="dataSource" />
        <!--    实体类起别名    -->
        <property name="typeAliasesPackage" value="com.hk.model"></property>
        <!--   指定mapper接口所对应的xml-->
        <property name="mapperLocations" value="classpath:com/hk/mapper/*.xml"></property>
        <!-- 指定Mybatis的核心配置文件 -->
        <property name="configLocation" value="classpath:mybatis.xml"></property>
    </bean>
    <!--    3.配置接口AccountDao所在的包 映射扫描配置类 mvc配置 -->
    <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--    接口所在的包    -->
        <property name="basePackage" value="com.hk.mapper" />
        <!--    扫描工厂并注入sqlSessionFactoryBeanName   -->
        <property name="sqlSessionFactoryBeanName" value="sessionFactory" />
    </bean>
    <!-- 配置spring声明式事务管理   -->
    <!--  配置事务管理器  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--  传入连接池对象      -->
        <property name="dataSource" ref="dataSource" />
    </bean>

3.创建动态数据源管理类

DynamicDataSource类继承AbstractRoutingDataSource,实现determineCurrentLookupKey()方法。

/**
 * @Author: aerfazhe
 * @Date: 2021/6/26 10:00
 * @Statement: 数据源切换类
 */
public class CustomerContextHolder {

    public static final String DATA_SOURCE_DEFAULT = "dataSourceTargetA";

    public static final String DATA_SOURCE_B = "dataSourceTargetB";

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static void setCustomerType(String customerType) {
        contextHolder.set(customerType);
    }

    public static String getCustomerType() {
        return contextHolder.get();
    }

    public static void clearCustomerType() {
        contextHolder.remove();
    }

}

5.使用多数据源代码

以Controller层代码为演示,需要添加信息到B数据源,完成后切换到A数据源进行查询,具体代码如下

@PostMapping("/save")
    public String save(User user) {
        CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B);
        int save = iuserService.save(user);
        CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_DEFAULT);
        if (save == 1) {
            return "success";
        }
        return "error";
    }

@GetMapping("/findAll")
    @ResponseBody
    public Map<String,Object> findAll() {
        List<User> userList = iuserService.findAll();
        Map<String,Object> map = new HashMap<>();
        map.put("status",0);
        map.put("msg","success");
        map.put("list",userList);
        return map;
    }

先从数据库B中的表进行添加数据,添加完成后,会切换到默认的数据库A,然后再从数据库A中的表查询数据,这样就达到了在一个Web项目中,根据不用的模块和业务需求,实现多个数据库存储数据和它们之间的切换了。

如有任何问题,欢迎评论区留言,希望可以帮助到大家。

 

学习参考至:https://www.cnblogs.com/zhaosq/p/10616244.html

posted @ 2021-06-26 15:15  阿尔法哲  阅读(888)  评论(0编辑  收藏  举报