我正在创建一个基于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"
因此,正如您可能想象的那样,我的查询构建代码很长,具有所有这些不同的条件。有关更好的方法的任何建议?
答案 0 :(得分:3)
字符串连接不是SQL的最佳选择。您可以做的最好的事情是使用QueryDSL或JOOQ并以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)
从简单的角度来看
如果您知道所有类型的列,则可以使用数组和表来使此代码更多地受数据驱动。