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