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.query; 017 018 019import com.mybatisflex.core.constant.SqlConnector; 020import com.mybatisflex.core.constant.SqlConsts; 021import com.mybatisflex.core.constant.SqlOperator; 022import com.mybatisflex.core.dialect.IDialect; 023import com.mybatisflex.core.exception.FlexExceptions; 024import com.mybatisflex.core.util.ClassUtil; 025import com.mybatisflex.core.util.ObjectUtil; 026import com.mybatisflex.core.util.StringUtil; 027 028import java.lang.reflect.Array; 029import java.util.Collection; 030import java.util.List; 031import java.util.function.BooleanSupplier; 032 033public class QueryCondition implements CloneSupport<QueryCondition> { 034 035 036 protected QueryColumn column; 037 protected String logic; 038 protected Object value; 039 protected boolean effective = true; 040 041 //当前条件的上一个条件 042 protected QueryCondition prev; 043 044 //当前条件的下一个条件 045 protected QueryCondition next; 046 047 //两个条件直接的连接符 048 protected SqlConnector connector; 049 050 /** 051 * 是否为空条件,默认false 052 */ 053 private boolean empty = false; 054 055 protected boolean notEmpty() { 056 return !empty; 057 } 058 059 protected QueryCondition setEmpty(boolean empty) { 060 this.empty = empty; 061 return this; 062 } 063 064 public static QueryCondition createEmpty() { 065 return new QueryCondition().when(false).setEmpty(true); 066 } 067 068 069 public static QueryCondition create(String schema, String table, String column, String logic, Object value) { 070 return create(new QueryColumn(schema, table, column), logic, value); 071 } 072 073 public static QueryCondition create(QueryColumn queryColumn, Object value) { 074 return create(queryColumn, SqlConsts.EQUALS, value); 075 } 076 077 public static QueryCondition create(QueryColumn queryColumn, SqlOperator logic, Collection<?> values) { 078 return create(queryColumn, logic, values == null ? null : values.toArray()); 079 } 080 081 public static QueryCondition create(QueryColumn queryColumn, SqlOperator logic, Object value) { 082 return create(queryColumn, logic.getValue(), value); 083 } 084 085 public static QueryCondition create(QueryColumn queryColumn, String logic, Collection<?> values) { 086 return create(queryColumn, logic, values == null ? null : values.toArray()); 087 } 088 089 public static QueryCondition create(QueryColumn queryColumn, String logic, Object value) { 090 QueryCondition condition = new QueryCondition(); 091 condition.setColumn(queryColumn); 092 condition.setLogic(logic); 093 condition.setValue(value); 094 return condition; 095 } 096 097 public QueryColumn getColumn() { 098 return column; 099 } 100 101 public void setColumn(QueryColumn column) { 102 this.column = column; 103 } 104 105 public Object getValue() { 106 return checkEffective() ? value : null; 107 } 108 109 public void setValue(Object value) { 110 this.value = value; 111 } 112 113 public String getLogic() { 114 return logic; 115 } 116 117 public void setLogic(String logic) { 118 this.logic = logic; 119 } 120 121 /** 122 * 动态条件构造。 123 * 124 * @param effective 是否启用该条件 125 * @return {@link QueryCondition} 126 */ 127 public QueryCondition when(boolean effective) { 128 if (notEmpty()) { 129 this.effective = effective; 130 } 131 return this; 132 } 133 134 /** 135 * 动态条件构造。 136 * 137 * @param fn 是否启用该条件 138 * @return {@link QueryCondition} 139 */ 140 public QueryCondition when(BooleanSupplier fn) { 141 if (notEmpty()) { 142 this.effective = fn.getAsBoolean(); 143 } 144 return this; 145 } 146 147 public boolean checkEffective() { 148 return effective; 149 } 150 151 152 public QueryCondition and(String sql) { 153 return and(new RawQueryCondition(sql)); 154 } 155 156 public QueryCondition and(String sql, Object... params) { 157 return and(new RawQueryCondition(sql, params)); 158 } 159 160 public QueryCondition and(QueryCondition nextCondition) { 161 return new Brackets(this).and(nextCondition); 162 } 163 164 public QueryCondition or(String sql) { 165 return or(new RawQueryCondition(sql)); 166 } 167 168 public QueryCondition or(String sql, Object... params) { 169 return or(new RawQueryCondition(sql, params)); 170 } 171 172 public QueryCondition or(QueryCondition nextCondition) { 173 return new Brackets(this).or(nextCondition); 174 } 175 176 protected void connect(QueryCondition nextCondition, SqlConnector connector) { 177 178 if (this.next != null) { 179 this.next.connect(nextCondition, connector); 180 } else { 181 nextCondition.connector = connector; 182 this.next = nextCondition; 183 nextCondition.prev = this; 184 } 185 } 186 187 public String toSql(List<QueryTable> queryTables, IDialect dialect) { 188 StringBuilder sql = new StringBuilder(); 189 //检测是否生效 190 if (checkEffective()) { 191 QueryCondition prevEffectiveCondition = getPrevEffectiveCondition(); 192 if (prevEffectiveCondition != null && this.connector != null) { 193 sql.append(this.connector); 194 } 195 //列 196 sql.append(getColumn().toConditionSql(queryTables, dialect)); 197 198 //逻辑符号 199 sql.append(logic); 200 201 //值(或者问号) 202 if (value instanceof QueryColumn) { 203 sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect)); 204 } 205 //子查询 206 else if (value instanceof QueryWrapper) { 207 sql.append(SqlConsts.BRACKET_LEFT) 208 .append(dialect.buildSelectSql((QueryWrapper) value)) 209 .append(SqlConsts.BRACKET_RIGHT); 210 } 211 //原生sql 212 else if (value instanceof RawQueryCondition) { 213 sql.append(((RawQueryCondition) value).getContent()); 214 } 215 //正常查询,构建问号 216 else { 217 appendQuestionMark(sql); 218 } 219 } 220 221 if (this.next != null) { 222 return sql + next.toSql(queryTables, dialect); 223 } 224 225 return sql.toString(); 226 } 227 228 229 /** 230 * 获取上一个 “有效” 的条件 231 * 232 * @return QueryCondition 233 */ 234 protected QueryCondition getPrevEffectiveCondition() { 235 if (prev == null) { 236 return null; 237 } 238 return prev.checkEffective() ? prev : prev.getPrevEffectiveCondition(); 239 } 240 241 protected QueryCondition getNextEffectiveCondition() { 242 if (next == null) { 243 return null; 244 } 245 return next.checkEffective() ? next : next.getNextEffectiveCondition(); 246 } 247 248 249 protected void appendQuestionMark(StringBuilder sqlBuilder) { 250 //noinspection StatementWithEmptyBody 251 if (SqlConsts.IS_NULL.equals(logic) 252 || SqlConsts.IS_NOT_NULL.equals(logic) 253 || value instanceof QueryColumn 254 || value instanceof QueryWrapper 255 || value instanceof RawQueryCondition) { 256 //do nothing 257 } 258 259 //between, not between 260 else if (SqlConsts.BETWEEN.equals(logic) || SqlConsts.NOT_BETWEEN.equals(logic)) { 261 sqlBuilder.append(SqlConsts.AND_PLACEHOLDER); 262 } 263 //in, not in 264 else if (SqlConsts.IN.equals(logic) || SqlConsts.NOT_IN.equals(logic)) { 265 int paramsCount = calculateValueArrayCount(); 266 sqlBuilder.append(SqlConsts.BRACKET_LEFT); 267 for (int i = 0; i < paramsCount; i++) { 268 sqlBuilder.append(SqlConsts.PLACEHOLDER); 269 if (i != paramsCount - 1) { 270 sqlBuilder.append(SqlConsts.DELIMITER); 271 } 272 } 273 sqlBuilder.append(SqlConsts.BRACKET_RIGHT); 274 } else { 275 sqlBuilder.append(SqlConsts.PLACEHOLDER); 276 } 277 } 278 279 280 private int calculateValueArrayCount() { 281 Object[] values = (Object[]) value; 282 int paramsCount = 0; 283 for (Object object : values) { 284 if (object != null && ClassUtil.isArray(object.getClass())) { 285 paramsCount += Array.getLength(object); 286 } else { 287 paramsCount++; 288 } 289 } 290 return paramsCount; 291 } 292 293 294 boolean containsTable(String... tables) { 295 if (column == null || !checkEffective()) { 296 return nextContainsTable(tables); 297 } 298 if (column instanceof FunctionQueryColumn) { 299 /* 300 * 连表分页查询的where中使用QueryMethods导致count查询优化错误 301 * fix https://github.com/mybatis-flex/mybatis-flex/issues/307 302 */ 303 List<QueryColumn> columns = ((FunctionQueryColumn)column).getColumns(); 304 for (QueryColumn queryColumn : columns) { 305 if (containsTable(queryColumn, tables)) { 306 return true; 307 } 308 } 309 } 310 return containsTable(column, tables) || nextContainsTable(tables); 311 } 312 313 boolean containsTable(QueryColumn column, String... tables) { 314 for (String table : tables) { 315 String tableName = StringUtil.getTableNameWithAlias(table)[0]; 316 if (column.table != null && tableName.equals(column.table.name)) { 317 return true; 318 } 319 } 320 return false; 321 } 322 323 boolean nextContainsTable(String... tables) { 324 if (next == null) { 325 return false; 326 } 327 return next.containsTable(tables); 328 } 329 330 @Override 331 public String toString() { 332 return "QueryCondition{" + 333 "column=" + column + 334 ", logic='" + logic + '\'' + 335 ", value=" + value + 336 ", effective=" + effective + 337 '}'; 338 } 339 340 @Override 341 public QueryCondition clone() { 342 try { 343 QueryCondition clone = (QueryCondition) super.clone(); 344 // deep clone ... 345 clone.column = ObjectUtil.clone(this.column); 346 clone.value = ObjectUtil.cloneObject(this.value); 347 clone.prev = clone.next = null; 348 if (this.next != null) { 349 clone.next = this.next.clone(); 350 clone.next.prev = clone; 351 } 352 return clone; 353 } catch (CloneNotSupportedException e) { 354 throw FlexExceptions.wrap(e); 355 } 356 } 357 358}