springBoot+mybatis 配置双数据源

提示:本文章的应用场景,需要从数据库一中的数据每天定时同步到另一个数据库当中,两个数据库表都不同,只有部分可匹配,此处便需要配置双数据源,且使用了 atomikos 进行事务管理


前言

提示:双数据源需要在配置文件中配置两个数据库连接信息,且需要两个数据库工具类,用于注册sqlSessionFactory 与  sqlSessionTemplate


pom.xml

       <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
      </dependency>
      <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
         <optional>true</optional>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
<!--         <scope>test</scope>-->
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-configuration-processor</artifactId>
      </dependency>
      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis</artifactId>
         <version>3.5.3</version>
      </dependency>
      <dependency>
         <groupId>org.mybatis</groupId>
         <artifactId>mybatis-spring</artifactId>
         <version>2.0.3</version>
      </dependency>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-jdbc</artifactId>
         <version>5.3.14</version>
      </dependency>
      <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>1.1.20</version>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jta-atomikos</artifactId>
      </dependency>
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>5.1.47</version>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

一、读取配置文件,编写两个数据库的配置类

两个数据库的配置以及工具类:

application.properties 

#启动端口
server.port=9050

##from
spring.datasource.primary.jdbc-url=jdbc:mysql://172.17.13.157:26001/mainTest?useUnicode=true&characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.primary.borrowConnectionTimeout=30
spring.datasource.primary.loginTimeout=30
spring.datasource.primary.maintenanceInterval=60
spring.datasource.primary.maxIdleTime=60
spring.datasource.primary.maxLifetime=20000
spring.datasource.primary.maxPoolSize=25
spring.datasource.primary.minPoolSize=3
spring.datasource.primary.uniqueResourceName= dataSource1
##to
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&useTimezone=true&serverTimezone=GMT%2B8
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.secondary.borrowConnectionTimeout=30
spring.datasource.secondary.loginTimeout=30
spring.datasource.secondary.maintenanceInterval=60
spring.datasource.secondary.maxIdleTime=60
spring.datasource.secondary.maxLifetime=20000
spring.datasource.secondary.maxPoolSize=25
spring.datasource.secondary.minPoolSize=3
spring.datasource.secondary.uniqueResourceName= dataSource2

PrimaryConfig

package rms.bohui.db_sync.config;


import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages = "rms.bohui.db_sync.fromDB.dao", sqlSessionFactoryRef = "sqlSessionFactory1", sqlSessionTemplateRef = "sqlSessionTemplate1")
public class PrimaryConfig {


    @Value("${spring.datasource.primary.driver-class-name}")
    private String primary_driverClassName;

    @Value("${spring.datasource.primary.jdbc-url}")
    private String primary_url;

    @Value("${spring.datasource.primary.username}")
    private String primary_username;

    @Value("${spring.datasource.primary.password}")
    private String primary_password;

    @Value("${spring.datasource.primary.borrowConnectionTimeout}")
    private int borrowConnectionTimeout;

    @Value("${spring.datasource.primary.loginTimeout}")
    private int loginTimeout;

    @Value("${spring.datasource.primary.maintenanceInterval}")
    private int maintenanceInterval;

    @Value("${spring.datasource.primary.maxIdleTime}")
    private int maxIdleTime;

    @Value("${spring.datasource.primary.maxLifetime}")
    private int maxLifetime;

    @Value("${spring.datasource.primary.maxPoolSize}")
    private int maxPoolSize;

    @Value("${spring.datasource.primary.minPoolSize}")
    private int minPoolSize;

    @Value("${spring.datasource.primary.uniqueResourceName}")
    private String uniqueResourceName;


    @Primary
    @Bean("dataSource1")
    public DataSource primaryDataSource() throws SQLException {

        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(primary_url);
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setPassword(primary_password);
        mysqlXADataSource.setUser(primary_username);
        //注册到全局事务
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName(uniqueResourceName);
        xaDataSource.setMinPoolSize(minPoolSize);
        xaDataSource.setMaxPoolSize(maxPoolSize);
        xaDataSource.setMaxLifetime(maxLifetime);
        xaDataSource.setBorrowConnectionTimeout(borrowConnectionTimeout);
        xaDataSource.setLoginTimeout(loginTimeout);
        xaDataSource.setMaintenanceInterval(maintenanceInterval);
        xaDataSource.setMaxIdleTime(maxIdleTime);
        xaDataSource.setTestQuery(null);
        return xaDataSource;

//        DruidDataSource dataSource = new DruidDataSource();
//        dataSource.setDriverClassName(this.primary_driverClassName);
//        dataSource.setUrl(this.primary_url);
//        dataSource.setUsername(this.primary_username);
//        dataSource.setPassword(this.primary_password);
//        return dataSource;
    }


