目前代码写的很随性,导致以后业务增加时拓展起来繁杂,所以我们将已有逻辑进行第一重构:
package com.xiaoma.fileimport.common;
/**
* 任务主执行类
* 使用工厂模式,首先将任务共同行为抽象出来
*
* @author mawuhui
* @since 2020-06-30 17:59
*/
public interface BatTaskRun {
/**
* 具体任务执行
*
* @param taskNo 任务编号
* @return
*/
int execute(String taskNo);
/**
* 具体任务执行
*
* @param taskNo 任务编号
* @param taskDate 任务日期
* @return
*/
int execute(String taskNo, String taskDate);
}
package com.xiaoma.fileimport.common;
import com.xiaoma.fileimport.handler.Processor;
import com.xiaoma.fileimport.service.FileReader;
import com.xiaoma.fileimport.service.FileToDataBase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @author mawuhui
* @since 2020-07-01 13:42
*/
public abstract class AbsBatTaskRun implements BatTaskRun {
@Autowired
public FileReader reader;
@Autowired
public FileToDataBase fileToDataBase;
@Autowired
public Processor processor;
@Autowired
public JdbcTemplate jdbcTemplate;
@Override
public int execute(String taskNo) {
// 未传入时间的时候使用当前时间 如 20200630
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMdd");
String taskDate = LocalDate.now().format(df);
return execute(taskNo, taskDate);
}
// 导入功能整体逻辑确定,部分功能不确认,抽象成方法,具体子类中去实现
@Override
public int execute(String taskNo, String taskDate) {
// 文件名获取
String fileName = String.format("%s_%s", taskNo, taskDate);
// 1. 信号文件读取
List<String> ctlFile = reader.readTxtFile(String.format("E:\\project\\%s.ctl", fileName));
// 2. 数据文件读取
List<String> datFile = reader.readTxtFile(String.format("E:\\project\\%s.dat", fileName));
// 3. 校验行数
/** 信号文件中的行数 */
Integer ctllines = Integer.parseInt(ctlFile.get(3).split("=")[1]);
/** 数据文件中的行数 */
Integer datlines = datFile.size();
if (!ctllines.equals(datlines)) {
throw new RuntimeException(String.format("文件校验出错,文件实际条数:%s,信号文件条数:%s;", datlines, ctllines));
}
// 入库前操作
preProcessor(taskNo);
// 4. 执行入库
fileToDataBase.execute(ctlFile, datFile);
// 入库后操作
postProcessor(taskNo);
return 0;
}
/**
* 前置处理
*
* @param taskNo
* @return
*/
public abstract boolean preProcessor(String taskNo);
/**
* 后置处理
*
* @param taskNo
* @return
*/
public abstract boolean postProcessor(String taskNo);
}
修改T_USER_20200630.ctl文件
添加 数据类型 为增量的标志 type=ADD
columnList=[user_name]%@#%[sex]%@#%[note]%@#%
split=%@#%
tableName=T_USER
rows=2
type=ADD
修改myfile_20200630.ctl文件
添加 数据类型 为全量的标志 type=ALL
columnList=[name]%@#%[age]%@#%[language]%@#%[text]%@#%[email]%@#%
split=%@#%
tableName=MYFILE
rows=3
type=ALL
文件数据解析导入数据库类修改 FileToDataBase.java
package com.xiaoma.fileimport.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 文件数据解析导入数据库
*
* @author mawuhui
* @since 2020-06-30 18:21
*/
@Component
public class FileToDataBase {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* @param ctlFile
* @param datFile
* @return
*/
public int execute(List<String> ctlFile, List<String> datFile) {
String[] sql = buildSql(ctlFile, datFile);
jdbcTemplate.batchUpdate(sql);
return 0;
}
/**
* @param ctlFile
* @param datFile
* @return
*/
public String[] buildSql(List<String> ctlFile, List<String> datFile) {
String[] s = new String[datFile.size()];
String tableName = ctlFile.get(2).split("=")[1];
String split = ctlFile.get(1).split("=")[1];
// 获取数据文件类型: 全量提供ALL, 增量提供ADD;
// 全量提供无变化,增量提供,数据实际导入到临时表中,需要修改TABLENAME
String type = ctlFile.get(4).split("=")[1];
if (type.equals("ADD")) {
tableName = tableName + "_TEMP";
}
// 组装insert语句
StringBuilder builder1 = new StringBuilder();
builder1.append("insert into ").append(tableName).append("(");
String columns = ctlFile.get(0).split("=")[1].replace("%@#%", "").replace("][", ",");
builder1.append(columns.substring(1, columns.length() - 1));
builder1.append(") values(");
int i = 0;
for (String line : datFile) {
List<String> temp = Arrays.asList(line.split(split));
String dat = temp.stream().map(item -> "'" + item + "'").collect(Collectors.joining(","));
s[i] = builder1.toString() + dat + ")";
i++;
}
return s;
}
}
package com.xiaoma.fileimport.task;
import com.xiaoma.fileimport.common.AbsBatTaskRun;
import org.springframework.stereotype.Component;
/**
* 在定义组件的bean name 时 定义为任务编号 taskNo
* @author mawuhui
* @since 2020-07-01 13:34
*/
@Component(value = "myfile")
public class MyFile extends AbsBatTaskRun {
@Override
public boolean preProcessor(String taskNo) {
jdbcTemplate.execute("truncate table " + taskNo);
return true;
}
@Override
public boolean postProcessor(String taskNo) {
return true;
}
}
package com.xiaoma.fileimport.task;
import com.xiaoma.fileimport.common.AbsBatTaskRun;
import org.springframework.stereotype.Component;
/**
* 在定义组件的bean name 时 定义为任务编号 taskNo
* @author mawuhui
* @since 2020-07-01 14:03
*/
@Component(value = "T_USER")
public class TUser extends AbsBatTaskRun {
@Override
public boolean preProcessor(String taskNo) {
jdbcTemplate.execute(String.format("truncate table %s_TEMP", taskNo));
return true;
}
@Override
public boolean postProcessor(String taskNo) {
jdbcTemplate.execute("call updateTUser");
return true;
}
}
工厂模式配合spring容器bean获取具体的子实现类
package com.xiaoma.fileimport.common;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author mawuhui
* @since 2020-07-01 13:45
*/
@Component
public class TaskFactory implements ApplicationContextAware {
@Autowired
private ApplicationContext applicationContext;
/**
* 整合了spring 所以直接使用了spring ioc容器的中获取
*
* @param taskNo
* @return
*/
public BatTaskRun getBatTaskRun(String taskNo) {
return (BatTaskRun) applicationContext.getBean(taskNo);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (this.applicationContext == null) {
this.applicationContext = applicationContext;
}
}
}
package com.xiaoma.fileimport;
import com.xiaoma.fileimport.common.BatTaskRun;
import com.xiaoma.fileimport.common.TaskFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FileImportApplication implements CommandLineRunner {
@Autowired
TaskFactory taskFactory;
public static void main(String[] args) {
//参数写一个就是使用当前日期,当提供测试文件日期不为当前日期的时候,传入日期为参数即可
//SpringApplication.run(FileImportApplication.class, new String[]{"myfile"});
SpringApplication.run(FileImportApplication.class, new String[]{"T_USER", "20200630"});
}
/**
* 文件导入数据库执行入口
* 通过配置根据传入的文件导入编号找到对应的文件名
*
* @param args
* @throws Exception
*/
@Override
public void run(String... args) throws Exception {
String taskNo = args[0];
// 获取方式修改为工厂获取模式
BatTaskRun batTaskRun = taskFactory.getBatTaskRun(taskNo);
if (args.length == 2) {
String taskDate = args[1];
batTaskRun.execute(taskNo, taskDate);
} else {
batTaskRun.execute(taskNo);
}
}
}
修改T_USER_20200630.dat
文件内容
mawuhui4%@#%0%@#%js%@#%
mawuhui3%@#%0%@#%redis%@#%
数据库中原有数据
执行FileImportApplication中main方法后,查看数据库数据
就不会产生新的bug
,调用方只用给定需要的任务编号即可,即可正确的解析文件入库;以上重构针对给定格式的文件,需求总是多变的,下一节小马又将面对新的需求数据文件格式为其他格式的
;