菜鸟开发之任务调度--不同数据源数据同步

将一个数据库表的数据同步至另一数据库表中是开发过程中必不可少的业务需求,大部人在遇到此类问题时就会选择复制表结构,表对表插入数据,当然这不乏是个很方便快捷的方式,但是如果后期需要更多的这样处理就很麻烦,所以在以上的情况下实现任务调度不为一个非常好的方法。下面本菜菜鸟就以PG数据库下两个不同的库之间数据同步为例,展开共享自己的小收获。

目录

一、数据源的配置

pg1Config

pg2Config

二、 实现调度

1、Service

Service

ServiceImpl

2、Mapper

Mapper

Xml

三、总结

此功能是在XxlJob中实现

时间紧,任务重!直接就开始了,谨记不同数据源之间同步最重要最主要的是配置

一、数据源的配置

XxlJob中,一般主要两部分,一是服务启动模块,二是具体实现模块,服务驱动实现,在实现模块中配置数据源。

这里有一点很重要,非常值得注意,一般配置数据库链接为url,但是但是但是多数据源用jdbc-url,否则配置的可能就会不生效。

datasource:
    dynamic:
      primary: pg1 #设置默认的数据源或者数据源组,默认值即为pg1
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        pg1:
          jdbc-url: jdbc:postgresql://xx.xx.xx.xx:xxxx/pg1?currentSchema=pg1&useUnicode=true&characterEncoding=utf8
          username: root
          password: 123456
          driver-class-name: org.postgresql.Driver
          type: com.alibaba.druid.pool.DruidDataSource
        pg2:
          jdbc-url: jdbc:postgresql://xx.xx.xx.xx:xxxx/pg2?currentSchema=pg2&useUnicode=true&characterEncoding=utf8
          username: root
          password: 123456
          driver-class-name: org.postgresql.Driver
          type: com.alibaba.druid.pool.DruidDataSource
      druid:
        #     初始化大小,最小,最大
        initial-size: 5
        max-active: 30
        min-idle: 5
        #      获取数据库连接等待的超时时间
        max-wait: 3000
        max-open-prepared-statements: -1
        #      配置多久进行一次检测,检测需要关闭的空闲连接 单位毫秒
        time-between-eviction-runs-millis: 60000
        #      配置连接在池中的最小生存时间
        min-evictable-idle-time-millis: 300000
        #      配置连接在池中的最大生存时间
        max-evictable-idle-time-millis: 400000
        #      系统启动时通过该sql语句验证数据库是否可用,如果不配置validationQuery,则下面三项无效
        validation-query: SELECT 1
        #      启用空闲连接检测,以便回收
        test-while-idle: true
        #      从连接池获取连接时,是否检测连接可用性,开启性能会有些许影响
        test-on-borrow: false
        #      释放连接到连接池时,是否检测连接可用性,开启性能会有些许影响
        test-on-return: false
        #      打开PSCache,并且指定每个连接上PSCache的大小
        pool-prepared-statements: false
        max-pool-prepared-statement-per-connection-size: 20
        #      配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,wall
        use-global-data-source-stat: true
        #      通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

然后进行配置文件处理,这目的主要用于扫描相应的mapper        

pg1Config

@Configuration
// 配置mybatis的接口类放的地方
@MapperScan(basePackages = "com.app.task.relations.mapper.pg1", sqlSessionFactoryRef = "pg1SqlSessionFactory")
public class Pg1Config {
    // 将这个对象放入Spring容器中
    @Bean(name = "pg1DataSource")
    // 表示这个数据源是默认数据源
    @Primary
    // 读取application.properties中的配置参数映射成为一个对象
    // prefix表示参数的前缀
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.pg1")
    public DataSource getPg1DateSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "pg1SqlSessionFactory")
    // 表示这个数据源是默认数据源
    @Primary
    // @Qualifier表示查找Spring容器中名字为pg1DataSource的对象
    public SqlSessionFactory pg1SqlSessionFactory(@Qualifier("pg1DataSource") DataSource datasource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resources = resolver.getResources("classpath*:mapping/pg1/*Mapper.xml");
        bean.setMapperLocations(resources);
        return bean.getObject();
    }

    @Bean("pg1SqlSessionTemplate")
    // 表示这个数据源是默认数据源
    @Primary
    public SqlSessionTemplate pg1SqlSessionTemplate(
            @Qualifier("pg1SqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }
/**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */

}

pg2Config

@Configuration
@MapperScan(basePackages = "com.app.task.relations.mapper.pg2", sqlSessionFactoryRef = "pg2SqlSessionFactory")
public class Pg2Config {
    @Bean(name = "pg2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.pg2")
    public DataSource getPg2DateSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "pg2SqlSessionFactory")
    public SqlSessionFactory pg2SqlSessionFactory(@Qualifier("pg2DataSource") DataSource datasource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(datasource);
        bean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/pg2/*.xml"));
        return bean.getObject();
    }

    @Bean("pg2SqlSessionTemplate")
    public SqlSessionTemplate pg2SqlSessionTemplate(
            @Qualifier("pg2SqlSessionFactory") SqlSessionFactory sessionfactory) {
        return new SqlSessionTemplate(sessionfactory);
    }

    
}

以上的配置完成之后就可以实现自己的需求任务,在相应的服务内创建自己任务,启动服务,利用debug日志检查是否成功。

@Slf4j
@Component
public class SyncJob {

    @Autowired
    Pg1Service pg1Service;

    /**
     *   pg同步
     * @param param
     * @return
     * @throws Exception
     */
    @XxlJob("pgSyncJob")
    public ReturnT<String> pgSyncJob(String param) throws Exception {

        pg1Service.saveSyncData();
        return ReturnT.SUCCESS;
    }
}
/**
 *  服务启动  
 */
@SpringBootApplication(scanBasePackages = {"com.app.job.executor","com.app.task.relations"})
public class RelationsExecutorApplication {

    @PostConstruct
    void setDefaultTimezone() { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); }

    public static void main(String[] args) {
        SpringApplication.run(RelationsExecutorApplication.class, args);
    }

}

