Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.yx.db.sql.Select Maven / Gradle / Ivy
/**
* Copyright (C) 2016 - 2030 youtongluan.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.yx.db.sql;
import static org.yx.db.sql.Operation.BIG;
import static org.yx.db.sql.Operation.BIG_EQUAL;
import static org.yx.db.sql.Operation.IN;
import static org.yx.db.sql.Operation.LESS;
import static org.yx.db.sql.Operation.LESS_EQUAL;
import static org.yx.db.sql.Operation.LIKE;
import static org.yx.db.sql.Operation.NOT;
import static org.yx.db.sql.Operation.NOT_IN;
import static org.yx.db.sql.Operation.NOT_LIKE;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.yx.db.enums.CacheType;
import org.yx.db.enums.CompareNullPolicy;
import org.yx.db.event.DBEventPublisher;
import org.yx.db.event.QueryEvent;
import org.yx.db.kit.DBKits;
import org.yx.db.visit.Exchange;
import org.yx.db.visit.PojoResultHandler;
import org.yx.db.visit.ResultHandler;
import org.yx.db.visit.SumkDbVisitor;
import org.yx.exception.SumkException;
import org.yx.util.CollectionUtil;
import org.yx.util.kit.Asserts;
/**
* 比较跟整个addEqual是add关系。同一种比较类型,比如less,它的一个key只能设置一次,后设置的会覆盖前面设置的
* 比较中用到的key,都是java中的key,大小写敏感.
*/
public class Select extends SelectBuilder {
public Select(SumkDbVisitor>> visitor) {
super(visitor);
}
/**
* @param fail
* 如果为true,会验证map参数中,是否存在无效的key,预防开发人员将key写错。默认为true
* @return 当前对象
*/
public Select failIfPropertyNotMapped(boolean fail) {
this.failIfPropertyNotMapped = fail;
return this;
}
/**
* 物理分表的情况下,设置分区名。这个方法只能调用一次
*
* @param sub
* 分区名
* @return 当前对象
*/
public Select partition(String sub) {
sub(sub);
return this;
}
/**
* 允许不设置where条件
*
* @param empty
* true表示允许where条件为空
* @return 当前对象
*/
public Select allowEmptyWhere(boolean empty) {
this.allowEmptyWhere = empty;
return this;
}
protected ResultHandler resultHandler;
public Select resultHandler(ResultHandler resultHandler) {
this.resultHandler = Objects.requireNonNull(resultHandler);
return this;
}
/**
* select的where比较条件中,对于null的处理
*
* @param policy
* null的处理策略
* @return 当前对象
*/
public Select compareNullPolicy(CompareNullPolicy policy) {
this.compareNullPolicy = Objects.requireNonNull(policy);
return this;
}
protected Select addCompares(Operation op, Object pojo) {
Map map = this.populate(pojo, false);
if (CollectionUtil.isEmpty(map)) {
return this;
}
for (Map.Entry en : map.entrySet()) {
this.setCompare(op, en.getKey(), en.getValue());
}
return this;
}
protected Select setCompare(Operation op, String key, Object value) {
if (key == null || key.isEmpty()) {
return this;
}
if (_compare == null) {
_compare = new ArrayList<>(8);
}
this._compare.add(new ColumnOperation(key, op, value));
return this;
}
/**
* 设置大于,一个key只能设置一次,后设置的会覆盖前面设置的。
*
* @param key
* java字段的名称
* @param value
* 值
* @return 当前对象
*/
public Select bigThan(String key, Object value) {
return setCompare(BIG, key, value);
}
public Select bigOrEqual(String key, Object value) {
return setCompare(BIG_EQUAL, key, value);
}
public Select lessThan(String key, Object value) {
return setCompare(LESS, key, value);
}
public Select lessOrEqual(String key, Object value) {
return setCompare(LESS_EQUAL, key, value);
}
/**
* like操作,%号要自己添加
*
* @param key
* 字段名
* @param value
* 值,不会自动添加%
* @return 当前对象
*/
public Select like(String key, Object value) {
return setCompare(LIKE, key, value);
}
public Select notLike(String key, Object value) {
return setCompare(NOT_LIKE, key, value);
}
/**
* 不等于操作
*
* @param key
* 字段名
* @param value
* 值,不会自动添加%
* @return 当前对象
*/
public Select not(String key, Object value) {
return setCompare(NOT, key, value);
}
/**
* sql中的in查询
*
* @param key
* 字段名
* @param values
* 值列表,不能为空
* @return 当前对象
*/
public Select in(String key, Collection values) {
return setCompare(IN, key, values.toArray(new Object[values.size()]));
}
public Select notIn(String key, Collection values) {
return setCompare(NOT_IN, key, values.toArray(new Object[values.size()]));
}
public Select bigThan(Object pojo) {
return addCompares(BIG, pojo);
}
/**
* 大于等于
*
* @param pojo
* 对pojo中所有的kv做大于等于操作
* @return 当前对象
*/
public Select bigOrEqual(Object pojo) {
return addCompares(BIG_EQUAL, pojo);
}
public Select lessThan(Object pojo) {
return addCompares(LESS, pojo);
}
/**
* 小于或等于
*
* @param pojo
* 对pojo中所有的kv做小于等于操作
* @return 当前对象
*/
public Select lessOrEqual(Object pojo) {
return addCompares(LESS_EQUAL, pojo);
}
public Select like(Object pojo) {
return addCompares(LIKE, pojo);
}
public Select not(Object pojo) {
return addCompares(NOT, pojo);
}
/**
* 根据字段名和判断条件移除所有符合条件的比较
*
* @param key
* java字段名,可以为null。null表示所有的字段都要移除
* @param op
* 比较条件,可以为null。null表示所有的条件都要移除
* @return 当前对象
*/
public Select removeCompares(String key, Operation op) {
if (_compare == null) {
return this;
}
Iterator it = this._compare.iterator();
while (it.hasNext()) {
ColumnOperation cp = it.next();
if (key != null && !key.equals(cp.name)) {
continue;
}
if (op != null && cp.type != op) {
continue;
}
it.remove();
}
if (this._compare.isEmpty()) {
this._compare = null;
}
return this;
}
/**
* 升序排列。asc和desc的调用顺序决定了在sql中出现的顺序。 此方法可以调用多次
*
* @param field
* 升序字段
* @return 当前对象
*/
public Select orderByAsc(String field) {
return this.addOrderBy(field, false);
}
protected Select addOrderBy(String name, boolean desc) {
if (this.orderby == null) {
this.orderby = new ArrayList<>(2);
}
this.orderby.add(new Order(name, desc));
return this;
}
/**
* 增加降序排列
*
* @param field
* 降序字段
* @return 当前对象
*/
public Select orderByDesc(String field) {
return this.addOrderBy(field, true);
}
/**
* 设置查询的便宜量,从0开始。
*
* @param offset
* from的位置
* @return 当前对象
*/
public Select offset(int offset) {
Asserts.requireTrue(offset >= 0, "offset must bigger or equal than 0");
this.offset = offset;
return this;
}
/**
*
* @param limit
* 返回的最大条数。
* @return 当前对象
*/
public Select limit(int limit) {
Asserts.requireTrue(limit >= 0, "limit must bigger or equal than 0");
this.limit = limit;
return this;
}
/**
*
* @param columns
* 设置查询放回的列,列名是java中的字段名。如果不设,将返回所有的字段
* @return 当前对象
*/
public Select selectColumns(String... columns) {
if (columns == null || columns.length == 0) {
this.selectColumns = null;
return this;
}
this.selectColumns = Arrays.asList(columns);
return this;
}
/**
* 如果为false,就不会从缓存中加载数据
*
* @param fromCache
* 默认为true。sumk.sql.fromCache=false可以将全局参数设为false
* @return 当前对象
*/
public Select fromCache(boolean fromCache) {
this.fromCache = fromCache;
return this;
}
/**
* 如果为false,查出的结果将不会用于更新缓存
*
* @param toCache
* 该参数设为true的实际意义不大
* @return 当前对象
*/
public Select toCache(boolean toCache) {
this.toCache = toCache;
return this;
}
/**
* 设置相等的条件。本方法可以被多次执行。 src中的各个条件是and关系。不同src之间是or关系
* 注意:如果pojo是map类型,那么它的null值是有效条件
*
* @param src
* map或pojo类型。
* @return 当前对象
*/
public Select addEqual(Object src) {
this._addIn(src);
return this;
}
/**
* 各个addEqual之间的条件是OR,如果要组装AND条件,请用addEqual(Object src)
*
* @param field
* 字段名
* @param value
* 要查询的条件的值
* @return 当前对象
*/
public Select addEqual(String field, Object value) {
this._addInByMap(Collections.singletonMap(field, value));
return this;
}
/**
* 传入多个条件
*
* @param ins
* 集合各元素之间是or关系,对象中各个kv是and关系
* @return 当前对象
*/
public Select addEquals(Collection ins) {
for (Object src : ins) {
this._addIn(src);
}
return this;
}
/**
* 通过数据库主键列表查询主键,单主键ids就只有一个,多主键就传入多个 注意:调用本方法之前,要确保调用过tableClass()方法
*
* @param ids
* id列表,顺序跟pojo中定义的一致(按order顺序或书写顺序)
* @return 注意:调用本方法之前,要确保调用过tableClass()方法
*/
public Select byDatabaseId(Object... ids) {
return byId(true, ids);
}
/**
* 通过redis主键列表查询主键,单主键ids就只有一个,多主键就传入多个
* 所有id属于同一条记录,如果要使用单主键的in查询,请用本类的in方法
* 注意:调用本方法之前,要确保调用过tableClass()方法
*
* @param ids
* id列表,顺序跟pojo中定义的一致(按order顺序或书写顺序)
* @return 当前对象
*
*/
public Select byCacheId(Object... ids) {
return byId(false, ids);
}
protected Select byId(boolean databaseId, Object... ids) {
if (ids == null || ids.length == 0) {
return this;
}
makeSurePojoMeta();
List cms = databaseId ? this.pojoMeta.getDatabaseIds() : this.pojoMeta.getCacheIDs();
Asserts.requireTrue(cms != null && cms.size() == ids.length, pojoMeta.getTableName() + "没有设置主键,或者主键个数跟参数个数不一致");
Map map = new HashMap<>();
for (int i = 0; i < ids.length; i++) {
map.put(cms.get(i).getFieldName(), ids[i]);
}
_addInByMap(map);
return this;
}
public Select tableClass(Class tableClass) {
this.tableClass = tableClass;
return this;
}
protected ResultHandler resultHandler() {
return this.resultHandler == null ? PojoResultHandler.handler : this.resultHandler;
}
protected boolean canUseInCache() {
return this.pojoMeta.cacheIDs.size() == 1 && CollectionUtil.isEmpty(this.in)
&& this.pojoMeta.cacheType() == CacheType.SINGLE && _compare != null && _compare.size() == 1
&& _compare.get(0).type == IN
&& _compare.get(0).name.equals(this.pojoMeta.cacheIDs.get(0).field.getName());
}
protected List queryFromCache(Exchange exchange) throws Exception {
if (CollectionUtil.isEmpty(this.in) && CollectionUtil.isEmpty(this._compare)) {
return null;
}
if (!(fromCache && this.orderby == null && this.offset == 0 && !pojoMeta.isNoCache())) {
return null;
}
List list = new ArrayList<>();
String singleKeyName = this.canUseInCache() ? this.pojoMeta.cacheIDs.get(0).field.getName() : null;
if (singleKeyName != null) {
Object[] vs = (Object[]) _compare.get(0).value;
List> newIN = new ArrayList<>(vs.length);
for (Object v : vs) {
newIN.add(Collections.singletonMap(singleKeyName, v));
}
exchange.setLeftIn(newIN);
} else if (CollectionUtil.isEmpty(_compare)) {
exchange.setLeftIn(this.in);
} else {
return null;
}
exchange.findFromCache(pojoMeta);
if (exchange.getData() != null && exchange.getData().size() > 0) {
List tmp = this.resultHandler().parseFromJson(pojoMeta, exchange.getData(),
CollectionUtil.unmodifyList(this.selectColumns));
if (tmp != null && tmp.size() > 0) {
list.addAll(tmp);
}
}
List> left = exchange.getLeftIn();
if (left == null) {
return list;
}
if (singleKeyName != null) {
List vs = new ArrayList<>(left.size());
for (Map m : left) {
vs.add(m.get(singleKeyName));
}
this._compare = Collections.singletonList(new ColumnOperation(singleKeyName, IN, vs.toArray()));
} else {
this.in = left;
}
return list;
}
public List queryList() {
final List> origin = this.in = CollectionUtil.unmodifyList(this.in);
final List orginCompare = this._compare = CollectionUtil.unmodifyList(this._compare);
try {
makeSurePojoMeta().getCounter().incrQueryCount();
Exchange exchange = new Exchange();
List list = this.queryFromCache(exchange);
if (list != null && CollectionUtil.isEmpty(exchange.getLeftIn())) {
return list;
}
boolean canUseCache = true;
if (list == null) {
list = Collections.emptyList();
canUseCache = false;
}
List dbData = this.resultHandler().parse(pojoMeta, this.accept(visitor));
if (dbData == null || dbData.isEmpty()) {
return list;
}
list = merge(list, dbData);
List> eventIn = canUseCache ? exchange.getLeftIn() : this.in;
if (this.toCache && selectColumns == null && this.offset == 0
&& (canUseCache || CollectionUtil.isEmpty(this._compare))
&& (limit <= 0 || limit >= DBSettings.asNoLimit()) && CollectionUtil.isNotEmpty(eventIn)
&& eventIn.size() == 1 && dbData.size() < DBSettings.maxQueryCacheSize()) {
QueryEvent event = new QueryEvent(this.pojoMeta.getTableName());
event.setIn(eventIn);
event.setResult(dbData);
DBEventPublisher.publish(event);
}
if (this.limit > 0 && list.size() > this.limit) {
return list.subList(0, this.limit);
}
return list;
} catch (Exception e) {
throw SumkException.wrap(e);
} finally {
this.in = origin;
this._compare = orginCompare;
}
}
protected List merge(List cacheList, List dbList) throws Exception {
if (cacheList.isEmpty()) {
return dbList;
}
List ret = new ArrayList<>(cacheList.size() + dbList.size());
ret.addAll(dbList);
Set keys = new HashSet<>();
PojoMeta pm = this.pojoMeta;
List columns = pm.databaseIds;
for (T t : dbList) {
String key = pm.joinColumns(t, false, columns);
if (key != null) {
keys.add(key);
}
}
for (T t : cacheList) {
String key = pm.joinColumns(t, false, columns);
if (key == null || keys.add(key)) {
ret.add(t);
}
}
return ret;
}
public T queryOne() {
return DBKits.queryOne(this.queryList());
}
/**
* 根据select的条件,查询符合条件的记录数。其中offset、limit、order by属性被过滤掉
* 这个方法可以在select执行前调用,也可以在select执行后调用
*
* @return 符合条件的数据库记录数
*/
public long count() {
return new Count(this).execute();
}
}