作为一个技术栈出身的攻城狮,虽然走上管理之路,但是技术是不能扔下的,时不时的拿起来重温一下,理论与实践相结合...
使用背景:
住建部某区块链共享平台(下游系统)需要自于上游系统的生产库数据,数据量不大十几万,而且须要每天提供截止当天的增量数据,要求每条数据给出数据变化时间及标示,即数据若是插入有插入时间和插入标示;若是改动,有改动时间和改动标示;若是删除需逻辑删除、有删除标示且有删除时间等等。
解决办法:
kettle的转换ktr里有一个图元叫做合并记录。能够把两个表输入分为源和目的依据唯一标示进行全量比对。得到增量的数据流,再写入到中间表里,即能够实现该需求。实现功能的ktr如下图:
1 输入源 合并记录
上图。最左側是两个表输入,上面一个GRZHXX 是上游系统,下一个是SEND_GRZHXX目标数据
GRZHXX的数据来源SQL:注意一定要排序操作!
SEND_GRZHXX的数据来源SQL: 注意一定要排序操作!
2 值映射
例如以下图, 将输入源与目的源的每一个字段数据依据唯一字段比較后到值映射图元,使用字段名为起的后面用到的一个变量名(可随意起)。源值列为系统默认
1 代表输入源与目标源比较后删除的数据标志
2 是输入源新增
3 是输入源更新
4是不变 目标值 是自己起的名字 能够依据须要不变 或改动
标志字段:设置标志字段的名称,标志字段用于保存比较的结果,比较结果有下列几种。
1. “identical” – 旧数据和新数据一样
2. “changed” – 数据发生了变化;
3. “new” – 新数据中有而旧数据中没有的记录
4. “deleted” –旧数据中有而新数据中没有的记录
3过滤无效记录
例如以下图,条件 flagfield is not null (后面没显示完),若条件成立发送给下一步zh_check_date,若不成立发送给空操作。
通过下面的过滤标志过滤各种类型数据。
4 新增数据推断add
例如以下图,zh_check_date 为获取当前的系统时间变量。
add2 图元打开为 画圈的图 左側的地方 ,条件 flagfield = add_rec ,若成立及发送数据到中间画圈的add图元,若不成立则 发送数据到mod_del图元 (矩形红框) 如果为true数据到 add图元,打开 即是下图右側 部分 填写须要插入的数据字段 再到insert图元 ,就可以把输入源比目标源多的新数据更新到目标表来 而且加上时间戳。
5 改动或删除 mod_rec
例如以下图,如果数据从add2发送而来。 打开矩形框 mod_del 条件flagfield = mod_rec 若true 则发送到 update mapping 若flase 则发送到delete mappinig 。如果是更新,则右側 的查询keyword 是 更新的比較字段 即是一開始合并记录的比較字段 ,更新字段就是 除了比較字段之外的其它字段。这样数据就能够从 输入源更新到目标源。
6 删除数据 delete mapping
例如以下图。如果数据流到了 delete mapping 。则 仅仅须要依据比較字段把 目标表的时间戳更新 和 状态更新为del_rec就可以,下图zh_check_type 为flag_field的值
7 数据结果查看
zh_check_type和check_date已更新成功
8 程序调用 (定时执行、批量执行)
在程序中直接调用ktr执行数据同步操作,需要注意的是,从Kettle工具中拷贝jar到项目中
具体实现代码如下:
import java.text.SimpleDateFormat;import java.util.Date; import org.pentaho.di.core.KettleEnvironment;import org.pentaho.di.core.exception.KettleException;import org.pentaho.di.core.util.EnvUtil;import org.pentaho.di.job.Job;import org.pentaho.di.job.JobMeta;import org.pentaho.di.trans.Trans;import org.pentaho.di.trans.TransMeta;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component; @Componentpublic class TestKettle { public static String filePath = " pdi-ce-5.1.0.0-752/data-integration/"; public static String fileName = "GRZHXX.ktr"; public static void main(String[] args) { System.out.println("============>>>>>> job开始执行 【 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date()) + "】"); String jobFileName = filePath + "TestJob.kjb"; try { long startTime = System.currentTimeMillis(); // callKettleJob(jobFileName); callNativeTrans(filePath + fileName); long endTime = System.currentTimeMillis(); System.out.println("数据抽取任务运行时间:" + (endTime - startTime) / 1000+ "S"); } catch (KettleException e) { e.printStackTrace(); } } @Scheduled(cron = "0 30 16 * * ? ") public void process() { System.out.println("============>>>>>> job开始执行 【 " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date()) + "】"); String jobFileName = filePath + "TestJob.kjb"; try { long startTime = System.currentTimeMillis(); // callKettleJob(jobFileName); callNativeTrans(filePath + fileName); long endTime = System.currentTimeMillis(); System.out.println("数据抽取任务运行时间:" + (endTime - startTime) / 1000+ "S"); } catch (KettleException e) { e.printStackTrace(); } } public static void callKettleJob(String jobFileName) throws KettleException { KettleEnvironment.init();// 初始化 JobMeta jobMeta = new JobMeta(jobFileName, null);// jobFileName是job脚本路径及文件名 Job job = new Job(null, jobMeta); // job.setVariable("AJBH", "123");// 传入参数 job.start(); job.waitUntilFinished(); if (job.getErrors() > 0) { throw new KettleException("job执行不成功,有步骤失败!"); } System.out.println("============>>>>>> job执行完成"); } /** * 调用本地的转换文件 */ public static void callNativeTrans(String transFileName) throws KettleException { KettleEnvironment.init();// 初始化 // 转换元对象 TransMeta transMeta = new TransMeta(transFileName); // 转换 Trans trans = new Trans(transMeta); // 执行转换 trans.execute(null); // 等待转换执行结束 trans.waitUntilFinished(); } }对于定时执行,可以执行kettle里面的job文件也可以通过程序定时任务来控制同时还可以多线程同时操作:public static void main(String arg[]) throws Exception { String idname="7";//参数值 String filename1="./test1.ktr";//ktr路径 String filename2="./test2.ktr";//ktr路径 KettleEnvironment.init();//初始化kettle环境 /*定义文件路径,模型元数据,模型三个容器*/ ArrayList list1=new ArrayList(); ArrayList list2=new ArrayList(); ArrayList list3=new ArrayList(); /*添加文件对象*/ list1.add(filename1); list1.add(filename2); //System.out.print("=======================1:"+list1.get(0)); //System.out.print("=======================2:"+list1.get(1)); /*遍历文件对象,创建转换元数据对象*/ for(int i=0;i