在Web开发中,多种条件的综合查询非常常见,应对这种业务需求我们通常使用下面几种方法来实现:
PS:
i.(Hibernate的条件查询Criteria Queries当然是非常方便的,本文仅探讨JDBC方式的使用)
ii.(欢迎高手、大拿们拍砖,请勿人身攻击)


1.直接将参数值拼接到SQL语句中,然后进行查询。
这种方式的安全性应当说是比较差的,一不小心就被SQL注入了。虽然可以先过滤参数值中的特殊字符,但总感觉不是很优雅。
2.先使用占位符'?'来拼接SQL,然后再通过条件判断去填充PreparedStatement。
用过这种方式的TX,都知道这种方式的复杂性。先要在拼SQL时判断一次,然后还要在填充pst时再判断一次,麻烦。
3.存储过程
本人一直不爽存储过程,以前有一个项目从MySQL迁移到MSSQL,后来又换成ORACLE,最后产品的不同版本运行在不同数据库上,当时差点要了亲命了。

其实讲这么多无非就是想要一种相对优雅简单的查询方式,前一段看到.NET中的SQLHelper受到一些启发,然后就写了这么一个类似的组件(其实我Google了半个小时都没有找到符合要求的)。

基本思想:
1.在控制器中(Servlet/Action etc...)中将查询表单中的参数值添加到查询条件列表中
2.在DAO(你明白的)中设置基本SQL
3.迭代查询条件列表,使用占位符拼接SQL
4.在DAO中拿SQL创建PreparedStatement
5.迭代查询条件列表,为pst填充值
6.接下来,没有特殊的地方了,就是正常的executeQuery,while(rs.next()){...}
将第1/2/3/5步骤的功能提取出来做成一个工具类,然后其它地方就可以随意使用了,嘿嘿

好了,不啰嗦了直接上代码吧

Parameter.java

Java代码 复制代码 收藏代码
  1. /**
  2. * 查询参数类,用于表示条件参数对象
  3. * @author Lixor(at)live.cn
  4. *
  5. */
  6. public class Parameter{
  7. private String field;
  8. private Object value;
  9. private String operator;
  10. /**
  11. *
  12. * @param field 数据库字段名
  13. * @param operator 数据库操作符 =、>=、<、like etc...
  14. * @param value 参数值 Object
  15. */
  16. public Parameter(String field,String operator, Object value) {
  17. super();
  18. this.field = field;
  19. this.value = value;
  20. this.operator = operator;
  21. }
  22. public String getField() {
  23. return field;
  24. }
  25. public Object getValue() {
  26. return value;
  27. }
  28. public String getOperator() {
  29. return operator;
  30. }
  31. }
/**
 * 查询参数类,用于表示条件参数对象
 * @author Lixor(at)live.cn
 *
 */
public class Parameter{
	private String field;
	private Object value;
	private String operator;
	/**
	 * 
	 * @param field 数据库字段名
	 * @param operator 数据库操作符 =、>=、<、like etc...
	 * @param value 参数值 Object
	 */
	public Parameter(String field,String operator, Object value) {
		super();
		this.field = field;
		this.value = value;
		this.operator = operator;
	}
	public String getField() {
		return field;
	}
	public Object getValue() {
		return value;
	}
	public String getOperator() {
		return operator;
	}	
}



DynamicQuery.java

Java代码 复制代码 收藏代码
  1. /**
  2. * 动态查询工具类,用于拼接SQL、填充pst
  3. * @author Lixor(at)live.cn
  4. *
  5. */
  6. public class DynamicQuery {
  7. private static Logger logger=Logger.getLogger(DynamicQuery.class);
  8. private String templet = " AND %s %s ?";
  9. private String baseSql;
  10. private ArrayList<Parameter> parameters = new ArrayList<Parameter>();
  11. public DynamicQuery() {
  12. }
  13. /**
  14. * 要求baseSql带有where条件
  15. *
  16. * @param baseSql
  17. */
  18. public void setBaseSql(String baseSql) {
  19. this.baseSql = baseSql;
  20. }
  21. public void addParameter(Parameter parameter) {
  22. parameters.add(parameter);
  23. }
  24. public String generateSql() {
  25. StringBuffer buffer = new StringBuffer(baseSql);
  26. for (Parameter p : parameters) {
  27. buffer.append(String.format(templet, p.getField(), p.getOperator()));
  28. }
  29. logger.debug(buffer);
  30. return buffer.toString();
  31. }
  32. public void fillPreparedStatement(PreparedStatement pst) throws SQLException {
  33. int count = 1;
  34. for (Parameter p : parameters) {
  35. pst.setObject(count, p.getValue());
  36. count++;
  37. }
  38. }
  39. }
