在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");

	}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM