JFinal 3.0 Sql 管理与动态生成

3.0使用Template Engine 实现了 Sql 管理功能,但为了项目后期的管理与维护,我需要知道项目运行过程中详细的日志信息,所以我设置了

arp.setShowSql(true);

打印sql语句,以便后期帮助我分析错误。

但是将sql迁移到外部文件中以后,为了保持配置文件sql的良好可读性,我换行编辑代码:

#sql("getByIds")
    select * from test where id in (
        #for(id : ids)
            #(id)
            #if(!for.last)
                ,
            #end
        #end
    )
#end

实现一个in查询:

select * from test where id in (1,2,3)

sql语句拼接时并不能过滤换行符,导致我日志中输出的sql语句根本无法阅读:

Sql: select * from test where id in (

1

,

2

,

3

)

如果遇到更复杂的sql,这显然不是一种好的体验方式,请问有什么好的解决办法可以解决此问题,谢谢。

评论区

aloneJFinal

2017-03-29 17:22

需要去掉字符串中的\n与\t

aloneJFinal

2017-03-29 17:32

我的处理方式是,接管System.out流自己进行处理:
package com.alone.mate.utils;

import java.io.PrintStream;

import org.apache.log4j.Logger;

/**
* 将所有System.out流转发到自定义流中以便用log4j记录日志
*
* @author alone
*/
public class LogUtils {

private static final Logger logger = Logger.getLogger(LogUtils.class);

public static void init() {
PrintStream printStream = new PrintStream(System.out) {

@Override
public void print(boolean b) {
log(Boolean.valueOf(b));
}

@Override
public void print(char c) {
log(Character.valueOf(c));
}

@Override
public void print(char[] s) {
log(s == null ? null : new String(s));
}

@Override
public void print(double d) {
log(Double.valueOf(d));
}

@Override
public void print(float f) {
log(Float.valueOf(f));
}

@Override
public void print(int i) {
log(Integer.valueOf(i));
}

@Override
public void print(long l) {
log(l);
}

@Override
public void print(Object obj) {
log(obj);
}

@Override
public void print(String s) {
log(s);
}

@Override
public void println(boolean b) {
logLn(Boolean.valueOf(b));
}

@Override
public void println(char c) {
logLn(Character.valueOf(c));
}

@Override
public void println(char[] s) {
logLn(s == null ? null : new String(s));
}

@Override
public void println(double d) {
logLn(Double.valueOf(d));
}

@Override
public void println(float f) {
logLn(Float.valueOf(f));
}

@Override
public void println(int i) {
logLn(Integer.valueOf(i));
}

@Override
public void println(long l) {
logLn(l);
}

@Override
public void println(Object obj) {
logLn(obj);
}

@Override
public void println(String s) {
logLn(s);
}
};
System.setOut(printStream);
System.setErr(printStream);
}

private static void log(Object info) {
if (info.toString().startsWith("Sql:")) {
info = info.toString().replace("\n", "").replace("\t", "");
}
logger.info("System.out.print: " + info);
}

private static void logLn(Object info) {
if (info.toString().startsWith("Sql:")) {
info = info.toString().replace("\n", "").replace("\t", "");
}
logger.info("System.out.println: " + info);
}
}

手动获取SqlReporter类中invoke方法打印的sql,进行判断,从而去除sql中的\n 与 \t,但是这种方法治标不治本,不能从源头上解决问题。

JFinal

2017-03-30 10:19

template engine 是在大量固定文本中嵌入少量动态代码,所以这些大量文本也是生成的内容的一部分,模板引擎通常不方便主动去除,因为这些换行回也许是用户本来就需要的

所以,将下面的代码改一下:
#if(!for.last)
,
#end

改成:
#if(!for.last) , #end

也就是说,不需要保留的回车与换行在模板中就去掉它。jfinal 默认对独占一行的指令所在行的回车换行进行过删除,可能 for 与 if 嵌套的情况还有一点小瑕疵,正在改进

widely

2017-03-30 11:12

@JFinal 请教 下怎么在外部sql文件中 使用if 指令

JFinal

2017-03-30 18:33

@widely 外部 sql 管理功能支持 jfinal template engine 的所有功能,所以 if for 这类指令全都支持,根据 jfinal 手册中介绍的 jfinal template engine 功能就一下就好

逍遥一生

