多条件检索SQL语句(Multi-condition SQL)

                    多条件检索SQL语句(Multi-conditio SQL)
0.应用场景
一般情况下,MIS系统中会出现检索的功能,而检索的时候可以包含多种语义的要求,例如前方一致,模糊匹配,多条件检索等等。考虑以下的场景:
在一个DNS管理系统中,包含了IP地址,主机名,主机区域(Location)的信息,可以根据这三个条件进行与/或的检索,例如如下的Interface:
Condition:(AND/OR)    IP address:_______    Hostname:_______    Location_______
语义说明如下:
a.用户可以通过Radio按钮来选择"与"条件还是"或"条件
(当然,这里还可以更加负责,针对每两个条件使用与/或)
b.如果输入框未输入值或者输入空白字符,不考虑该条件
当输入框全部未输入或者输入的都是空白字符时,无论是"与"条件还是"或"条件,都进行全检索
c.IP地址中包含的*号代表任意多个任意字符
d._和%作为任意字符进行处理

1.测试环境
JDK1.6
Oracle 10gR2

2.解决方案:
a.使用Statement进行条件的拼接,如下所示:
StringBuffer buffer = new StringBuffer("SELECT * FROM HOST WHERE "); if (ip != null && !ip.trim().equals("")) { ip = ip.replaceAll("//*", "%"); // TODO buffer.append("IP LIKE '" + ip + "' "); if (andOp) { buffer.append("AND "); } else { buffer.append("OR "); } } if (hostname != null && !hostname.trim().equals("")) { buffer.append("HOSTNAME LIKE '" + hostname + "' "); // TODO if (andOp) { buffer.append("AND "); } else { buffer.append("OR "); } } if (location != null && !location.trim().equals("")) { // SAME AS HOSTNAME } // TODO final String QUERY = buffer.toString(); rs = stmt.executeQuery(QUERY);
上面代码中,TODO部分需要完成三部分工作:
i.需要对SQL的特殊字符进行替换,例如'等,同时对于字符序列"--"也要进行处理(这是一种典型的SQL注入的攻击方式)
ii.最后,还需要判断WHERE后面是否有条件子句,如果没有,要对查询语句进行处理(也可以使用一些flag变量,在过程中进行处理)
iii.需要对SQL LIKE语句中的匹配符进行转义,例如_和%。
尤其是第一部分的工作,做起来比较麻烦。另外,组装SQL查询语句的过程也比较复杂和难于理解。

b.使用预编译的PreparedStatement,如下所示:
private static final String SEARCH_HOST_INFO_AND = "SELECT *" + " FROM HOST" + " WHERE (IP LIKE ? OR 1 = ?)" + " AND (HOSTNAME LIKE ? OR 1 = ?)" + " AND (LOCATION LIKE ? OR 1 = ?)"; private static final String SEARCH_HOST_INFO_OR = "SELECT *" + " FROM HOST" + " WHERE (IP LIKE ? AND 1 = ?)" + " OR (HOSTNAME LIKE ? AND 1 = ?)" + " OR (LOCATION LIKE ? AND 1 = ?)"; if (andOp) { stmt = conn.prepareStatement(SEARCH_HOST_INFO_AND); if (ip != null && !ip.trim().equals("")) { stmt.setInt(2, 0); } else { stmt.setInt(2, 1); } if (hostname != null && !hostname.trim().equals("")) { stmt.setInt(4, 0); } else { stmt.setInt(4, 1); } if (location != null && !location.trim().equals("")) { // SAME AS HOSTNAME } } else { stmt = conn.prepareStatement(SEARCH_HOST_INFO_OR); // TODO if (ip != null && !ip.trim().equals("")) { stmt.setInt(2, 1); } else { stmt.setInt(2, 0); } if (hostname != null && !hostname.trim().equals("")) { stmt.setInt(4, 1); } else { stmt.setInt(4, 0); } if (location != null && !location.trim().equals("")) { // SAME AS HOSTNAME } // TODO stmt.setString(1, ip.replaceAll("//*", "%")); stmt.setString(3, "%" + hostname + "%"); rs = stmt.executeQuery();
这种方式还需要完成以下工作:
i.当所有的输入框全部为空时,条件为OR时,检索结果为0件。
ii.需要对SQL LIKE语句中的匹配符进行转义。
虽然这种方式也不是十分的完美,但是相对于第一种方式,已经改善了一些,起码条理清楚了很多。

笔者也在寻求更好的方式,如果您有更好的方式,希望能够不吝赐教。
在此先谢过了。
3.总结
在更多的情况下,PreparedStatement是优于Statement的选择,而使用setType(index,value)也比SQL语句的字符串拼接具有更好的可读性。

你可能感兴趣的:(sql,工作,String,null,search,query)