001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.mybatisflex.core.util; 017 018import java.lang.reflect.Array; 019import java.time.LocalDateTime; 020import java.time.format.DateTimeFormatter; 021import java.util.Date; 022import java.util.StringJoiner; 023import java.util.regex.Matcher; 024 025import static com.mybatisflex.core.constant.SqlConsts.*; 026 027public class SqlUtil { 028 029 private SqlUtil() { 030 } 031 032 public static void keepColumnSafely(String column) { 033 if (StringUtil.isBlank(column)) { 034 throw new IllegalArgumentException("Column must not be empty"); 035 } else { 036 column = column.trim(); 037 } 038 039 int strLen = column.length(); 040 for (int i = 0; i < strLen; ++i) { 041 char ch = column.charAt(i); 042 if (Character.isWhitespace(ch)) { 043 throw new IllegalArgumentException("Column must not has space char."); 044 } 045 if (isUnSafeChar(ch)) { 046 throw new IllegalArgumentException("Column has unsafe char: [" + ch + "]."); 047 } 048 } 049 } 050 051 052 /** 053 * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) 054 */ 055 private static final String SQL_ORDER_BY_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; 056 057 public static void keepOrderBySqlSafely(String value) { 058 if (!value.matches(SQL_ORDER_BY_PATTERN)) { 059 throw new IllegalArgumentException("Order By sql not safe, order by string: " + value); 060 } 061 } 062 063 064 private static final char[] UN_SAFE_CHARS = "'`\"<>&+=#-;".toCharArray(); 065 066 private static boolean isUnSafeChar(char ch) { 067 for (char c : UN_SAFE_CHARS) { 068 if (c == ch) { 069 return true; 070 } 071 } 072 return false; 073 } 074 075 076 /** 077 * 根据数据库响应结果判断数据库操作是否成功。 078 * 079 * @param result 数据库操作返回影响条数 080 * @return {@code true} 操作成功,{@code false} 操作失败。 081 */ 082 public static boolean toBool(int result) { 083 return result > 0 || result == -2; 084 } 085 086 public static boolean toBool(long result) { 087 return result > 0; 088 } 089 090 091 /** 092 * 根据数据库响应结果判断数据库操作是否成功。 093 * 有 1 条数据成功便算成功 094 * 095 * @param results 操作数据的响应成功条数 096 * @return {@code true} 操作成功,{@code false} 操作失败。 097 */ 098 public static boolean toBool(int[] results) { 099 for (int result : results) { 100 if (toBool(result)) { 101 return true; 102 } 103 } 104 return false; 105 } 106 107 108 /** 109 * 替换 sql 中的问号 ? 110 * 111 * @param sql sql 内容 112 * @param params 参数 113 * @return 完整的 sql 114 */ 115 public static String replaceSqlParams(String sql, Object[] params) { 116 if (params == null || params.length == 0) { 117 return sql; 118 } 119 120 StringBuilder sqlBuilder = new StringBuilder(); 121 char quote = 0; 122 int index = 0; 123 for (int i = 0; i < sql.length(); ++i) { 124 char ch = sql.charAt(i); 125 if (ch == '\'') { 126 if (quote == 0) { 127 quote = ch; 128 } else if (quote == '\'') { 129 quote = 0; 130 } 131 } else if (ch == '"') { 132 if (quote == 0) { 133 quote = ch; 134 } else if (quote == '"') { 135 quote = 0; 136 } 137 } 138 if (quote == 0 && ch == '?' && index < params.length) { 139 sqlBuilder.append(getParamString(params, index++)); 140 } else { 141 sqlBuilder.append(ch); 142 } 143 } 144 145 return sqlBuilder.toString(); 146 } 147 148 149 private static String getParamString(Object[] params, int index) { 150 Object value = params[index]; 151 if (value == null) { 152 return "null"; 153 } 154 // number or bool 155 else if (value instanceof Number || value instanceof Boolean) { 156 return value.toString(); 157 } 158 // array 159 else if (ClassUtil.isArray(value.getClass())) { 160 StringJoiner joiner = new StringJoiner(",", "[", "]"); 161 for (int i = 0; i < Array.getLength(value); i++) { 162 joiner.add(String.valueOf(Array.get(value, i))); 163 } 164 return joiner.toString(); 165 } 166 // other 167 else { 168 StringBuilder sb = new StringBuilder(); 169 sb.append("'"); 170 if (value instanceof Date) { 171 sb.append(DateUtil.toDateTimeString((Date) value)); 172 } else if (value instanceof LocalDateTime) { 173 sb.append(((LocalDateTime) value).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 174 } else { 175 sb.append(value); 176 } 177 sb.append("'"); 178 return Matcher.quoteReplacement(sb.toString()); 179 } 180 } 181 182 183 public static String buildSqlParamPlaceholder(int count) { 184 StringBuilder sb = new StringBuilder(BRACKET_LEFT); 185 for (int i = 0; i < count; i++) { 186 sb.append(PLACEHOLDER); 187 if (i != count - 1) { 188 sb.append(DELIMITER); 189 } 190 } 191 sb.append(BRACKET_RIGHT); 192 return sb.toString(); 193 } 194 195}