2017-03-31 14:27

@JFinal 波总这个需要改进一下 不然页面中通过F12看代码有点乱,多了很多换行,看起来不美观。如果解析后可以自动过滤掉模版中多余的空格和换行就完美了,这样 jfinal template engine用起来就舒服了

JFinal

2017-03-31 15:10

@逍遥一生 必须会的,后续版本会打磨这个模块

siyuan

2017-04-01 01:46

@JFinal

--------------------------------------------------------------------------------
Sql:
select * from d_user where bigId in(12,1)

JFinal action report -------- 2017-04-01 00:49:09 ------------------------------
Url : GET /test5
Controller : web.controller.Index.(Index.java:1)
Method : test5
Parameter : id[]={12,1}
--------------------------------------------------------------------------------
Sql:
select * from d_user where bigId in('12',1)

JFinal action report -------- 2017-04-01 00:49:31 ------------------------------
Url : GET /test5
Controller : web.controller.Index.(Index.java:1)
Method : test5
Parameter : id[]={'12',1}
--------------------------------------------------------------------------------
像楼主这样拼装SQL会有注入漏洞吧!!能否做到完全的prepareStatement呢?

JFinal

2017-04-01 10:43

@siyuan jfinal 数据库操作全部使用 PreparedStatement,无法被 SQL 注入,如果还是不放心,在 DruidPlugin 中配置一个 WallFilter

siyuan

2017-04-01 23:42

@JFinal 但在没有使用Druid的情况,SQL Template拼装 in 条件时会有驻入问题,因为是拼装,如果 in 字段为字符类型
那么http://localhost:8080/test5?id='0') or 1=1 or bigId in(1&id=3

拼装的SQL如下:

Sql: select vcPassword "密码",vcAccount "账号" from d_user where bigId in('0') or 1=1 or bigId in(1,3)

JFinal action report -------- 2017-04-01 23:38:23 ------------------------------
Url : GET /test5
Controller : web.controller.Index.(Index.java:1)
Method : test5
Parameter : id[]={'0') or 1=1 or bigId in(1,3}
--------------------------------------------------------------------------------

JFinal

2017-04-01 23:52

@siyuan 真实环境去测试一下,preparestatement 是可以防止 sql 注入的

siyuan

2017-04-02 00:29

@JFinal
就是真实环境,记录都查出来了~,现在是可以用drud的WallFilter实现防注入,只是想SQL Template层面能否实现,如mybatis的
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
它首先生成 select * from post p where id in(?,?,..) 再使用 preparestatement设置list参数所以它没有注入的问题

JFinal

2017-04-02 11:01

@siyuan 专业的人干专业的事,所以防注入的事情还是交给 Druid 的 WallFilter 吧

zhaofan

2018-07-14 13:29

@JFinal 我试了下 DB.find("select * from user where name ='' or 1=1#'");注入成功,是我的使用方式不对么?

JFinal

2018-07-14 15:42

select * from user where name ='' or 1=1#' 这个是条正常的 sql ,与注入没有关系

jfinal 内部所有数据库操作 API 使用的是 preparestatement,这个东东是要配合问号占位符的,例如:
Object para = "or 1=1#'";
Db.find("select * from user where name =? ", para);

像上面一样来用,无论你组装什么样的参数都没有用

黄天霸

2018-08-21 15:41

兄弟 你这个sql 打印的时候 怎么才能把参数值打印出来 我这边sql 打印出来都是带?号的

JFinal

2018-08-21 17:15

@siyuan 才注意到你的回复内涵,jfinal 的 sql 管理功能当然会支持生成问号占位符来防止 sql 注入,注意看文档,要使用 #para() 指令,那么就会像 mybatis 一样生成这样的 sql :
select * from post p where id in(?,?,..)

#para 指令的用法:
http://www.jfinal.com/doc/5-13

JFinal

2018-08-21 17:17

@黄天霸 参考这几个:
http://www.jfinal.com/share/492
https://www.dreamlu.net/druid/jfinal/2017/09/22/Print-executable-SQL-using-the-Druid-LogFilter.html
http://www.jfinal.com/share/324

黄天霸

2018-08-22 09:59

@JFinal 好的 感谢大神指导

JJfinal

2019-01-30 18:58

哇 , 我和你写的一模一样

热门反馈

扫码入社