    @Primary
    @Bean("sqlSessionFactory1")
    public SqlSessionFactory sqlSessionFactoryPrimary(@Qualifier("dataSource1")DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean("sqlSessionTemplate1")
    public SqlSessionTemplate sqlSessionTemplatePrimary(@Qualifier("sqlSessionFactory1")SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

配置类当中   @MapperScan 指定了各自数据库操作的dao层代码,所以操作数据库的Mapper代码不可放在一个文件夹当中,平常配置中,该文件应该创建事务管理器,而本项目中使用了其他事务管理,也就是代码中的  AtomikosDataSourceBean 类,所以不需要配置事务管理。

SecondaryConfig

package rms.bohui.db_sync.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.atomikos.jdbc.AtomikosDataSourceBean;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.sql.SQLException;

@Configuration
@MapperScan(basePackages = "rms.bohui.db_sync.toDB.dao", sqlSessionFactoryRef = "sqlSessionFactory2", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class SecondaryConfig {
    @Value("${spring.datasource.secondary.driver-class-name}")
    private String secondary_driverClassName;

    @Value("${spring.datasource.secondary.jdbc-url}")
    private String secondary_url;

    @Value("${spring.datasource.secondary.username}")
    private String secondary_username;

    @Value("${spring.datasource.secondary.password}")
    private String secondary_password;

    @Value("${spring.datasource.secondary.borrowConnectionTimeout}")
    private int borrowConnectionTimeout;

    @Value("${spring.datasource.secondary.loginTimeout}")
    private int loginTimeout;

    @Value("${spring.datasource.secondary.maintenanceInterval}")
    private int maintenanceInterval;

    @Value("${spring.datasource.secondary.maxIdleTime}")
    private int maxIdleTime;

    @Value("${spring.datasource.secondary.maxLifetime}")
    private int maxLifetime;

    @Value("${spring.datasource.secondary.maxPoolSize}")
    private int maxPoolSize;

    @Value("${spring.datasource.secondary.minPoolSize}")
    private int minPoolSize;

    @Value("${spring.datasource.secondary.uniqueResourceName}")
    private String uniqueResourceName;

    @Bean("dataSource2")
    public DataSource secondaryDataSource() throws SQLException {

        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(secondary_url);
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setPassword(secondary_password);
        mysqlXADataSource.setUser(secondary_username);
        //注册到全局事务
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setUniqueResourceName(uniqueResourceName);
        xaDataSource.setMinPoolSize(minPoolSize);
        xaDataSource.setMaxPoolSize(maxPoolSize);
        xaDataSource.setMaxLifetime(maxLifetime);
        xaDataSource.setBorrowConnectionTimeout(borrowConnectionTimeout);
        xaDataSource.setLoginTimeout(loginTimeout);
        xaDataSource.setMaintenanceInterval(maintenanceInterval);
        xaDataSource.setMaxIdleTime(maxIdleTime);
        xaDataSource.setTestQuery(null);
        return xaDataSource;

//        DruidDataSource dataSource = new DruidDataSource();
//        dataSource.setDriverClassName(this.secondary_driverClassName);
//        dataSource.setUrl(this.secondary_url);
//        dataSource.setUsername(this.secondary_username);
//        dataSource.setPassword(this.secondary_password);
//        return dataSource;
    }

    @Bean("sqlSessionFactory2")
    public SqlSessionFactory sqlSessionFactorySecondary(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }


    @Bean("sqlSessionTemplate2")
    public SqlSessionTemplate sqlSessionTemplateSecondary(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}

二、编写业务代码

service层代码:

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import rms.bohui.db_sync.config.idgenerate.IdGenerate;
import rms.bohui.db_sync.entity.*;
import rms.bohui.db_sync.fromDB.dao.PrimaryMapper;
import rms.bohui.db_sync.service.Service;
import rms.bohui.db_sync.toDB.dao.SecondaryMapper;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class ServiceImpl implements Service {

    static Logger logger= LoggerFactory.getLogger(ServiceImpl.class);


    @Autowired
    PrimaryMapper primaryMapper;

    @Autowired
    SecondaryMapper secondaryMapper;


    //查询台站相关信息,并进行入库处理
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int syncOrg() {
        AtomicInteger successNum = new AtomicInteger(0);
        try {
            //获取未提取的台站数据
            List<TbBaseOrg> orgList = primaryMapper.selectOrg();
            orgList.stream().forEach(org->{
                org.setDescription("该台站从其他数据库共享而来");
                //插入rms数据库
                int num = secondaryMapper.insertOrg(org);
                if (num>0){
                    successNum.addAndGet(primaryMapper.updateOrg(org.getStID()));
                }
            });
        }catch (Exception e){
            logger.error("同步台站失败,异常:"+e.getMessage());
            e.printStackTrace();
         }
        return  successNum.intValue();
    }
}

dao层代码:

        PrimaryMapper:

package rms.bohui.db_sync.fromDB.dao;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import rms.bohui.db_sync.entity.*;

import java.util.List;

@Mapper
public interface PrimaryMapper {

    //查询未同步的台站信息
    @Select({"<script>",
            "select * from tb_base_station t where t.fetch_flag_SYS_ZHYW = 0",
            "</script>"})
    List<TbBaseOrg> selectOrg();


    //修改台站数据提取标识
    @Update({"update tb_base_station t set t.fetch_flag_SYS_ZHYW = 1 , t.fetch_time_SYS_ZHYW = sysdate()",
            " where stID = #{id} "})
    int updateOrg(int id);
}

        SecondaryMapper:

import org.apache.ibatis.annotations.*;
import rms.bohui.db_sync.entity.*;

import java.util.List;

@Mapper
public interface SecondaryMapper {

    /**
     * 新增 单位
     * @param rmsSysOrg 单位 数据实体
     * @return 影响行数
     */
    @Insert({"<script>",
            "INSERT INTO tb_system_org (",
            "<trim suffixOverrides=','>",
            "  is_valid,",
            "  creator_id,",
            "  create_time,",
            "  <if test='stName != null'> name,</if>",
            "  <if test='stType != null'> type_dic,</if>",
            "  <if test='phone != null'> telephone,</if>",
            "  <if test='addr != null'> address,</if>",
            "  <if test='longitude != null'> longitude,</if>",
            "  <if test='lititude != null'> latitude,</if>",
            "  <if test='altitude != null'> altitude,</if>",
            "  <if test='acreage != null'> land_area,</if>",
            "  <if test='build_time != null'> build_time,</if>",
            "  <if test='stCode != null'> org_code,</if>",
            "  <if test='description != null'> description,</if>",
            "  <if test='stID != null'> fetch_id,</if>",
            "</trim>",
            ") ",
            "VALUES (",
            "<trim suffixOverrides=','>",
            "   0, ",
            "   1, ",
            "  sysdate(),",
            "  <if test='stName != null'> #{stName},</if>",
            "  <if test='stType != null'> #{stType},</if>",
            "  <if test='phone != null'> #{phone},</if>",
            "  <if test='addr != null'> #{addr},</if>",
            "  <if test='longitude != null'> #{longitude},</if>",
            "  <if test='lititude != null'> #{lititude},</if>",
            "  <if test='altitude != null'> #{altitude},</if>",
            "  <if test='acreage != null'> #{acreage},</if>",
            "  <if test='build_time != null'> #{build_time},</if>",
            "  <if test='stCode != null'> #{stCode},</if>",
            "  <if test='description != null'> #{description},</if>",
            "  <if test='stID != null'> #{stID},</if>",
            "</trim>",
            ")",
            "</script>"})
    int insertOrg(TbBaseOrg rmsSysOrg);
}

        台站实体类代码:

package rms.bohui.db_sync.entity;

import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * 台站实体类
 */
@Data
@NoArgsConstructor
public class TbBaseOrg {

    /** 共享台站表记录ID */
    private int stID;
    /** 最后修改用户ID */
    private int userID;
    /** 台站编号 */
    private String stCode;
    /** 台站名称 */
    private String stName;
    /** 台站类型 */
    private String stType;
    /** 所属机构 */
    private String org;
    /** 台站地址 */
    private String addr;
    /** 联系电话 */
    private String phone;
    /** 建立时间 */
    private String build_time;
    /** 经度 */
    private String longitude;
    /** 纬度 */
    private String lititude;
    /** 海拔 */
    private String altitude;
    /** 占地面积 */
    private String acreage;
    /** 所在城市名称 */
    private String city_name;
    /** 创建时间 */
    private String createdTime;
    /** 发布时间 */
    private String modifyTime;
    /** 提取标识 */
    private String fetchFlag;
    /** 提取时间 */
    private Date fetchTime;
    /** rms数据库主键id */
    private Long id;
    /** 描述 */
    private String description;
}

其他业务代码不在描述,正常调用service即可。

如需要打印日志到文件中,可加入logback.xml

logback.xml:

        

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
    <!-- 应用名称 -->
    <property name="APP_NAME" value="rmsSyncLog" />
    <!--日志文件的保存路径,首先查找系统属性-Dlog.dir,如果存在就使用其;否则,在当前目录下创建名为logs目录做日志存放的目录 -->
    <property name="LOG_HOME" value="${log.dir:-logs}/${APP_NAME}" />
    <!-- 日志输出格式 -->
    <property name="ENCODER_PATTERN"
              value="%d{yyyy-MM-dd  HH:mm:ss.SSS} [%thread] %-5level %logger{80} - %msg%n" />
    <contextName>${APP_NAME}</contextName>

    <!-- 控制台日志:输出全部日志到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <Pattern>${ENCODER_PATTERN}</Pattern>
        </encoder>
    </appender>

    <!-- 文件日志:输出全部日志到文件 -->
    <appender name="FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/output-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 错误日志:用于将错误日志输出到独立文件 -->
    <appender name="ERROR_FILE"
              class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>${ENCODER_PATTERN}</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>WARN</level>
        </filter>
    </appender>

    <root>
        <level value="INFO" />
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <appender-ref ref="ERROR_FILE" />
    </root>
</configuration>

总结

提示:代码已经演示完毕,只演示部分代码,其他代码贴出来也没用,业务不适用,不同数据库操作,调用不同的Mapper即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值