zoukankan      html  css  js  c++  java
  • 在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语句中找到以@开始,以" ", " ", " ", " ", ",", ")", ">", "<", "!", "'", "-", "+", "/"为结束的符号,则会认为是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("
    ", " ").replace("
    ", " ").replace("	", " ").replace("
    ", " ");
    		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 = {" ", "	", "
    ", "
    ", ",", ")", ">", "<", "!", "'", "-", "+", "/"};		
    	
    	/**
    	 * 查找下一个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
    ,@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("
    ", " ").replace("
    ", " ").replace("	", " "), target);
    		
    		sql = "Select @a @b,@c>,@d<,@e!,@f),'@g',@h
    ,@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("
    ", " ").replace("
    ", " ").replace("	", " "), 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
    ,@i-,@j+,@k/, @l";
    		SQLParams params = SQLParamsUtil.parse(sql);
    		
    		Assert.assertEquals("Select ? ?,?>,?<,?!,?),'?',?
    ,?-,?+,?/, ?", params.getSql());

                            Assert.assertEquals(params.getParams().get(3), "c");

    	}
    

      

  • 相关阅读:
    21.Merge Two Sorted Lists 、23. Merge k Sorted Lists
    34. Find First and Last Position of Element in Sorted Array
    leetcode 20. Valid Parentheses 、32. Longest Valid Parentheses 、301. Remove Invalid Parentheses
    31. Next Permutation
    17. Letter Combinations of a Phone Number
    android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事项
    oc 异常处理
    oc 类型判断
    oc Delegate
    oc 协议
  • 原文地址:https://www.cnblogs.com/Rong-/p/5574128.html
Copyright © 2011-2022 走看看