使用过MyBatis的小伙伴们应该知道,MyBatis支持SQL拦截的功能,如果项目使用JFinal,也想使用SQL拦截功能,读JFinal源码发现,JFinal目前还不支持此功能,仅仅只是在Config类中调用SqlReporter在控制台输出SQL。于是,参考这个SqlReporter简单实现了SQL拦截的功能,供有需要的小伙伴参考。
SQL拦截代理类:
/**
* 基于JDK Proxy实现SQL拦截
* @param <T>
*/
public class JdkProxy<T> implements InvocationHandler {
private final Set<String> methods = new HashSet<>(Arrays.asList("getDataSource", "getConnection", "prepareStatement"));
private T target;
public JdkProxy(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public T newProxyInstance() {
Class<?> clazz = target.getClass();
Class<?>[] classes;
if (clazz.isInterface()) {
classes = new Class[]{clazz};
} else {
classes = clazz.getInterfaces();
}
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), classes, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (methods.contains(method.getName())) {
if (("getDataSource".equals(method.getName()) || "getConnection".equals(method.getName())) && (args == null || args.length == 0)) {
return new JdkProxy<Object>(method.invoke(target, args)).newProxyInstance();
}
if ("prepareStatement".equals(method.getName()) && args != null && args.length > 0) {
// 拦截变更sql
args[0] = modifySql(String.valueOf(args[0]));
}
}
return method.invoke(target, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
/**
* 修改sql
* @param originSql
* @return
*/
private String modifySql(String originSql) {
originSql = originSql.trim();
// SQL语句类型:SELECT、INSERT、UPDATE、DELETE、TRUNCATE、ALTER、CREATE、。。。
String sqlType = originSql.substring(0, originSql.indexOf(" ")).toUpperCase();
String sql = simplifySql(originSql);
String datetime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(Instant.now());
switch(sqlType) {
case "DELETE":
// 【拦截删除操作,禁止任何物理删除操作】
sql = "UPDATE" + sql.substring(11, sql.length());
sql = sql.substring(0, sql.toUpperCase().indexOf(" WHERE ")) + " SET deleteFlag = '1', updateAt='" + datetime + "' " + sql.substring(sql.toUpperCase().indexOf(" WHERE "), sql.length());
break;
case "UPDATE":
sql = sql.substring(0, sql.toUpperCase().indexOf(" WHERE ")) + ", updateAt='" + datetime + "' " + sql.substring(sql.toUpperCase().indexOf("WHERE "), sql.length());
break;
case "INSERT":
String upperSql = sql.toUpperCase();
if (upperSql.contains("VALUES")) {
String[] valuesStrArray = {") VALUES(", ") VALUES (", ")VALUES(", ")VALUES ("};
int index = -1;
for (String str : valuesStrArray) {
index = upperSql.indexOf(str);
if (index > 0) {
// TODO 获取当前用户ID
String deptId = "";
sql = sql.substring(0, index) + ",statusCode,updateAt,createBy,createByDeptId) " + sql.substring(index + 1, sql.length());
sql = sql.substring(0, sql.length() - 1) + ",'1','" + datetime + "','" + getCurrentUser() + "','" + deptId + "')";
break;
}
}
if (index <= 0) {
System.err.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>非法的INSERT SQL语句:" + sql.replaceAll("(?m)^\\s*$(\\n|\\r\\n)", ""));
}
}
break;
default:
return sql;
}
System.out.println("变更前sql:" + originSql + "\n变更后sql:" + sql);
return sql;
}
/**
* 获取当前用户ID
* @return
*/
private String getCurrentUser() {
return "74897C57832941ABBB2E596017504C334R92R1SH";
}
/**
* 将SQL语句变成一条语句,并且每个单词的间隔都是1个空格
* @param sql
* @return
*/
private String simplifySql(String sql) {
return sql.replaceAll("[\r\n]", " ").replaceAll("\\s{2,}", " ");
}
}JFinalConfig中使用:
public class Config extends JFinalConfig {
@Override
public void configConstant(Constants me) {}
@Override
public void configEngine(Engine me) {}
@Override
public void configRoute(Routes me) {}
@Override
public void configHandler(Handlers me) {}
@Override
public void configInterceptor(Interceptors me) {}
@Override
public void configPlugin(Plugins me) {
IDataSourceProvider dbPlugin = addDbPlugin(me);
// 配置ActiveRecord插件
ActiveRecordPlugin arp = new ActiveRecordPlugin(dbPlugin);
me.add(arp);
}
/**
* 创建DruidPlugin
* @param me
* @return
*/
private IDataSourceProvider addDbPlugin(Plugins me) {
DruidPlugin druidPlugin = new DruidPlugin(jdbcUrl, jdbcUser, jdbcPassword, jdbcDriver);
me.add(new JdkProxy<>(druidPlugin).newProxyInstance());
// 【注意】不能直接返回druidPlugin
return (IDataSourceProvider) me.getPluginList().get(me.getPluginList().size() - 1);
}
@Override
public void onStart() {}
@Override
public void onStop() {}
}结束。