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 table, String column, String logic, Object value) { 066 QueryCondition condition = new QueryCondition(); 067 condition.setColumn(new QueryColumn(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 StringQueryCondition(sql)); 146 } 147 148 public QueryCondition and(String sql, Object... params) { 149 return and(new StringQueryCondition(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 StringQueryCondition(sql)); 158 } 159 160 public QueryCondition or(String sql, Object... params) { 161 return or(new StringQueryCondition(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 sql.append(getColumn().toConditionSql(queryTables, dialect)); 187 sql.append(" ").append(logic).append(" "); 188 if (value instanceof QueryColumn) { 189 sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect)); 190 } 191 //子查询 192 else if (value instanceof QueryWrapper) { 193 sql.append("(").append(dialect.buildSelectSql((QueryWrapper) value)).append(")"); 194 } 195 //原生sql 196 else if (value instanceof RawValue) { 197 sql.append(((RawValue) value).getContent()); 198 } 199 //正常查询,构建问号 200 else { 201 appendQuestionMark(sql); 202 } 203 } 204 205 if (this.next != null) { 206 return sql + next.toSql(queryTables, dialect); 207 } 208 209 return sql.toString(); 210 } 211 212 213 protected QueryCondition getEffectiveBefore() { 214 if (before != null && before.checkEffective()) { 215 return before; 216 } else if (before != null) { 217 return before.getEffectiveBefore(); 218 } else { 219 return null; 220 } 221 } 222 223 224 protected void appendQuestionMark(StringBuilder sqlBuilder) { 225 if (LOGIC_IS_NULL.equals(logic) 226 || LOGIC_IS_NOT_NULL.equals(logic) 227 || value instanceof QueryColumn 228 || value instanceof QueryWrapper 229 || value instanceof RawValue) { 230 //do nothing 231 } 232 233 //between, not between 234 else if (LOGIC_BETWEEN.equals(logic) || LOGIC_NOT_BETWEEN.equals(logic)) { 235 sqlBuilder.append(" ? AND ? "); 236 } 237 //in, not in 238 else if (LOGIC_IN.equals(logic) || LOGIC_NOT_IN.equals(logic)) { 239 int paramsCount = calculateValueArrayCount(); 240 sqlBuilder.append('('); 241 for (int i = 0; i < paramsCount; i++) { 242 sqlBuilder.append('?'); 243 if (i != paramsCount - 1) { 244 sqlBuilder.append(','); 245 } 246 } 247 sqlBuilder.append(')'); 248 } else { 249 sqlBuilder.append(" ? "); 250 } 251 } 252 253 254 private int calculateValueArrayCount() { 255 Object[] values = (Object[]) value; 256 int paramsCount = 0; 257 for (Object object : values) { 258 if (object != null && ClassUtil.isArray(object.getClass())) { 259 paramsCount += Array.getLength(object); 260 } else { 261 paramsCount++; 262 } 263 } 264 return paramsCount; 265 } 266 267 @Override 268 public String toString() { 269 return "QueryCondition{" + 270 "column=" + column + 271 ", logic='" + logic + '\'' + 272 ", value=" + value + 273 ", effective=" + effective + 274 '}'; 275 } 276}