/**
 * 动态查询工具类,用于拼接SQL、填充pst
 * @author Lixor(at)live.cn
 *
 */
public class DynamicQuery {
	private static Logger logger=Logger.getLogger(DynamicQuery.class);
	
	private String templet = " AND %s %s ?";
	private String baseSql;
	private ArrayList<Parameter> parameters = new ArrayList<Parameter>();

	public DynamicQuery() {

	}

	/**
	 * 要求baseSql带有where条件
	 * 
	 * @param baseSql
	 */
	public void setBaseSql(String baseSql) {
		this.baseSql = baseSql;
	}

	public void addParameter(Parameter parameter) {
		parameters.add(parameter);
	}

	public String generateSql() {
		StringBuffer buffer = new StringBuffer(baseSql);
		for (Parameter p : parameters) {
			buffer.append(String.format(templet, p.getField(), p.getOperator()));
		}
		logger.debug(buffer);
		return buffer.toString();
	}

	public void fillPreparedStatement(PreparedStatement pst) throws SQLException {
		int count = 1;		
		for (Parameter p : parameters) {
			pst.setObject(count, p.getValue());
			count++;
		}
	}


}



示例:查询产品信息

QueryServlet.java

Java代码 复制代码 收藏代码
  1. DynamicQuery query=new DynamicQuery();
  2. query.addParameter(new Parameter("p.name" ,"like","电视"));
  3. query.addParameter(new Parameter("p.type_id","=" ,1));
  4. query.addParameter(new Parameter("p.productDate" ,">=",java.sql.Date.valueOf("2010-09-04")));
  5. query.addParameter(new Parameter("p.price" ,">=",1000.0f));
  6. ProductDao dao=new ProductDao();
  7. List<Product> productList=dao.query(query);
  8. request.setAttribute("productList",productList);
DynamicQuery query=new DynamicQuery();
query.addParameter(new Parameter("p.name" ,"like","电视"));
query.addParameter(new Parameter("p.type_id","=" ,1));
query.addParameter(new Parameter("p.productDate" ,">=",java.sql.Date.valueOf("2010-09-04")));
query.addParameter(new Parameter("p.price" ,">=",1000.0f));

ProductDao dao=new ProductDao();
List<Product> productList=dao.query(query);

request.setAttribute("productList",productList);



ProductDao.java

Java代码 复制代码 收藏代码
  1. public List<Product> query(DynamicQuery query) {
  2. List<Product> productList = new ArrayList<Product>();
  3. try {
  4. String sql = "SELECT p.id,p.name,p.price,p.productDate,p.image,p.type_id,t.name,p.description FROM tbl_product p,tbl_type t WHERE p.type_id=t.id";
  5. query.setBaseSql(sql);
  6. sql = query.generateSql();//如果想排序,自行在sql后添加
  7. Connection conn = null;
  8. try {
  9. conn = DbUtil.getConnection();
  10. PreparedStatement pst= conn.prepareStatement(sql);
  11. query.fillPreparedStatement(pst);//填充pst
  12. ResultSet rs = pst.executeQuery();
  13. while (rs.next()) {
  14. Product product = new Product();
  15. ……
  16. productList.add(product);
  17. }
  18. rs.close();
  19. pst.close();
  20. } finally {
  21. if (conn != null) {
  22. conn.close();
  23. }
  24. }
  25. } catch (Exception e) {
  26. // TODO Auto-generated catch block
  27. e.printStackTrace();
  28. }
  29. return productList;
  30. }