在JDBC中使用带参数的SQL语句
ADO.Net中,支持带参数的SQL语句,例如:Select * from Tables where column1=@column1,其中@column1为SQL参数,使用起来非常方便,而JDBC中没有找到此功能,感觉有点不便, 于是想自己实现一个.今天正好看见csdn中有一篇http://blog.csdn.net/wallimn/article/details/3734242 文章,有些感触,于是把自己的实现也写出来.
我的思路:
1: 在SQL语句中找到以@开始,以" ", "\t", "\n", "\r", ",", ")", ">", "<", "!", "'", "-", "+", "/"为结束的符号,则会认为是SQL参数.
2: 将SQL语句,按@拆分到一个List中,如果是SQL参数,则在使用的时候,替换为相应的参数值.
分析:
1: 该实现模拟了一个ADO.NET的SQL参数功能(SQLClient下)
2: 坏处是如果SQL语句中原来就包含@的非参数字符串,则会被误认为SQL参数.
实现:
1: 定义SQL语句拆分后的对象,应该包含字符串,以及是否是SQL参数等信息,类如下:
package hij.cache.extension; final class SQLStr { /**
* 是否是SQL参数
*/
private boolean Param; /**
* 对应的文本
*/
private String text; /**
* 对应的值,一般为Text去除@
*/
private String value; public String getValue() {
return value;
} public boolean isParam() {
return Param;
} public String getText() {
return text;
} public void setText(String text) {
this.text = text;
if(text== null) {
return;
}
if (text.indexOf("@") >= 0) {
Param = true;
} else {
Param = false;
} this.text = this.text.replace("\r\n", " ").replace("\r", " ").replace("\t", " ").replace("\n", " ");
if (Param) {
value = this.text.substring(1);
}
}
}
2: 解析SQL语句,按照@拆分SQL语句,并存储到List<SQLStr>中.
package hij.cache.extension; import java.util.ArrayList;
import java.util.List; import hij.util.generic.IFuncP1; /**
* 解析带参数的SQL语句
* @author XuminRong
*
*/
final class ParseSQL { /**
* 根据@解析字符串,并存储到List中
* @param sql
* @return
*/
public static List<SQLStr> parase(String sql) {
List<SQLStr> lst = new ArrayList<SQLStr>();
if (sql == null) {
return lst;
}
int begin = 0;
int end = sql.indexOf('@');
while (end >= 0) {
String text = sql.substring(begin, end);
SQLStr param1 = new SQLStr();
param1.setText(text);
lst.add(param1);
begin = end;
end = getParamEnd(sql, end);
if (end != -1) {
text = sql.substring(begin, end);
SQLStr param2 = new SQLStr();
param2.setText(text);
lst.add(param2);
} else {
break;
} begin = end;
end = sql.indexOf('@', begin);
} if (begin < sql.length()) {
String text = sql.substring(begin, sql.length());
SQLStr param = new SQLStr();
param.setText(text);
lst.add(param);
}
return lst;
} /**
* SQL语句中,SQL参数的结束符
*/
static String[] arr = {" ", "\t", "\n", "\r", ",", ")", ">", "<", "!", "'", "-", "+", "/"}; /**
* 查找下一个SQL参数的位置
* @param sql
* @param begin
* @return
*/
private static int getParamEnd(String sql, int begin) {
int index = -1;
for (int i = 0; i < arr.length; i++) {
int pos = sql.indexOf(arr[i], begin);
if (index == -1 && pos != -1) {
index = pos;
continue;
}
if (pos != -1 && pos < index) {
index = pos;
}
} return index;
} /**
* 根据回调函数创建对象
* @param lst
* @param callback
* @return
*/
public static String createSQL(List<SQLStr> lst, IFuncP1<String, String> callback) {
if (lst == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lst.size(); i++) {
SQLStr info = lst.get(i);
if (!info.isParam()) {
sb.append(info.getText());
continue;
}
if (callback == null) {
return "";
}
String ret = callback.handle(info.getValue());
sb.append(ret == null? "": ret);
}
return sb.toString();
}
}
测试代码:
下面是测试代码:
package hij.cache.extension; import java.util.List; import org.junit.Assert;
import org.junit.Test; import hij.util.generic.IFuncP1; public class TestCacheProxy { @Test
public void test_Parse_SQL() {
String sql = "Select @a @b,@c>,@d<,@e!,@f),'@g',@h\r\n,@i-,@j+,@k/, @l";
List<SQLStr> lst = ParseSQL.parase(sql); String target = "";
for (int i = 0; i < lst.size(); i++) {
target += lst.get(i).getText();
} Assert.assertEquals(sql.replace("\r\n", " ").replace("\r", " ").replace("\t", " "), target); sql = "Select @a @b,@c>,@d<,@e!,@f),'@g',@h\r\n,@i-,@j+,@k/";
lst = ParseSQL.parase(sql); target = "";
for (int i = 0; i < lst.size(); i++) {
target += lst.get(i).getText();
} Assert.assertEquals(sql.replace("\r\n", " ").replace("\r", " ").replace("\t", " "), target);
String sql2 = ParseSQL.createSQL(lst, new IFuncP1<String, String>(){ @Override
public String handle(String v) {
switch (v) {
case "a":
{
return "a";
}
case "b":
{
return "b";
}
case "c":
{
return "c";
}
case "d":
{
return "d";
}
case "e":
{
return "e";
}
case "f":
{
return "f";
}
case "g":
{
return "g";
}
case "h":
{
return "h";
}
case "i":
{
return "i";
}
case "j":
{
return null;
}
case "k":
{
return "k";
}
default:
{
return null;
}
}
} });
Assert.assertEquals(sql2, "Select a b,c>,d<,e!,f),'g',h ,i-,+,k/");
}
@Test
public void test_Parse_SQL_2() {
String sql = "Selecta, b, c, d";
List<SQLStr> lst = ParseSQL.parase(sql);
Assert.assertEquals(lst.size(), 1);
}
}
备注:
1: IFuncP1:
这是一个接口,是我仿照.NET的委托IFunc定义的一个接口,主要是提供一个有返回值且有一个参数的接口,代码如下:
package hij.util.generic; /**
* 单参有返回值接口
* @author XuminRong
*
* @param <P>
* @param <T>
*
*/
public interface IFuncP1<P, T> {
public T handle(P p);
}
备注2:
1: 看了http://blog.csdn.net/wallimn/article/details/3734242后,发现这个博客的思路比我的好,以后可以参考修改,使用PreparedStatement的内在机制,效率和复杂度应该比自己实现要好.
2: 我当前的实现有问题,我希望能实现:
1) 使用SQL参数
2) 同时可以使用String的format功能,这一点似乎不容易做到.
看了http://blog.csdn.net/wallimn/article/details/3734242后,对其进行重构及测试,下面是相关代码:
1: 抽象出一个SQL对象:SQLParams,包含SQL语句和参数Map
package hij.cache.extension; import java.util.HashMap;
import java.util.Map; public final class SQLParams {
String sql;
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public Map<Integer, String> getParams() {
return params;
}
public void setParams(Map<Integer, String> params) {
this.params = params;
}
Map<Integer, String> params = new HashMap<Integer, String>();
}
2: 添加SQL参数辅助类:这是对NamedParamSqlUtil的重构.(以一个有单参返回值的接口代替fillParameters的pMap,以@代替:)
package hij.cache.extension; 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 hij.util.generic.IFuncP1; /**
* SQL参数处理辅助类
* 参考自:http://blog.csdn.net/wallimn/article/details/3734242
* @author XuminRong
*
*/
public final class SQLParamsUtil { /**
* 分析处理带命名参数的SQL语句。使用Map存储参数,然后将参数替换成?
* @param sql
* @return
*/
public static SQLParams parse(String sql) {
SQLParams param = new SQLParams();
String regex = "(@(\\w+))";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sql);
int idx=1;
while (m.find()) {
//参数名称可能有重复,使用序号来做Key
param.getParams().put(new Integer(idx++), m.group(2));
//System.out.println(m.group(2));
}
String result = sql.replaceAll(regex, "?");
param.setSql(result);
return param;
}
/**
* 使用参数值Map,填充pStat
* @param pStat
* @param pMap 命名参数的值表,其中的值可以比较所需的参数多。
* @return
*/
public static boolean fillParameters(PreparedStatement pStat, SQLParams param, IFuncP1<String,Object> func){
if (pStat == null || param == null) {
return false;
}
if (param.getParams().size() > 0 && func == null) {
return false;
}
for (Integer key : param.getParams().keySet()) {
String paramName = param.getParams().get(key);
Object val = func.handle(paramName);
try
{
pStat.setObject(key, val);
}
catch(Exception ex)
{
ex.printStackTrace();
return false;
}
}
return true;
}
}
3: 测试程序
@Test
public void test_SQLParams_parse() {
String sql = "Select @a @b,@c>,@d<,@e!,@f),'@g',@h\r\n,@i-,@j+,@k/, @l";
SQLParams params = SQLParamsUtil.parse(sql); Assert.assertEquals("Select ? ?,?>,?<,?!,?),'?',?\r\n,?-,?+,?/, ?", params.getSql());
Assert.assertEquals(params.getParams().get(3), "c");
}
在JDBC中使用带参数的SQL语句的更多相关文章
- 通过带参数的Sql语句来实现模糊查询(多条件查询)
#region 通过带参数的Sql语句来实现模糊查询(多条件查询) StringBuilder sb = new StringBuilder("select * from books&quo ...
- laravel 中的 toSql 获取带参数的 sql 语句
默认情况下,toSql 获取到的 sql 里面的参数使用 "?" 代替的,如下: DB::table('user')->where('id', 1)->toSql(); ...
- .Net ADO拼接带参数的SQL语句
首先是在DAL数据访问层中的代码://数据更新的方法public static int shuxing_update(s_passnature model) { string sql = " ...
- SqlParameter类——带参数的SQL语句
http://blog.csdn.net/woshixuye/article/details/7218770 SqlParameter 类 表示 SqlCommand 的参数,也可以是它到 DataS ...
- ADO.NET中带参数的Sql语句的陷阱
1.使用Parameter //利用构造函数方式 ,不推荐这样写 Parameter p =new Parameter("@id",值); cmd.Parameters.Add(p ...
- 使用带参数的SQL语句向数据库中插入空值
private void button1_Click(object sender, EventArgs e) { string name = textBox1.Text; int age = Conv ...
- ado.net调用带参数的sql语句
- JAVA执行带参数的SQL语句
转自 http://www.cnblogs.com/raymond19840709/archive/2008/05/12/1192948.html
- 执行带参数的sql字符串
--要传入的参数 declare @Rv NVARCHAR(40) --要执行的带参数的sql字符串 declare @sql nvarchar(max) set @sql='select * fr ...
- C#中 如何执行带GO的sql 语句
C#中是不允许执行带GO的sql 语句的, 如何做呢? 思路就是将带GO的sql语句转化为分段执行, 但在同一事务内执行. 扩展方法是个很不错的主意, 但是尽量不要影响原来的cmd的一些东东, 如 c ...
随机推荐
- Python之subprocess 执行报错
问题 我用subprocess 执行命令查 主机Ip 报错 command = """ "grep {0} /etc/hosts | awk '{print ...
- 9.Kubernetes核心技术-Controller
Kubernetes核心技术-Controller 内容 什么是Controller Pod和Controller的关系 Deployment控制器应用场景 yaml文件字段说明 Deployment ...
- Python 潮流周刊#77:Python 依赖管理就像垃圾场火灾?(摘要)
本周刊由 Python猫 出品,精心筛选国内外的 250+ 信息源,为你挑选最值得分享的文章.教程.开源项目.软件工具.播客和视频.热门话题等内容.愿景:帮助所有读者精进 Python 技术,并增长职 ...
- delphi12 Android Edit SDK安装
安装 delphi 12.1 后,编译 FMX Android 程序失败! 查找原因,SDK配置全是叹号! 之前用过SDK Manager.exe,直接打开即可以选择下载,但现在发现没有了,查找资料如 ...
- nginx防盗链接的使用
以 local.hyperf.com为例 nginx配置文件如下 # 至少需要一个 Hyperf 节点,多个配置多行 upstream hyperf { # Hyperf HTTP Server 的 ...
- git rebase -i的时候用的不是 vi 编辑器是 nano编辑器不会用
今天给同事 rebase 代码 发现 git fetch && git rebase -i origin/develop 的时候 出现了 那个 nano 编辑器的界面 不会用,和vim ...
- Goland破解之无限重置(最新)
分享一下 JetBrains 全家桶 IDEA 2021.x 的激活破解教程,相当于永久激活 破解了,亲测有效,下面是详细文档哦~ JetBrains 全家桶 IDEA 2021.x 破解激活教程,相 ...
- Win10多用户同时登陆
1. 说明: (1)Win服务器版默认是支持多用户登陆的,甚至可以在主机上用不同用户自己远程登陆自己,如window server 2016. (2)Win10正常情况下是不允许用户同时远程的,即一个 ...
- Vue 模版解析
1.大括号表达式 (1)在MVVM()中接收并保存配置对象 (2)调用Compile编译函数,将el和vm传入 function MVVM (option) { this.$option = opti ...
- bat隐藏窗口运行
在bat脚本开头添加: if "%1" == "h" goto begin mshta vbscript:createobject("wscript. ...