001/** 002 * Copyright (c) 2022-2023, 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.query; 017 018 019import com.mybatisflex.core.dialect.IDialect; 020import com.mybatisflex.core.util.ClassUtil; 021 022import java.io.Serializable; 023import java.lang.reflect.Array; 024import java.util.List; 025import java.util.function.Predicate; 026import java.util.function.Supplier; 027 028public class QueryCondition implements Serializable { 029 030 public static final String LOGIC_LIKE = "LIKE"; 031 public static final String LOGIC_GT = ">"; 032 public static final String LOGIC_GE = ">="; 033 public static final String LOGIC_LT = "<"; 034 public static final String LOGIC_LE = "<="; 035 public static final String LOGIC_EQUALS = "="; 036 public static final String LOGIC_NOT_EQUALS = "!="; 037 038 public static final String LOGIC_IS_NULL = "IS NULL"; 039 public static final String LOGIC_IS_NOT_NULL = "IS NOT NULL"; 040 041 public static final String LOGIC_IN = "IN"; 042 public static final String LOGIC_NOT_IN = "NOT IN"; 043 public static final String LOGIC_BETWEEN = "BETWEEN"; 044 public static final String LOGIC_NOT_BETWEEN = "NOT BETWEEN"; 045 046 047 protected QueryColumn column; 048 protected String logic; 049 protected Object value; 050 protected boolean effective = true; 051 052 //当前条件的上个条件 053 protected QueryCondition before; 054 //当前条件的上个下一个 055 protected QueryCondition next; 056 //两个条件直接的连接符 057 protected SqlConnector connector; 058 059 060 public static QueryCondition createEmpty() { 061 return new QueryCondition().when(false); 062 } 063 064 065 public static QueryCondition create(String schema, String table, String column, String logic, Object value) { 066 QueryCondition condition = new QueryCondition(); 067 condition.setColumn(new QueryColumn(schema, table, column)); 068 condition.setLogic(logic); 069 condition.setValue(value); 070 return condition; 071 } 072 073 public static QueryCondition create(QueryColumn queryColumn, Object value) { 074 return create(queryColumn, LOGIC_EQUALS, value); 075 } 076 077 public static QueryCondition create(QueryColumn queryColumn, String logic, Object value) { 078 QueryCondition condition = new QueryCondition(); 079 condition.setColumn(queryColumn); 080 condition.setLogic(logic); 081 condition.setValue(value); 082 return condition; 083 } 084 085 public QueryCondition() { 086 } 087 088 public QueryColumn getColumn() { 089 return column; 090 } 091 092 public void setColumn(QueryColumn column) { 093 this.column = column; 094 } 095 096 public Object getValue() { 097 return checkEffective() ? value : null; 098 } 099 100 public void setValue(Object value) { 101 this.value = value; 102 } 103 104 public String getLogic() { 105 return logic; 106 } 107 108 public void setLogic(String logic) { 109 this.logic = logic; 110 } 111 112 113 public QueryCondition when(boolean effective) { 114 this.effective = effective; 115 return this; 116 } 117 118 public void when(Supplier<Boolean> fn) { 119 Boolean effective = fn.get(); 120 this.effective = (effective != null && effective); 121 } 122 123 public <T> QueryCondition when(Predicate<T> fn) { 124 Object val = this.value; 125 if (LOGIC_LIKE.equals(logic) && val instanceof String) { 126 String valStr = (String) val; 127 if (valStr.startsWith("%")) { 128 valStr = valStr.substring(1); 129 } 130 if (valStr.endsWith("%")) { 131 valStr = valStr.substring(0, valStr.length() - 1); 132 } 133 val = valStr; 134 } 135 this.effective = fn.test((T) val); 136 return this; 137 } 138 139 public boolean checkEffective() { 140 return effective; 141 } 142 143 144 public QueryCondition and(String sql) { 145 return and(new RawFragment(sql)); 146 } 147 148 public QueryCondition and(String sql, Object... params) { 149 return and(new RawFragment(sql, params)); 150 } 151 152 public QueryCondition and(QueryCondition nextCondition) { 153 return new Brackets(this).and(nextCondition); 154 } 155 156 public QueryCondition or(String sql) { 157 return or(new RawFragment(sql)); 158 } 159 160 public QueryCondition or(String sql, Object... params) { 161 return or(new RawFragment(sql, params)); 162 } 163 164 public QueryCondition or(QueryCondition nextCondition) { 165 return new Brackets(this).or(nextCondition); 166 } 167 168 protected void connect(QueryCondition nextCondition, SqlConnector connector) { 169 if (this.next != null) { 170 this.next.connect(nextCondition, connector); 171 } else { 172 this.next = nextCondition; 173 this.connector = connector; 174 nextCondition.before = this; 175 } 176 } 177 178 public String toSql(List<QueryTable> queryTables, IDialect dialect) { 179 StringBuilder sql = new StringBuilder(); 180 //检测是否生效 181 if (checkEffective()) { 182 QueryCondition effectiveBefore = getEffectiveBefore(); 183 if (effectiveBefore != null) { 184 sql.append(effectiveBefore.connector); 185 } 186 //列 187 sql.append(getColumn().toConditionSql(queryTables, dialect)); 188 //逻辑符号 189 sql.append(" ").append(logic).append(" "); 190 191 //值(或者问号) 192 if (value instanceof QueryColumn) { 193 sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect)); 194 } 195 //子查询 196 else if (value instanceof QueryWrapper) { 197 sql.append("(").append(dialect.buildSelectSql((QueryWrapper) value)).append(")"); 198 } 199 //原生sql 200 else if (value instanceof RawFragment) { 201 sql.append(((RawFragment) value).getContent()); 202 } 203 //正常查询,构建问号 204 else { 205 appendQuestionMark(sql); 206 } 207 } 208 209 if (this.next != null) { 210 return sql + next.toSql(queryTables, dialect); 211 } 212 213 return sql.toString(); 214 } 215 216 217 protected QueryCondition getEffectiveBefore() { 218 if (before != null && before.checkEffective()) { 219 return before; 220 } else if (before != null) { 221 return before.getEffectiveBefore(); 222 } else { 223 return null; 224 } 225 } 226 227 228 protected void appendQuestionMark(StringBuilder sqlBuilder) { 229 if (LOGIC_IS_NULL.equals(logic) 230 || LOGIC_IS_NOT_NULL.equals(logic) 231 || value instanceof QueryColumn 232 || value instanceof QueryWrapper 233 || value instanceof RawFragment) { 234 //do nothing 235 } 236 237 //between, not between 238 else if (LOGIC_BETWEEN.equals(logic) || LOGIC_NOT_BETWEEN.equals(logic)) { 239 sqlBuilder.append(" ? AND ? "); 240 } 241 //in, not in 242 else if (LOGIC_IN.equals(logic) || LOGIC_NOT_IN.equals(logic)) { 243 int paramsCount = calculateValueArrayCount(); 244 sqlBuilder.append('('); 245 for (int i = 0; i < paramsCount; i++) { 246 sqlBuilder.append('?'); 247 if (i != paramsCount - 1) { 248 sqlBuilder.append(','); 249 } 250 } 251 sqlBuilder.append(')'); 252 } else { 253 sqlBuilder.append(" ? "); 254 } 255 } 256 257 258 private int calculateValueArrayCount() { 259 Object[] values = (Object[]) value; 260 int paramsCount = 0; 261 for (Object object : values) { 262 if (object != null && ClassUtil.isArray(object.getClass())) { 263 paramsCount += Array.getLength(object); 264 } else { 265 paramsCount++; 266 } 267 } 268 return paramsCount; 269 } 270 271 272 boolean containsTable(String... tables) { 273 if (column == null || !checkEffective()) { 274 return nextContainsTable(tables); 275 } 276 for (String table : tables) { 277 if (column.table != null && table.equals(column.table.name)) { 278 return true; 279 } 280 } 281 return nextContainsTable(tables); 282 } 283 284 private boolean nextContainsTable(String... tables) { 285 if (next == null) { 286 return false; 287 } 288 return next.containsTable(tables); 289 } 290 291 @Override 292 public String toString() { 293 return "QueryCondition{" + 294 "column=" + column + 295 ", logic='" + logic + '\'' + 296 ", value=" + value + 297 ", effective=" + effective + 298 '}'; 299 } 300}