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