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}