二、 实现调度

菜菜鸟实现同步逻辑:删——查——插

1、Service

Service

1)、pg1Service

数据库pg1里面的pg处理接口

/**
 *  保存DATA
 */
public interface Pg1Service {

    void saveSyncData();

    void deletePg1OldData();

    void savePg1NewData(List<PgVO> pgVOS);
}

2)、pg2Service

数据库pg2里面的pg处理接口

public interface Pg2Service {

    Cursor<PgVO> getPg2Data();

}

ServiceImpl

1)、pg1ServiceImpl

个人想法简单粗暴,删原来的,插最新的。

@Slf4j
@Service
public class Pg1ServiceImpl implements Pg1Service {

    @Autowired
    private Pg1Mapper pg1Mapper;

    @Autowired
    private Pg1Service pg1Service;

    @Autowired
    private Pg2Service pg2Service;


    @Override
    public void deletePg1OldData() {
        pg1Mapper.deletePg1OldData();
    }

    @Override
    public void savePg1NewData(List<PgVO> pgVOS) {
        pg1Mapper.savepg1NewData(pgVOS);
    }

    /**
     *  删除原表数据
     *  插入  异步处理
     *  保存新表
     *  失败回滚,重新调度
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveSyncData() {
        log.info("Start scheduling:");

        // 删除原有数据
        pg1Service.deletePg1OldData();

        // 保存数据
        int size=1000;
        Cursor<PgVO> odsData = pg2Service.getOdsPg2Data();

        // TODO  改进:根据城市同步  更新区县及城市编码
        // 插入
        List<PgVO> pgVOS = new ArrayList<>();
        odsData.forEach(x -> {
            pgVOS.add(x);
            if(pgVOS.size()>=size){
                log.info("SAVE DATA:{}", pgVOS.size());
                pg1Service.savePg1NewData(pgVOS);
                // 清理集合
                pgVOS.clear();
            }
        });

        // 最后数据量不足1000
        if(CollectionUtil.isNotEmpty(pgVOS)){
            log.info("Less than 1000 pieces of data:{}",pgVOS.size());
            pg1Service.savePg1NewData(pgVOS);
        }

        // 核对 更新区县及城市编码
    }

}

2)、pg2ServiceImpl

@Slf4j
@Service
public class Pg2ServiceImpl implements Pg2Service {

    @Autowired
    private Pg2Mapper mapper;

    @Override
    @Transactional
    public Cursor<PgVO> getPg2Data() {
        return mapper.getPg2Data();
    }

}

2、Mapper

Mapper

1)、Pg1Mapper

@Mapper
public interface Pg1Mapper {

    void savePg1NewData(@Param("list") List<PgVO> list);

    void deletePg1OldData();

}

2)、Pg2Mapper

@Mapper
public interface Pg2Mapper {

    Cursor<PgVO> getPg2Data();

}

Xml

1)、Pg1Xml

    <delete id="deletePg1Data">
        TRUNCATE table pg.pg1;
    </delete>

    <insert id="savePg1NewData"
            parameterType="com.topprism.task.relations.vo.PgVO">
        insert into pg.pg1(
            province,
            city,
            district
        ) values
        <foreach collection="list" item="item" separator=",">
            (
             #{item.province},
             #{item.city},
             #{item.district}
            )
        </foreach>
    </insert>

1)、Pg2Xml

    <select id="getPg2Data" parameterType="com.topprism.task.relations.vo.PgVO"                          resultSetType="FORWARD_ONLY" fetchSize="1000">
        SELECT
            province,
            city,
            district
        FROM
            pg.pg2
    </select>

三、总结

        此任务调度过程最重要的数据源的配置,可能一个小小的空格就会发现注入不进去,导致扫不到包。在调度过程中不能改变原有业务需要的单一数据源,所以配置之处就需考虑到,启动服务时不用扫描mapper,只需各个业务需求需要什么数据源,就自行扫描自己的mapper。

        多数据源配置根据业务需求配置,上面的菜菜鸟只需要两个数据源,如果所需较多数据源,可以实现多数据源动态切换,菜菜鸟正在努力

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值