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