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; 020 021import java.io.Serializable; 022import java.lang.reflect.Array; 023import java.util.List; 024import java.util.function.Predicate; 025import java.util.function.Supplier; 026 027public class QueryCondition implements Serializable { 028 029 public static final String LOGIC_LIKE = "LIKE"; 030 public static final String LOGIC_GT = ">"; 031 public static final String LOGIC_GE = ">="; 032 public static final String LOGIC_LT = "<"; 033 public static final String LOGIC_LE = "<="; 034 public static final String LOGIC_EQUALS = "="; 035 public static final String LOGIC_NOT_EQUALS = "!="; 036 037 public static final String LOGIC_IS_NULL = "IS NULL"; 038 public static final String LOGIC_IS_NOT_NULL = "IS NOT NULL"; 039 040 public static final String LOGIC_IN = "IN"; 041 public static final String LOGIC_NOT_IN = "NOT IN"; 042 public static final String LOGIC_BETWEEN = "BETWEEN"; 043 public static final String LOGIC_NOT_BETWEEN = "NOT BETWEEN"; 044 045 046 protected QueryColumn column; 047 protected String logic; 048 protected Object value; 049 protected boolean effective = true; 050 051 //当前条件的上个条件 052 protected QueryCondition before; 053 //当前条件的上个下一个 054 protected QueryCondition next; 055 //两个条件直接的连接符 056 protected SqlConnector connector; 057 058 059 public static QueryCondition createEmpty() { 060 return new QueryCondition().when(false); 061 } 062 063 064 public static QueryCondition create(String table, String column, String logic, Object value) { 065 QueryCondition condition = new QueryCondition(); 066 condition.setColumn(new QueryColumn(table, column)); 067 condition.setLogic(logic); 068 condition.setValue(value); 069 return condition; 070 } 071 072 public static QueryCondition create(QueryColumn queryColumn, Object value) { 073 return create(queryColumn, LOGIC_EQUALS, value); 074 } 075 076 public static QueryCondition create(QueryColumn queryColumn, String logic, Object value) { 077 QueryCondition condition = new QueryCondition(); 078 condition.setColumn(queryColumn); 079 condition.setLogic(logic); 080 condition.setValue(value); 081 return condition; 082 } 083 084 public QueryCondition() { 085 } 086 087 public QueryColumn getColumn() { 088 return column; 089 } 090 091 public void setColumn(QueryColumn column) { 092 this.column = column; 093 } 094 095 public Object getValue() { 096 return checkEffective() ? value : null; 097 } 098 099 public void setValue(Object value) { 100 this.value = value; 101 } 102 103 public String getLogic() { 104 return logic; 105 } 106 107 public void setLogic(String logic) { 108 this.logic = logic; 109 } 110 111 112 public QueryCondition when(boolean effective) { 113 this.effective = effective; 114 return this; 115 } 116 117 public void when(Supplier<Boolean> fn) { 118 Boolean effective = fn.get(); 119 this.effective = (effective != null && effective); 120 } 121 122 public <T> QueryCondition when(Predicate<T> fn){ 123 Object val = this.value; 124 if (LOGIC_LIKE.equals(logic) && val instanceof String) { 125 String valStr = (String) val; 126 if (valStr.startsWith("%")) { 127 valStr = valStr.substring(1); 128 } 129 if (valStr.endsWith("%")) { 130 valStr = valStr.substring(0, valStr.length() - 1); 131 } 132 val = valStr; 133 } 134 this.effective = fn.test((T) val); 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 StringQueryCondition(sql)); 145 } 146 147 public QueryCondition and(String sql, Object... params) { 148 return and(new StringQueryCondition(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 StringQueryCondition(sql)); 157 } 158 159 public QueryCondition or(String sql, Object... params) { 160 return or(new StringQueryCondition(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 if (this.next != null) { 169 this.next.connect(nextCondition, connector); 170 } else { 171 this.next = nextCondition; 172 this.connector = connector; 173 nextCondition.before = this; 174 } 175 } 176 177 public String toSql(List<QueryTable> queryTables, IDialect dialect) { 178 StringBuilder sql = new StringBuilder(); 179 //检测是否生效 180 if (checkEffective()) { 181 QueryCondition effectiveBefore = getEffectiveBefore(); 182 if (effectiveBefore != null) { 183 sql.append(effectiveBefore.connector); 184 } 185 sql.append(getColumn().toConditionSql(queryTables, dialect)); 186 sql.append(" ").append(logic).append(" "); 187 if (value instanceof QueryColumn) { 188 sql.append(((QueryColumn) value).toConditionSql(queryTables, dialect)); 189 } 190 //子查询 191 else if (value instanceof QueryWrapper) { 192 sql.append("(").append(dialect.buildSelectSql((QueryWrapper) value)).append(")"); 193 } 194 //原生sql 195 else if (value instanceof RawValue) { 196 sql.append(((RawValue) value).getContent()); 197 } 198 //正常查询,构建问号 199 else { 200 appendQuestionMark(sql); 201 } 202 } 203 204 if (this.next != null) { 205 return sql + next.toSql(queryTables, dialect); 206 } 207 208 return sql.toString(); 209 } 210 211 212 protected QueryCondition getEffectiveBefore() { 213 if (before != null && before.checkEffective()) { 214 return before; 215 } else if (before != null) { 216 return before.getEffectiveBefore(); 217 } else { 218 return null; 219 } 220 } 221 222 223 protected void appendQuestionMark(StringBuilder sqlBuilder) { 224 if (LOGIC_IS_NULL.equals(logic) 225 || LOGIC_IS_NOT_NULL.equals(logic) 226 || value instanceof QueryColumn 227 || value instanceof QueryWrapper 228 || value instanceof RawValue) { 229 //do nothing 230 } 231 232 //between, not between 233 else if (LOGIC_BETWEEN.equals(logic) || LOGIC_NOT_BETWEEN.equals(logic)) { 234 sqlBuilder.append(" ? AND ? "); 235 } 236 //in, not in 237 else if (LOGIC_IN.equals(logic) || LOGIC_NOT_IN.equals(logic)) { 238 int paramsCount = calculateValueArrayCount(); 239 sqlBuilder.append('('); 240 for (int i = 0; i < paramsCount; i++) { 241 sqlBuilder.append('?'); 242 if (i != paramsCount - 1) { 243 sqlBuilder.append(','); 244 } 245 } 246 sqlBuilder.append(')'); 247 } else { 248 sqlBuilder.append(" ? "); 249 } 250 } 251 252 253 private int calculateValueArrayCount() { 254 Object[] values = (Object[]) value; 255 int paramsCount = 0; 256 for (Object object : values) { 257 if (object != null && (object.getClass().isArray() 258 || object.getClass() == int[].class 259 || object.getClass() == long[].class 260 || object.getClass() == short[].class 261 || object.getClass() == float[].class 262 || object.getClass() == double[].class)) { 263 paramsCount += Array.getLength(object); 264 } else { 265 paramsCount++; 266 } 267 } 268 return paramsCount; 269 } 270 271 @Override 272 public String toString() { 273 return "QueryCondition{" + 274 "column=" + column + 275 ", logic='" + logic + '\'' + 276 ", value=" + value + 277 ", effective=" + effective + 278 '}'; 279 } 280}