背景:系统在进行数据导入的时候要进行唯一性校验
一、导入跟踪
1.查看jeecgBoot文档发现其导入功能使用autoPoi,查看官方文档发现autoPoi暂不支持添加数据校验规则
jeecgBoot文档地址 : http://doc.jeecg.com/2044224
autoPoi文档地址 : http://doc.autopoi.jeecg.com/1623974
2.跟踪其导入找到后端接口
前端请求接口
找到后端接口,类为 org.jeecg.modules.online.cgform.b.a
跟踪发现其调用的方法为
可以看到IOnlCgformSqlService是一个接口,路径为 org.jeecg.modules.online.cgform.service.IOnlCgformSqlService
找到其实现类为g类,路径为 org.jeecg.modules.online.cgform.service.impl.g
根据导入的数据量执行不同的操作,最终都调用了this.a方法
查看a方法发现其调用org.jeecg.modules.online.cgform.util.b.a方法后返回一个可执行的的sql变量var8,然后再执行sql语句
注意这个类中还有一个saveOrUpdateSubData方法也调用了this.a,所有我们要想在导入时添加校验必须修改saveBatchOnlineTable方法的逻辑。
二、添加校验规则
思路:根据我们前面的代码跟踪发现org.jeecg.modules.online.cgform.b.a类的onlCgformSqlService属性是通过@Autowired注入,那我们重新实现onlCgformSqlService接口,并将我们自己的类添加到spring容器中。
1.新建接口类IBaseCheckService
@FunctionalInterface
public interface IBaseCheckService<T> {
/**
* 校验数据
* @param t
*/
void checkData(T t);
}
2.在我们要导入的表的Impl类中实现IBaseCheckService接口,重写其checkData方法。
比如数据库表名为sys_user,对应的Impl类为SysUserServiceImpl
implements IBaseCheckService<String>
@Override
public void checkData(String studentStr) {
// 这里写我们的校验逻辑
// 校验不通过抛出异常 throw new RuntimeException("数据校验不通过");
}
3.新建BaseCheck类
public class BaseCheck<T> {
private IBaseCheckService baseCheckService;
public void setBaseCheckService(IBaseCheckService baseCheckService) {
this.baseCheckService = baseCheckService;
}
public BaseCheck() {
}
public void checkData(T t){
baseCheckService.checkData(t);
}
}
4.新建工具类,主要作用是将下划线转成驼峰
/**
* 下划线转驼峰
* @Author
* @Date
* @Description
**/
public class HumpUtil {
/**
* 下划线对应的ASCII
*/
private static final byte ASCII_UNDER_LINE = 95;
/**
* 小写字母a的ASCII
*/
private static final byte ASCII_a = 97;
/**
* 大写字母A的ASCII
*/
private static final byte ASCII_A = 65;
/**
* 小写字母z的ASCII
*/
private static final byte ASCII_z = 122;
/**
* 字母a和A的ASCII差距(a-A的值)
*/
private static final byte ASCII_a_A = ASCII_a - ASCII_A;
public static String toHump(String column) {
byte[] bytes = changeIdx(column);
bytes = removeUnderLine(bytes);
return new String(bytes);
}
/**
* 交换下划线和其后面字符的下标
* 将column从下划线命名方式转换成驼峰命名方式
* 0. 找到‘_’符号的ASCII码(95)对应的下标
* 1. 将下划线的下标的下一个元素转换为大写字段(如果是小写字母的话)并放到下划线对应的下标
* 2. 将下划线下标的下一个元素设置为下划线
* 3. 返回数组
*
* @param column 字段名称
*/
private static byte[] changeIdx(String column) {
byte[] bytes = column.getBytes();
for (int i = 0; i < bytes.length; i++) {
if (bytes[i] == ASCII_UNDER_LINE) {
if (i < bytes.length - 1) {
bytes[i] = toUpper(bytes[i + 1]);
bytes[i + 1] = ASCII_UNDER_LINE;
i++;
}
}
}
return bytes;
}
/**
* 将参数b转换为大写字母,小写字母ASCII范围(97~122)
* 0. 判断参数是否为小写字母
* 1. 将小写字母转换为大写字母(减去32)
*/
private static byte toUpper(byte b) {
if (b >= ASCII_a && b <= ASCII_z) {
return (byte) (b - ASCII_a_A);
}
return b;
}
/**
* 去除所有下划线
* 0. 新创建一个数组
* 1. 将所有非下划线字符都放入新数组中
*
* @param bytes 原始数组
* @return 处理后的字节数组
*/
private static byte[] removeUnderLine(byte[] bytes) {
// 存放非下划线字符的数量
int count = 0;
for (byte b : bytes) {
if (b == ASCII_UNDER_LINE) {
continue;
}
count++;
}
byte[] nBytes = new byte[count];
count = 0;
for (byte b : bytes) {
if (b == ASCII_UNDER_LINE) {
continue;
}
nBytes[count] = b;
count++;
}
return nBytes;
}
}
5.新建MyIOnlCgformSqlServiceImpl类实现IOnlCgformSqlService接口,复制org.jeecg.modules.online.cgform.service.impl.g类的代码,在saveBatchOnlineTable中调用this.a方法前调用数据校验的方法
代码如下
/**
* org.jeecg.modules.online.cgform.service.impl.g
* @Author
* @Date
* @Description
**/
public class MyIOnlCgformSqlServiceImpl implements IOnlCgformSqlService {
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
@Autowired
private IOnlCgformHeadService onlCgformHeadService;
public MyIOnlCgformSqlServiceImpl() {
}
@Override
public void saveBatchOnlineTable(OnlCgformHead head, List<OnlCgformField> fieldList, List<Map<String, Object>> dataList) throws BusinessException {
SqlSession var4 = null;
try {
b.a(2, dataList, fieldList);
var4 = this.sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
OnlCgformFieldMapper var5 = (OnlCgformFieldMapper)var4.getMapper(OnlCgformFieldMapper.class);
short var6 = 1000;
int var7;
String var8;
if (var6 >= dataList.size()) {
for(var7 = 0; var7 < dataList.size(); ++var7) {
var8 = JSON.toJSONString(dataList.get(var7));
this.checkData(var8, head);
this.a(var8, head, fieldList, var5);
}
} else {
for(var7 = 0; var7 < dataList.size(); ++var7) {
var8 = JSON.toJSONString(dataList.get(var7));
this.checkData(var8, head);
this.a(var8, head, fieldList, var5);
if (var7 % var6 == 0) {
var4.commit();
var4.clearCache();
}
}
}
var4.commit();
} catch (Exception var12) {
throw new BusinessException(var12.getMessage());
} finally {
var4.close();
}
}
@Override
public void saveOrUpdateSubData(String subDataJsonStr, OnlCgformHead head, List<OnlCgformField> subFiledList) throws BusinessException {
OnlCgformFieldMapper var4 = (OnlCgformFieldMapper) SpringContextUtils.getBean(OnlCgformFieldMapper.class);
this.a(subDataJsonStr, head, subFiledList, var4);
}
private void a(String var1, OnlCgformHead var2, List<OnlCgformField> var3, OnlCgformFieldMapper var4) throws BusinessException {
JSONObject var5 = JSONObject.parseObject(var1);
int var6 = this.onlCgformHeadService.executeEnhanceJava("import", "start", var2, var5);
String var7 = var2.getTableName();
Map var8;
if (1 == var6) {
var8 = org.jeecg.modules.online.cgform.util.b.a(var7, var3, var5);
var4.executeInsertSQL(var8);
} else if (2 == var6) {
var8 = org.jeecg.modules.online.cgform.util.b.b(var7, var3, var5);
var4.executeUpdatetSQL(var8);
} else if (0 == var6) {
}
}
/**
* @param var1 导入的行数据
* @param var2 表信息
*/
private void checkData(String var1, OnlCgformHead var2) {
// 将表明转成驼峰
String service = HumpUtil.toHump(var2.getTableName());
BaseCheck<String> studentBaseCheck = new BaseCheck<>();
// 获取spring容器中对应的bean并设值
studentBaseCheck.setBaseCheckService(SpringContextUtils.getBean(service + "ServiceImpl", IBaseCheckService.class));
// 调用校验方法
studentBaseCheck.checkData(var1);
}
}
6.新建配置类,将spring容器中名为onlCgformSqlServiceImpl的bean移除,并将我们自己实现类的bean添加到spring容器中。beanName对应实现IOnlCgformSqlService接口的g类。
/**
* 前端页面导入按钮
* @Author
* @Date
* @Description
**/
@Configuration
public class ModifyImportConfig {
@Bean
public void modifyFormSqlServiceImplBean(){
String beanName="onlCgformSqlServiceImpl";
//获取context.
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) SpringContextUtils.getApplicationContext();
//获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableApplicationContext.getBeanFactory();
Object oldBean = SpringContextUtils.getBean(beanName);
if (!ObjectUtils.isEmpty(oldBean)){
// 移除
defaultListableBeanFactory.removeBeanDefinition(beanName);
}
// 重新注册
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyIOnlCgformSqlServiceImpl.class);
defaultListableBeanFactory.registerBeanDefinition(beanName,beanDefinitionBuilder.getBeanDefinition());
}
}
7.重启项目
注意:表对应的impl实现类一定要实现我们自定的校验接口,并重写checkData方法,否则在执行MyIOnlCgformSqlServiceImpl的checkData方法会报错