問題背景
今天使用kettle做數據歸檔時,發現kettle無法插入空字符串,它默認會把空字符串變爲null。這就導致問題出現。
查詢資料
通過查資料發現了網上的一個解決辦法,首先感謝 關於kettle的空字符串和NULL的問題 這篇文章。但是他那個是直接使用的kettle客戶端,並沒有和java集成。
然後我就去研究源碼,看kettle是否支持設置這樣的環境變量,我以KettleEnvironment.init();EnvUtil.environmentInit(); 這兩個初始化方法爲入口,經過仔細研究並沒有找到對應的辦法;
無奈之下,我就想:是否可以利用提前生成kettle.properties文件的辦法來解決。遂有本文。
解決方案
直接上代碼(本文是在quartz + kettle8二次開發-實現集羣高可用)基礎上繼續做的,修改了裏面的kettleUtil:
- 注意看裏面的dealWriteNull()方法
package cc.xxxx.utils;
import cc.xxxx.configs.EnvConfig;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.KettleClientEnvironment;
import org.pentaho.di.core.KettleEnvironment;
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.stereotype.Component;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.util.Map;
/**
* kettle工具類
* 使用靜態塊的原因:
* 1.執行ktr和kjb都要執行初始化,爲了代碼的複用;
* 2.由於quartz的定時任務是異步的,在springBoot剛剛啓動完成就可能會執行ktr和kjb,所以kettle的初始化工作
* 必須在springBoot啓動完成前執行,故使用靜態塊;
* @author wangll
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class KettleUtil {
@NonNull
private EnvConfig envConfig;
static {
log.info("正在初始化kettle ...");
try {
dealWriteNull();
KettleEnvironment.init();
EnvUtil.environmentInit();
}catch (Exception e){
log.error("初始化kettle失敗",e);
System.exit(0);
}
log.info("初始化kettle成功!");
}
/**
* 執行ktr文件內容
* @param file
* @param beginTime
* @param endTime
*/
public void runTrans(@NonNull String file, String beginTime, String endTime){
log.info("正在執行ktr ...");
try {
TransMeta transMeta = new TransMeta(new ByteArrayInputStream(file.getBytes("UTF-8")), null, true, null, null);
Trans trans = new Trans(transMeta);
Map<String,String> params = this.dealParams(beginTime,endTime);
params.forEach((k,v)->trans.setVariable(k,v));
trans.prepareExecution(null);
trans.startThreads();
trans.waitUntilFinished();
if (trans.getErrors() != 0) {
log.error("執行ktr過程中存在錯誤");
}
}catch (Exception e){
log.error("runTrans失敗",e);
}
log.info("ktr執行完畢!");
}
/**
* 執行kjb文件內容
* @param file
* @param beginTime
* @param endTime
*/
public void runJob(@NonNull String file, String beginTime, String endTime){
log.info("正在執行kjb ...");
try {
JobMeta jobMeta = new JobMeta(new ByteArrayInputStream(file.getBytes("UTF-8")), null, null);
Job job = new Job(null, jobMeta);
Map<String,String> params = this.dealParams(beginTime,endTime);
params.forEach((k,v)->job.setVariable(k,v));
job.start();
job.waitUntilFinished();
if (job.getErrors() != 0) {
log.error("執行kjb過程中存在錯誤");
}
}catch (Exception e){
log.error("runJob失敗",e);
}
log.info("kjb執行完畢...");
}
/**
* 處理公共參數
*/
private Map<String,String> dealParams(String beginTime, String endTime){
Map<String, String> params = envConfig.getDamEnv();
params.put("beginTime",beginTime);
params.put("endTime",endTime);
if (params.containsKey("beginTime")){
params.put("beginTimeId",TimeUtil.date2timeId(params.get("beginTime")));
}
if (params.containsKey("endTime")){
params.put("endTimeId",TimeUtil.date2timeId(params.get("endTime")));
}
log.debug("kettle參數:{}",params);
return params;
}
/**
* 解決kettle無法寫入空字符串的問題
* window環境中,需要在C:\Users\wangll\.kettle\kettle.properties中寫入如下配置;
* linux環境中,需要在/root/.kettle/kettle.properties中寫入如下配置。
* 故爲了方便直接使用它自帶的方法去生成上述文件
*/
public static void dealWriteNull()throws Exception{
String directory = Const.getKettleDirectory();
File dir = new File(directory);
dir.mkdirs();
KettleClientEnvironment.createKettleHome();
String kpFile = directory + Const.FILE_SEPARATOR + "kettle.properties";
File file = new File(kpFile);
FileWriter fw = new FileWriter(file);
BufferedWriter bw=new BufferedWriter(fw);
bw.write("KETTLE_EMPTY_STRING_DIFFERS_FROM_NULL=Y");
bw.close();
fw.close();
}
}