/* * Copyright 2002-2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.hasor.data.jdbc.core; import net.hasor.core.Hasor; import net.hasor.data.jdbc.SqlParameterSource; import java.sql.SQLException; import java.util.*; /** * 编译的SQL * @version : 2013-10-16 * @author 赵永春(zyc@hasor.net) */ public class ParsedSql { private String originalSql; private int namedParameterCount; private int unnamedParameterCount; private int totalParameterCount; private List<String> parameterNames; private List<int[]> parameterIndexes; // private ParsedSql(final String originalSql) { this.originalSql = originalSql; } public String getOriginalSql() { return this.originalSql; } public int getNamedParameterCount() { return this.namedParameterCount; } public int getUnnamedParameterCount() { return this.unnamedParameterCount; } public int getTotalParameterCount() { return this.totalParameterCount; } public List<String> getParameterNames() { return this.parameterNames; } public List<int[]> getParameterIndexes() { return this.parameterIndexes; } // // // // /**Set of characters that qualify as parameter separators, indicating that a parameter name in a SQL String has ended. */ private static final char[] PARAMETER_SEPARATORS = new char[] { '"', '\'', ':', '&', ',', ';', '(', ')', '|', '=', '+', '-', '*', '%', '/', '\\', '<', '>', '^' }; /** Set of characters that qualify as comment or quotes starting characters.*/ private static final String[] START_SKIP = new String[] { "'", "\"", "--", "/*" }; /**Set of characters that at are the corresponding comment or quotes ending characters. */ private static final String[] STOP_SKIP = new String[] { "'", "\"", "\n", "*/" }; //------------------------------------------------------------------------- // Core methods used by NamedParameterJdbcTemplate and SqlQuery/SqlUpdate //------------------------------------------------------------------------- public static ParsedSql getParsedSql(final String originalSql) { ParsedSql parSQL = new ParsedSql(originalSql); // //1.关键参数定义 List<String> parameterNames = new ArrayList<String>(); List<int[]> parameterIndexes = new ArrayList<int[]>(); int namedParameterCount = 0;//带有名字参数的总数 int unnamedParameterCount = 0;//无名字参数总数 int totalParameterCount = 0;//参数总数 // //2.分析SQL,提取出SQL中参数信息 Hasor.assertIsNotNull(originalSql, "SQL must not be null"); Set<String> namedParameters = new HashSet<String>(); char[] statement = originalSql.toCharArray(); int i = 0; while (i < statement.length) { int skipToPosition = ParsedSql.skipCommentsAndQuotes(statement, i);//从当前为止掠过的长度 if (i != skipToPosition) { if (skipToPosition >= statement.length) { break; } i = skipToPosition; } char c = statement[i]; if (c == ':' || c == '&') { int j = i + 1; if (j < statement.length && statement[j] == ':' && c == ':') { i = i + 2;// Postgres-style "::" casting operator - to be skipped. continue; } while (j < statement.length && !ParsedSql.isParameterSeparator(statement[j])) { j++; } if (j - i > 1) { String parameter = originalSql.substring(i + 1, j); if (!namedParameters.contains(parameter)) { namedParameters.add(parameter); namedParameterCount++; } parameterNames.add(parameter); parameterIndexes.add(new int[] { i, j });//startIndex, endIndex totalParameterCount++; } i = j - 1; } else if (c == '?') { unnamedParameterCount++; totalParameterCount++; } i++; } parSQL.namedParameterCount = namedParameterCount;/*带有名字参数的总数*/ parSQL.unnamedParameterCount = unnamedParameterCount;/*匿名参数的总数*/ parSQL.totalParameterCount = totalParameterCount;/*总共参数个数*/ parSQL.parameterIndexes = parameterIndexes; parSQL.parameterNames = parameterNames; return parSQL; } /** Skip over comments and quoted names present in an SQL statement */ private static int skipCommentsAndQuotes(final char[] statement, final int position) { for (int i = 0; i < ParsedSql.START_SKIP.length; i++) { if (statement[position] == ParsedSql.START_SKIP[i].charAt(0)) { boolean match = true; for (int j = 1; j < ParsedSql.START_SKIP[i].length(); j++) { if (!(statement[position + j] == ParsedSql.START_SKIP[i].charAt(j))) { match = false; break; } } if (match) { int offset = ParsedSql.START_SKIP[i].length(); for (int m = position + offset; m < statement.length; m++) { if (statement[m] == ParsedSql.STOP_SKIP[i].charAt(0)) { boolean endMatch = true; int endPos = m; for (int n = 1; n < ParsedSql.STOP_SKIP[i].length(); n++) { if (m + n >= statement.length) return statement.length;// last comment not closed properly if (!(statement[m + n] == ParsedSql.STOP_SKIP[i].charAt(n))) { endMatch = false; break; } endPos = m + n; } if (endMatch) { return endPos + 1;// found character sequence ending comment or quote } } } // character sequence ending comment or quote not found return statement.length; } } } return position; } /** Determine whether a parameter name ends at the current position, that is, whether the given character qualifies as a separator. */ private static boolean isParameterSeparator(final char c) { if (Character.isWhitespace(c)) { return true; } for (char separator : ParsedSql.PARAMETER_SEPARATORS) { if (c == separator) { return true; } } return false; } //------------------------------------------------------------------------- // Core methods used by NamedParameterJdbcTemplate and SqlQuery/SqlUpdate //------------------------------------------------------------------------- /**生成SQL*/ public static String buildSql(final ParsedSql parsedSql, final SqlParameterSource paramSource) { String originalSql = parsedSql.getOriginalSql(); List<String> parameterNames = parsedSql.getParameterNames(); List<int[]> parameterIndexes = parsedSql.getParameterIndexes(); // StringBuilder sqlToUse = new StringBuilder(); int lastIndex = 0; for (int i = 0; i < parameterNames.size(); i++) { String paramName = parameterNames.get(i); int[] indexes = parameterIndexes.get(i); int startIndex = indexes[0]; int endIndex = indexes[1]; sqlToUse.append(originalSql.substring(lastIndex, startIndex)); if (paramSource != null && paramSource.hasValue(paramName)) { Object value = paramSource.getValue(paramName); if (value instanceof Collection) { Iterator<?> entryIter = ((Collection<?>) value).iterator(); int k = 0; while (entryIter.hasNext()) { if (k > 0) { sqlToUse.append(", "); } k++; Object entryItem = entryIter.next(); if (entryItem instanceof Object[]) { Object[] expressionList = (Object[]) entryItem; sqlToUse.append("("); for (int m = 0; m < expressionList.length; m++) { if (m > 0) { sqlToUse.append(", "); } sqlToUse.append("?"); } sqlToUse.append(")"); } else { sqlToUse.append("?"); } } } else { sqlToUse.append("?"); } } else { sqlToUse.append("?"); } lastIndex = endIndex; } sqlToUse.append(originalSql.substring(lastIndex, originalSql.length())); return sqlToUse.toString(); } /**生成Values*/ public static Object[] buildSqlValues(final ParsedSql parsedSql, final SqlParameterSource paramSource) throws SQLException { String originalSql = parsedSql.getOriginalSql(); List<String> parameterNames = parsedSql.getParameterNames(); int namedParameterCount = parsedSql.getNamedParameterCount();//带有名字参数的总数 int unnamedParameterCount = parsedSql.getUnnamedParameterCount();//无名字参数总数 int totalParameterCount = parsedSql.getTotalParameterCount();//参数总数 // Object[] paramArray = new Object[totalParameterCount]; if (namedParameterCount > 0 && unnamedParameterCount > 0) { throw new SQLException("You can't mix named and traditional ? placeholders. You have " + namedParameterCount + " named parameter(s) and " + unnamedParameterCount + " traditonal placeholder(s) in [" + originalSql + "]"); } for (int i = 0; i < parameterNames.size(); i++) { String paramName = parameterNames.get(i); paramArray[i] = paramSource.getValue(paramName); } return paramArray; } }