Springboot集成kettle,实现数据全量更新

1.前言

	最近根据项目的需要,要做一个数据同步的项目,需要使用到kettle在网上查看了很多相关的帖子,很多
	都是直接使用kettle的客户端工具spoon进行直接同步,通过代码实现的很散。后面自己根据项目中的实际
	需求,实现一个简单的全量更新,满足了项目的需求,但还存在一些问题需要改进。

2. 实现

1. 集成jar包 
	kettle没有开放的SDK,所以需要自己下载kettle,找到lib文件夹中的jar包,导入到Springboot项目中   
    找到核心的几个Jar包commons-lang,commons-vfs2,kettle-core,kettle-engine,metastore 
    在resources目录下新建一个lib文件夹,将上述所有jar包放入到lib文件夹中
    <!--引入kettle核心包-->
    <dependency>
         <!--可以随便写-->
       <groupId>kettle</groupId>
           <!--可以随便写-->
       <artifactId>core</artifactId>
         <!--可以随便写-->
       <version>7.1.0.0-12</version>
       <scope>system</scope>
         <!--配置项目中对应的jar包路径-->
       <systemPath>${project.basedir}/src/main/resources/lib/kettle-core-7.1.0.0-12.jar</systemPath>
   </dependency>
    <dependency>
        <groupId>kettle</groupId>
        <artifactId>engine</artifactId>
        <version>7.1.0.0-12</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/lib/kettle-engine-7.1.0.0-12.jar</systemPath>
    </dependency>
    <dependency>
        <groupId>kettle</groupId>
        <artifactId>metastore</artifactId>
        <version>7.1.0.0-12</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/lib/metastore-7.1.0.0-12.jar</systemPath>
    </dependency>
    <dependency>
        <groupId>commons</groupId>
        <artifactId>vfs2</artifactId>
        <version>2.1</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/lib/commons-vfs2-2.1-20150824.jar</systemPath>
    </dependency>
    <dependency>
        <groupId>commons</groupId>
        <artifactId>lang</artifactId>
        <version>2.6</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/lib/commons-lang-2.6.jar</systemPath>
    </dependency>
    通过这样本地执行是没有问题,但是如果要打包部署到线上,还需要在maven中配置打包时将项目中的jar包也打包 
    还需要在Maven中添加一个配置
    <build>
        <plugins>
            <!--maven打包将本地jar包也打入-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <includeSystemScope>true</includeSystemScope>
                </configuration>
            </plugin>
       </plugins>     
</build>
2.Spoon创建ktr文件  
	这里我采用的方式是分为三个步骤,第一次是获取到源数据库的DDL语句,然后再执行DDL语句,最后再进行数据的拷贝,
	因为如果目标数据库不存在表是会报错的,所以必须保证目标数据库存在一致的表结构,所以执行DDL语句时  先执行删除
	表语句再执行创建DDL语句,保证数据库的结构是一致的。 
	1.获取DDL语句 

在这里插入图片描述
2.执行DDL语句
在这里插入图片描述
3.数据拷贝
在这里插入图片描述
创建好三个ktr文件后,放入到resources下,新建一个kettleFile文件夹,将三个文件放入文件夹中

3. 代码实现 
 	package com.xtoneict.kettle;
	
	import com.xtoneict.action.SaveConnectParameterAction;
	import lombok.extern.slf4j.Slf4j;
	import org.pentaho.di.core.KettleEnvironment;
	import org.pentaho.di.core.RowMetaAndData;
	import org.pentaho.di.core.database.DatabaseMeta;
	import org.pentaho.di.core.exception.KettleException;
	import org.pentaho.di.core.logging.KettleLogStore;
	import org.pentaho.di.trans.Trans;
	import org.pentaho.di.trans.TransMeta;
	
	import java.io.InputStream;
	import java.util.HashMap;
	import java.util.List;
	import java.util.Map;
	
	@Slf4j
	public class KettleUtils {
	
	    /**
	     * 获取kettle中的DDL语句
	     * @param originAction   源数据库信息
	     * @param ddlPath   获取DDL文件的输入流
	     * @return
	     */
	    public static Map<String, String> getKettleDDL(SaveConnectParameterAction originAction, InputStream ddlPath) throws KettleException {
	
	        Map<String, String> map = new HashMap<>();
	        KettleEnvironment.init();
	        TransMeta transMeta = new TransMeta(ddlPath,null,true,null,null);
	        List<DatabaseMeta> databases = transMeta.getDatabases();
	        DatabaseMeta originMeta = databases.get(0);
	        setDatabaseMeta(originMeta,originAction);
	        //创建tran对象
	        Trans trans = new Trans(transMeta);
	        trans.execute(null);
	        trans.waitUntilFinished();
	        if (trans.getErrors() != 0) {
	            String[] errMsgList = KettleLogStore.getAppender().getBuffer(trans.getLogChannelId(), false).toString().split("\n\r\n");
	            log.error("获取DDL语句出现错误"+errMsgList[0]);
	        }
	        //获取返回结果
	        List<RowMetaAndData> resultRows = trans.getResultRows();
	        resultRows.forEach(rowMetaAndData ->{
	            Object[] data = rowMetaAndData.getData();
	            map.put(data[0].toString(),data[3].toString());
	        });
	        return map;
	    }
	
	
	
	    /**
	     *  数据同步
	     * @param originAction    源数据库信息
	     * @param targetAction   目标数据库信息
	     * @param dataPath     数据拷贝文件的输入流
	     * @param ddlPath       创建DDL语句的输入流
	     * @param tableNames  选择拷贝表名称
	     * @return
	     */
	    public static Boolean copyData(SaveConnectParameterAction originAction, SaveConnectParameterAction targetAction, InputStream dataPath, InputStream ddlPath,
	                                   String ddlStr, List<String> tableNames) throws KettleException {
	        KettleEnvironment.init();
	        initDDL(targetAction,ddlPath,ddlStr);
	        TransMeta transMeta = new TransMeta(dataPath,null,true,null,null);
	        List<DatabaseMeta> databases = transMeta.getDatabases();
	        // 0为源   1为目标
	        DatabaseMeta originMeta = databases.get(0);
	        setDatabaseMeta(originMeta,originAction);
	        DatabaseMeta targetMeta = databases.get(1);
	        setDatabaseMeta(targetMeta,targetAction);
	     	 //创建tran对象
	        Trans trans = new Trans(transMeta);
	        tableNames.forEach(v-> {
	            try {
	                //设置参数
	                trans.setVariable("table_name", v);
	                trans.execute(null);
	                trans.waitUntilFinished();
	                if (trans.getErrors() != 0) {
	                    //失败 保存
	                    String[] errMsgList = KettleLogStore.getAppender().getBuffer(trans.getLogChannelId(), false).toString().split("\n\r\n");
	                    log.error("数据同步出现错误"+errMsgList[0]);
	                }
	            } catch (KettleException e) {
	                log.error("Kettle数据同步出现异常"+e.getMessage());
	            }
	        });
	        return Boolean.TRUE;
	
	
	    }
	
	
	    /**
	     *  执行DDL语句 创建表结构
	     * @param originAction  源数据库信息
	     * @param ddlPath    DDL文件的输入流
	     * @param ddlStr      执行的DDL语句
	     * @throws KettleException
	     */
	    private static void initDDL(SaveConnectParameterAction originAction, InputStream ddlPath,
	                               String ddlStr) throws KettleException {
	        TransMeta transMeta = new TransMeta(ddlPath,null,true,null,null);
	        DatabaseMeta database = transMeta.getDatabase(0);
	        setDatabaseMeta(database,originAction);
	        Trans trans = new Trans(transMeta);
	        trans.setVariable("ddl",ddlStr);
	        trans.execute(null);
	        trans.waitUntilFinished();
	        if (trans.getErrors() != 0) {
	            //失败 保存
	            String[] errMsgList = KettleLogStore.getAppender().getBuffer(trans.getLogChannelId(), false).toString().split("\n\r\n");
	            log.error("执行DDL语句出现错误"+errMsgList[0]);
	        }
	    }
	
	    /**
	     * 设置数据库连接参数
	     * @param databaseMeta
	     * @param action
	     */
	    public static void setDatabaseMeta(DatabaseMeta databaseMeta, SaveConnectParameterAction action){
	        //连接地址
	        databaseMeta.setHostname(action.getHost());
	        //数据库名称
	        databaseMeta.setDBName(action.getDbName());
	        //端口
	        databaseMeta.setDBPort(action.getPort());
	        //用户
	        databaseMeta.setUsername(action.getUsername());
	        //密码
	        databaseMeta.setPassword(action.getPassword());
	    }
	}

3.总结

	   这样实现了通过代码进行动态的数据拷贝,但是还是存在很多问题,只是简单实现了需求。但是kettle的相关资料比较
	   少,个人觉得代码操作的意义不是很大,但是通过代码实现增量更新会很麻烦  缺少资料进行操作。全量更新数据量太大
	   有很多问题,个人觉得kettle使用spoon工具类操作更优
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值