如何使PreparedStatement支持命名参数

/***********本人原创,欢迎转载,转载请保留本人信息*************/
/***********文章发表请与本人联系,作者保留所有权利*************/
作者:wallimn
电邮: [email protected]
博客: http://blog.csdn.net/wallimn
网络硬盘: http://wallimn.ys168.com
时间:2009-01-06
/***********本人原创,欢迎转载,转载请保留本人信息*************/
/***********文章发表请与本人联系,作者保留所有权利*************/

引言
  所谓命名参数,使用.net的人应该比较熟,很方便,参数多的时候不容易出错,搞不懂java为什么就不支持。如果没有使用过,或者对于命名参数不知所云,请看下面的说明。
    PreparedStatement中参数都是使用下标传值,如:
  1. PreparedStatement p = con.prepareStatement("select * from people where
  2. (first_name = ? or last_name = ?) and address = ?");
  3. p.setString(1, name);
  4. p.setString(2, name);
  5. p.setString(3, address);
  但是如果查询语句复杂,或者修改了sql,要跟踪每个参数的下标将变得非常麻烦并且容易出错,如果使用名称来命名参数就可以解决这个问题,如:
  1. PreparedStatement p = con.prepareStatement("select * from people where
  2. (first_name =:name or last_name =:name) and address =:address");
  3. p.setString("name", name);
  4. p.setString("address", address);
正文
  最近认真学习了一下正则表达式,发现使用正则表达式可以很好的解决这个问题。
  思路如下:
  以命名参数的形式写好SQL语句,然后使用正则表达式提取出所有参数,使用Map记录,然后将参数替换成?(也就是java要求的标准形式),使用标准的SQL语句来创建PreparedStatement,最后根据Map来设置PreparedStatement的参数。
  好了,废话少说。贴一下代码,我个人觉得实现的比较得比较简单、优雅。没有仔细测试,如发现问题,欢迎交流([email protected])。
  1. import java.sql.PreparedStatement;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import java.util.Map.Entry;
  5. import java.util.regex.Matcher;
  6. import java.util.regex.Pattern;
  7. import org.apache.commons.logging.Log;
  8. import org.apache.commons.logging.LogFactory;
  9. /**
  10.  * 用来模拟实现命名参数功能
  11.  * 
  12.  * 编码:wallimn 时间:2009-1-8 下午12:11:34
  13.  * 版本:V1.0
  14.  */
  15. public class NamedParamSqlUtil {
  16.     static final Log log = LogFactory.getLog(NamedParamSqlUtil.class);
  17.     private Map paramsMap=new HashMap();
  18.     
  19.     public Map getParamsMap(){
  20.         return paramsMap;
  21.     }
  22.     public void emptyMap(){
  23.         paramsMap.clear();
  24.     }

  25.     /**
  26.      * 分析处理带命名参数的SQL语句。使用Map存储参数,然后将参数替换成?
  27.      * 
  28.      * 作者:wallimn 时间:2009-1-8 下午12:14:10
  29.      * 邮件:[email protected]
  30.      * 博客:http://blog.csdn.net/wallimn
  31.      * 参数:
  32.      * @param sql
  33.      * @return
  34.      */
  35.     public String parseSql(String sql) {
  36.         String regex = "(:(//w+))";
  37.         Pattern p = Pattern.compile(regex);
  38.         Matcher m = p.matcher(sql);
  39.         emptyMap();
  40.         int idx=1;
  41.         while (m.find()) {
  42.             //参数名称可能有重复,使用序号来做Key
  43.             paramsMap.put(new Integer(idx++), m.group(2));
  44.             //System.out.println(m.group(2));
  45.         }
  46.         String result = sql.replaceAll(regex, "?");
  47.         log.debug("分析前:"+sql);
  48.         log.debug("分析后:"+result);
  49.         return result;
  50.     }
  51.     /**
  52.      * 使用参数值Map,填充pStat
  53.      * 
  54.      * 作者:wallimn 时间:2009-1-8 下午12:15:36
  55.      * 邮件:[email protected]
  56.      * 博客:http://blog.csdn.net/wallimn
  57.      * 参数:
  58.      * @param pStat
  59.      * @param pMap 命名参数的值表,其中的值可以比较所需的参数多。
  60.      * @return
  61.      */
  62.     public boolean fillParameters(PreparedStatement pStat,Map pMap){
  63.         boolean result=true;
  64.         String paramName=null;
  65.         Object paramValue=null;
  66.         int idx=1;
  67.         for(java.util.Iterator> itr = paramsMap.entrySet().iterator(); itr.hasNext();){
  68.             Entry entry = (Entry)itr.next();
  69.             paramName = entry.getValue();idx=pMap.getKey().intValue;
  70.             //不包含会返回null
  71.             paramValue = pMap.get(paramName);          
  72.             try {
  73.                 //paramValue为null,会出错吗?需要测试
  74.                 pStat.setObject(idx, paramValue);
  75.             } catch (Exception e) {
  76.                 log.error("填充参数出错,原因:"+e.getMessage());
  77.                 result=false;
  78.             }
  79.         }
  80.         return result;
  81.     }
  82. }


  使用上面的类的方法如下:
  1.     public boolean dbInserUpDe(Connection conn, String pSql,
  2.             Map pMap) {
  3.         if (pSql == null || "".equals(pSql)) {
  4.             log.error("SQL语句错误!");
  5.             return false;
  6.         }
  7.         boolean result = false;
  8.         NamedParamSqlUtil util = new NamedParamSqlUtil();
  9.         String sql = util.parseSql(pSql);
  10.         try {
  11.             PreparedStatement stat = conn.prepareStatement(sql);
  12.             util.fillParameters(stat, pMap);
  13.             result = stat.execute();
  14.         } catch (Exception e) {
  15.             log.error(e.getMessage());
  16.             return false;
  17.         }
  18.         return result;
  19.     }


后记
  使用Map来传递参数是个很好的办法,比较便于封装,而且使用过Struts2.0的人都知道,Struts2.0自动的把ServletRequest中的参数提取到了Map中,使用以上方法十分的方便。如果不使用Struts2.0,也可以使用一个循环很方便的把ServletRequest中的参数提取的Map中。
  另外,如果参数中有要求Date等类型时,要自行进行转化。字符串不用转换;数字类型在使用Oracle的情况下也不用转换(以字符串的形式传递数字类型,Oracle可以自动进行转换)。其它数据库不太了解。
/***********本人原创,欢迎转载,转载请保留本人信息*************/
/***********文章发表请与本人联系,作者保留所有权利*************/
作者:wallimn
电邮: [email protected]
博客: http://blog.csdn.net/wallimn
网络硬盘: http://wallimn.ys168.com
时间:2009-01-06
/***********本人原创,欢迎转载,转载请保留本人信息*************/
/***********文章发表请与本人联系,作者保留所有权利*************/

你可能感兴趣的:(Java相关)