最佳实践 - 根据用户选择构建查询

时间:2012-10-25 22:11:28

标签: java sql database oracle

我正在创建一个基于Web的小工具,允许用户以各种方式查询大型查找表。由于它是一个小工具,我使用JSP / Servlets。

查找表的定义如下:

column1 | column2 | column3 |日期|用户|计数

用户可以按列值或范围进行查询。此外,结果可以按特定列进行排序。因为该表有数十万条记录,而且增长迅速,我使用的是Oracle的ROWNUM,只返回了一小部分结果。

我有一个表单页面,可以从用户那里获取搜索条件。我根据一系列条件生成查询,例如:

query = "SELECT * FROM mytable WHERE 1=1 "
if(searchCriteria1 != "")
    query += "AND column1='searchCriteria1' "
if(searchCriteria2 != "")
    query += "AND column2='searchCriteria2' "
if(searchCriteria2 != "")
    query += "AND column2='searchCriteria2' "
if(searchCriteria3 != "")
    query += "AND column3='searchCriteria3' "
if((searchCriteria4 != "") && (searchCriteria5 != ""))
    query += "AND date>='searchCriteria4' AND date<='searchCriteria5' "
etc...

(这只是简化的伪代码)

在显示第一个结果页面后处理排序。用户单击该页面的列标题以按该列排序。这将是回发和查询数据库。基本上,我在上面运行相同的代码但最后是这样:

if(sortColumn1)
    query += "ORDER BY column1"
if(sortColumn2)
    query += "ORDER BY column2"
if(sortColumn3)
    query += "ORDER BY column3"

因此,正如您可能想象的那样,我的查询构建代码很长,具有所有这些不同的条件。有关更好的方法的任何建议?

5 个答案:

答案 0 :(得分:3)

字符串连接不是SQL的最佳选择。您可以做的最好的事情是使用QueryDSLJOOQ并以OO方式执行此操作。我对QueryDSL比较熟悉。查看示例here

SQLQuery query = new SQLQueryImpl(connection, dialect); 
query.from(myTable);

BooleanBuilder wheres = new BooleanBuilder();

if(notBlank(searchCriteria1))
   wheres.and(myTable.column1.eq(searchCriteria1));
if(notBlank(searchCriteria2))
   wheres.and(myTable.column2.eq(searchCriteria2));
if(notBlank(searchCriteria4) && notBlank(searchCriteria5))
   wheres.andAllOf(myTable.date.goe(searchCriteria4),  myTable.date.loe(searchCriteria5));  //you may want to use myTable.date.between(...)

if (...) {
   query.orderBy(myTable.column1.asc());
} else if (...){
   query.orderBy(myTable.column2.asc());
}

query.limit(100); //it is good to limit a result
query.list(myTable.all());

对于SQL注入,查询引擎会在构造SQL时将您的参数包装在命名参数中。

答案 1 :(得分:2)

首先,不要在查询中使用简单的String。您应该使用PreparedStatement来保护您免受SQL injection攻击(恶意用户破坏易受攻击的系统的一种非常简单的方式)。

其次,如果您的输入真的那么复杂,那么您将无法避免if()... else if()...块。看起来你的方法做得比它应该做的多得多。考虑根据用户想要运行的查询类型调用不同的方法。我已经实现了许多DAO,在if块中添加AND行时,无法绕过WHERE块中的所有空检查。

但是,你可以肯定做的一件事就是在一个private static final String常量中写下大部分查询,并在你想要参数的地方写上问号(?),附加你需要的其他标准根据用户输入,直接从中构建PreparedStatement。完成PreparedStatement后,您可以使用各种setter命令填写?。这很好地整理了代码。

答案 2 :(得分:1)

永远不会使用字符串连接将用户输入的数据放入查询中。您应该在查询中使用命名参数或位置参数并传递参数。

答案 3 :(得分:0)

我认为向后端发送请求进行排序并不是一个好主意。 js中有很多插件可以对表进行排序,可以使用它。

答案 4 :(得分:0)

从简单的角度来看

如果您知道所有类型的列,则可以使用数组和表来使此代码更多地受数据驱动。