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