如何使PreparedStatement支持命名参数
1个回答
推荐于2017-11-27 · 知道合伙人数码行家
huanglenzhi
知道合伙人数码行家
向TA提问 私信TA
知道合伙人数码行家
采纳数:117538
获赞数:517183
长期从事计算机组装,维护,网络组建及管理。对计算机硬件、操作系统安装、典型网络设备具有详细认知。
向TA提问 私信TA
关注
展开全部
所谓命名参数,使用.net的人应该比较熟,很方便,参数多的时候不容易出错,搞不懂java为什么就不支持。如果没有使用过,或者对于命名参数不知所云,请看下面的说明。
PreparedStatement中参数都是使用下标传值,如:
PreparedStatement p = con.prepareStatement("select * from people where
(first_name = ? or last_name = ?) and address = ?");
p.setString(1, name);
p.setString(2, name);
p.setString(3, address);
但是如果查询语句复杂,或者修改了sql,要跟踪每个参数的下标将变得非常麻烦并且容易出错,如果使用名称来命名参数就可以解决这个问题,如:
PreparedStatement p = con.prepareStatement("select * from people where
(first_name =:name or last_name =:name) and address =:address");
p.setString("name", name);
p.setString("address", address);
正文
最近认真学习了一下正则表达式,发现使用正则表达式可以很好的解决这个问题。
思路如下:
以命名参数的形式写好SQL语句,然后使用正则表达式提取出所有参数,使用Map记录,然后将参数替换成?(也就是java要求的标准形式),使用标准的SQL语句来创建PreparedStatement,最后根据Map来设置PreparedStatement的参数。
好了,废话少说。贴一下代码,我个人觉得实现的比较得比较简单、优雅。没有仔细测试,如发现问题,欢迎交流(wallimn@sohu.com)。
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 用来模拟实现命名参数功能
* <br/>
* 编码:wallimn 时间:2009-1-8 下午12:11:34<br/>
* 版本:V1.0<br/>
*/
public class NamedParamSqlUtil {
static final Log log = LogFactory.getLog(NamedParamSqlUtil.class);
private Map<Integer,String> paramsMap=new HashMap<Integer,String>();
public Map<Integer,String> getParamsMap(){
return paramsMap;
}
public void emptyMap(){
paramsMap.clear();
}
/**
* 分析处理带命名参数的SQL语句。使用Map存储参数,然后将参数替换成?
* <br/>
* 作者:wallimn 时间:2009-1-8 下午12:14:10<br/>
* 邮件:wallimn@sohu.com<br/>
* 博客:http://blog.csdn.net/wallimn<br/>
* 参数:<br/>
* @param sql
* @return
*/
public String parseSql(String sql) {
String regex = "(:(//w+))";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sql);
emptyMap();
int idx=1;
while (m.find()) {
//参数名称可能有重复,使用序号来做Key
paramsMap.put(new Integer(idx++), m.group(2));
//System.out.println(m.group(2));
}
String result = sql.replaceAll(regex, "?");
log.debug("分析前:"+sql);
log.debug("分析后:"+result);
return result;
}
/**
* 使用参数值Map,填充pStat
* <br/>
* 作者:wallimn 时间:2009-1-8 下午12:15:36<br/>
* 邮件:wallimn@sohu.com<br/>
* 博客:http://blog.csdn.net/wallimn<br/>
* 参数:<br/>
* @param pStat
* @param pMap 命名参数的值表,其中的值可以比较所需的参数多。
* @return
*/
public boolean fillParameters(PreparedStatement pStat,Map<String,Object> pMap){
boolean result=true;
String paramName=null;
Object paramValue=null;
int idx=1;
for(java.util.Iterator<Entry<Integer, String>> itr = paramsMap.entrySet().iterator(); itr.hasNext();){
Entry<Integer, String> entry = (Entry<Integer, String>)itr.next();
paramName = entry.getValue();idx=pMap.getKey().intValue;
//不包含会返回null
paramValue = pMap.get(paramName);
try {
//paramValue为null,会出错吗?需要测试
pStat.setObject(idx, paramValue);
} catch (Exception e) {
log.error("填充参数出错,原因:"+e.getMessage());
result=false;
}
}
return result;
}
}
使用上面的类的方法如下:
public boolean dbInserUpDe(Connection conn, String pSql,
Map<String, Object> pMap) {
if (pSql == null || "".equals(pSql)) {
log.error("SQL语句错误!");
return false;
}
boolean result = false;
NamedParamSqlUtil util = new NamedParamSqlUtil();
String sql = util.parseSql(pSql);
try {
PreparedStatement stat = conn.prepareStatement(sql);
util.fillParameters(stat, pMap);
result = stat.execute();
} catch (Exception e) {
log.error(e.getMessage());
return false;
}
return result;
}
后记
使用Map来传递参数是个很好的办法,比较便于封装,而且使用过Struts2.0的人都知道,Struts2.0自动的把ServletRequest中的参数提取到了Map中,使用以上方法十分的方便。如果不使用Struts2.0,也可以使用一个循环很方便的把ServletRequest中的参数提取的Map中。
另外,如果参数中有要求Date等类型时,要自行进行转化。字符串不用转换;数字类型在使用Oracle的情况下也不用转换(以字符串的形式传递数字类型,Oracle可以自动进行转换)。其它数据库不太了解。
作者:wallimn
PreparedStatement中参数都是使用下标传值,如:
PreparedStatement p = con.prepareStatement("select * from people where
(first_name = ? or last_name = ?) and address = ?");
p.setString(1, name);
p.setString(2, name);
p.setString(3, address);
但是如果查询语句复杂,或者修改了sql,要跟踪每个参数的下标将变得非常麻烦并且容易出错,如果使用名称来命名参数就可以解决这个问题,如:
PreparedStatement p = con.prepareStatement("select * from people where
(first_name =:name or last_name =:name) and address =:address");
p.setString("name", name);
p.setString("address", address);
正文
最近认真学习了一下正则表达式,发现使用正则表达式可以很好的解决这个问题。
思路如下:
以命名参数的形式写好SQL语句,然后使用正则表达式提取出所有参数,使用Map记录,然后将参数替换成?(也就是java要求的标准形式),使用标准的SQL语句来创建PreparedStatement,最后根据Map来设置PreparedStatement的参数。
好了,废话少说。贴一下代码,我个人觉得实现的比较得比较简单、优雅。没有仔细测试,如发现问题,欢迎交流(wallimn@sohu.com)。
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 用来模拟实现命名参数功能
* <br/>
* 编码:wallimn 时间:2009-1-8 下午12:11:34<br/>
* 版本:V1.0<br/>
*/
public class NamedParamSqlUtil {
static final Log log = LogFactory.getLog(NamedParamSqlUtil.class);
private Map<Integer,String> paramsMap=new HashMap<Integer,String>();
public Map<Integer,String> getParamsMap(){
return paramsMap;
}
public void emptyMap(){
paramsMap.clear();
}
/**
* 分析处理带命名参数的SQL语句。使用Map存储参数,然后将参数替换成?
* <br/>
* 作者:wallimn 时间:2009-1-8 下午12:14:10<br/>
* 邮件:wallimn@sohu.com<br/>
* 博客:http://blog.csdn.net/wallimn<br/>
* 参数:<br/>
* @param sql
* @return
*/
public String parseSql(String sql) {
String regex = "(:(//w+))";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sql);
emptyMap();
int idx=1;
while (m.find()) {
//参数名称可能有重复,使用序号来做Key
paramsMap.put(new Integer(idx++), m.group(2));
//System.out.println(m.group(2));
}
String result = sql.replaceAll(regex, "?");
log.debug("分析前:"+sql);
log.debug("分析后:"+result);
return result;
}
/**
* 使用参数值Map,填充pStat
* <br/>
* 作者:wallimn 时间:2009-1-8 下午12:15:36<br/>
* 邮件:wallimn@sohu.com<br/>
* 博客:http://blog.csdn.net/wallimn<br/>
* 参数:<br/>
* @param pStat
* @param pMap 命名参数的值表,其中的值可以比较所需的参数多。
* @return
*/
public boolean fillParameters(PreparedStatement pStat,Map<String,Object> pMap){
boolean result=true;
String paramName=null;
Object paramValue=null;
int idx=1;
for(java.util.Iterator<Entry<Integer, String>> itr = paramsMap.entrySet().iterator(); itr.hasNext();){
Entry<Integer, String> entry = (Entry<Integer, String>)itr.next();
paramName = entry.getValue();idx=pMap.getKey().intValue;
//不包含会返回null
paramValue = pMap.get(paramName);
try {
//paramValue为null,会出错吗?需要测试
pStat.setObject(idx, paramValue);
} catch (Exception e) {
log.error("填充参数出错,原因:"+e.getMessage());
result=false;
}
}
return result;
}
}
使用上面的类的方法如下:
public boolean dbInserUpDe(Connection conn, String pSql,
Map<String, Object> pMap) {
if (pSql == null || "".equals(pSql)) {
log.error("SQL语句错误!");
return false;
}
boolean result = false;
NamedParamSqlUtil util = new NamedParamSqlUtil();
String sql = util.parseSql(pSql);
try {
PreparedStatement stat = conn.prepareStatement(sql);
util.fillParameters(stat, pMap);
result = stat.execute();
} catch (Exception e) {
log.error(e.getMessage());
return false;
}
return result;
}
后记
使用Map来传递参数是个很好的办法,比较便于封装,而且使用过Struts2.0的人都知道,Struts2.0自动的把ServletRequest中的参数提取到了Map中,使用以上方法十分的方便。如果不使用Struts2.0,也可以使用一个循环很方便的把ServletRequest中的参数提取的Map中。
另外,如果参数中有要求Date等类型时,要自行进行转化。字符串不用转换;数字类型在使用Oracle的情况下也不用转换(以字符串的形式传递数字类型,Oracle可以自动进行转换)。其它数据库不太了解。
作者:wallimn
本回答被提问者和网友采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询