diff --git a/src/main/java/org/redkale/source/AbstractDataSqlSource.java b/src/main/java/org/redkale/source/AbstractDataSqlSource.java new file mode 100644 index 000000000..13ba55839 --- /dev/null +++ b/src/main/java/org/redkale/source/AbstractDataSqlSource.java @@ -0,0 +1,3283 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.math.*; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.Stream; +import org.redkale.annotation.AutoLoad; +import org.redkale.annotation.*; +import org.redkale.annotation.ResourceListener; +import org.redkale.annotation.ResourceType; +import static org.redkale.boot.Application.*; +import org.redkale.net.AsyncGroup; +import org.redkale.persistence.Table; +import org.redkale.service.Local; +import org.redkale.source.EntityInfo.EntityColumn; +import org.redkale.util.*; + +/** + * DataSource的SQL抽象实现类
+ * 注意: 所有的操作只能作用在一张表上,不能同时变更多张表 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(DataSource.class) +public abstract class AbstractDataSqlSource extends AbstractDataSource implements DataSqlSource, Function { + + //不存在分表时最大重试次数 + protected static final int MAX_RETRYS = 3; + + protected static final Flipper FLIPPER_ONE = new Flipper(1); + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected String name; + + protected boolean cacheForbidden; + + protected String dbtype; + + private boolean autoDDL; + + protected Properties readConfProps; + + protected Properties writeConfProps; + + @Resource(name = RESNAME_APP_CLIENT_ASYNCGROUP, required = false) + protected AsyncGroup clientAsyncGroup; + + //配置 APP_EXECUTOR资源为null + @Resource(name = RESNAME_APP_EXECUTOR, required = false) + protected ExecutorService workExecutor; + + protected BiFunction sqlFormatter; + + protected BiConsumer errorCompleteConsumer = (r, t) -> { + //if (t != null) logger.log(Level.INFO, "CompletableFuture complete error", (Throwable) t); + }; + + protected final BiFunction> fullloader = (s, i) + -> ((CompletableFuture) querySheetDBAsync(i, false, false, false, null, null, (FilterNode) null)).thenApply(e -> e == null ? new ArrayList() : e.list(true)); + + //超过多少毫秒视为较慢, 会打印警告级别的日志, 默认值: 2000 + protected long slowmsWarn; + + //超过多少毫秒视为很慢, 会打印错误级别的日志, 默认值: 3000 + protected long slowmsError; + + //用于反向LIKE使用 + protected String containSQL; + + //用于反向LIKE使用 + protected String notContainSQL; + + //用于判断表不存在的使用, 多个SQLState用;隔开 + protected String tableNotExistSqlstates; + + //用于复制表结构使用, sql语句必须包含IF NOT EXISTS判断,确保重复执行不会报错 + protected String tablecopySQL; + + protected AnyValue config; + + private EntityInfo currEntityInfo; + + public AbstractDataSqlSource() { + } + + @Override + public void init(AnyValue conf) { + super.init(conf); + this.config = conf; + if (conf.getAnyValue("read") == null) { //没有读写分离 + Properties rwConf = new Properties(); + conf.forEach((k, v) -> rwConf.put(k, decryptProperty(k, v))); + this.dbtype = parseDbtype(rwConf.getProperty(DATA_SOURCE_URL)); + initProperties(rwConf); + this.readConfProps = rwConf; + this.writeConfProps = rwConf; + } else { //读写分离 + Properties readConf = new Properties(); + Properties writeConf = new Properties(); + conf.getAnyValue("read").forEach((k, v) -> readConf.put(k, decryptProperty(k, v))); + conf.getAnyValue("write").forEach((k, v) -> writeConf.put(k, decryptProperty(k, v))); + this.dbtype = parseDbtype(readConf.getProperty(DATA_SOURCE_URL)); + initProperties(readConf); + initProperties(writeConf); + this.readConfProps = readConf; + this.writeConfProps = writeConf; + } + this.name = conf.getValue("name", ""); + this.sqlFormatter = (info, val) -> formatValueToString(info, val); + afterResourceChange(); + } + + protected void afterResourceChange() { + this.autoDDL = "true".equals(readConfProps.getProperty(DATA_SOURCE_TABLE_AUTODDL, "false").trim()); + + this.containSQL = readConfProps.getProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0"); + this.notContainSQL = readConfProps.getProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0"); + + this.tableNotExistSqlstates = ";" + readConfProps.getProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";"; + this.tablecopySQL = readConfProps.getProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}"); + + this.cacheForbidden = "NONE".equalsIgnoreCase(readConfProps.getProperty(DATA_SOURCE_CACHEMODE)); + this.slowmsWarn = Integer.parseInt(readConfProps.getProperty(DATA_SOURCE_SLOWMS_WARN, "2000").trim()); + this.slowmsError = Integer.parseInt(readConfProps.getProperty(DATA_SOURCE_SLOWMS_ERROR, "3000").trim()); + } + + @Override + @ResourceListener + public void onResourceChange(ResourceEvent[] events) { + if (events == null || events.length < 1) { + return; + } + //不支持读写分离模式的动态切换 + if (readConfProps == writeConfProps && (events[0].name().startsWith("read.") || events[0].name().startsWith("write."))) { + throw new SourceException("DataSource(name=" + resourceName() + ") not support to change to read/write separation mode"); + } + if (readConfProps != writeConfProps && (!events[0].name().startsWith("read.") && !events[0].name().startsWith("write."))) { + throw new SourceException("DataSource(name=" + resourceName() + ") not support to change to non read/write separation mode"); + } + + StringBuilder sb = new StringBuilder(); + if (readConfProps == writeConfProps) { + List allEvents = new ArrayList<>(); + Properties newProps = new Properties(); + newProps.putAll(this.readConfProps); + for (ResourceEvent event : events) { //可能需要解密 + String newValue = decryptProperty(event.name(), event.newValue().toString()); + allEvents.add(ResourceEvent.create(event.name(), newValue, event.oldValue())); + newProps.put(event.name(), newValue); + sb.append("DataSource(name=").append(resourceName()).append(") change '") + .append(event.name()).append("' to '").append(event.coverNewValue()).append("'\r\n"); + } + updateOneResourceChange(newProps, allEvents.toArray(new ResourceEvent[allEvents.size()])); + for (ResourceEvent event : allEvents) { + this.readConfProps.put(event.name(), event.newValue()); + } + } else { + List readEvents = new ArrayList<>(); + List writeEvents = new ArrayList<>(); + Properties newReadProps = new Properties(); + newReadProps.putAll(this.readConfProps); + Properties newWriteProps = new Properties(); + newWriteProps.putAll(this.writeConfProps); + for (ResourceEvent event : events) { + if (event.name().startsWith("read.")) { + String newName = event.name().substring("read.".length()); + String newValue = decryptProperty(event.name(), event.newValue().toString()); + readEvents.add(ResourceEvent.create(newName, newValue, event.oldValue())); + newReadProps.put(event.name(), newValue); + } else { + String newName = event.name().substring("write.".length()); + String newValue = decryptProperty(event.name(), event.newValue().toString()); + writeEvents.add(ResourceEvent.create(newName, newValue, event.oldValue())); + newWriteProps.put(event.name(), newValue); + } + sb.append("DataSource(name=").append(resourceName()).append(") change '") + .append(event.name()).append("' to '").append(event.coverNewValue()).append("'\r\n"); + } + if (!readEvents.isEmpty()) { + updateReadResourceChange(newReadProps, readEvents.toArray(new ResourceEvent[readEvents.size()])); + } + if (!writeEvents.isEmpty()) { + updateWriteResourceChange(newWriteProps, writeEvents.toArray(new ResourceEvent[writeEvents.size()])); + } + //更新Properties + if (!readEvents.isEmpty()) { + for (ResourceEvent event : readEvents) { + this.readConfProps.put(event.name(), event.newValue()); + } + } + if (!writeEvents.isEmpty()) { + for (ResourceEvent event : writeEvents) { + this.writeConfProps.put(event.name(), event.newValue()); + } + } + } + afterResourceChange(); + if (sb.length() > 0) { + logger.log(Level.INFO, sb.toString()); + } + } + + protected void updateOneResourceChange(Properties newProps, ResourceEvent[] events) { + throw new UnsupportedOperationException("Not supported yet."); + } + + protected void updateReadResourceChange(Properties newReadProps, ResourceEvent[] events) { + throw new UnsupportedOperationException("Not supported yet."); + } + + protected void updateWriteResourceChange(Properties newWriteProps, ResourceEvent[] events) { + throw new UnsupportedOperationException("Not supported yet."); + } + + protected void slowLog(long startTime, String... sqls) { + long cost = System.currentTimeMillis() - startTime; + if (slowmsError > 0 && cost > slowmsError) { + logger.log(Level.SEVERE, DataSource.class.getSimpleName() + "(name='" + resourceName() + "') slow sql cost " + cost + " ms, content: " + Arrays.toString(sqls)); + } else if (slowmsWarn > 0 && cost > slowmsWarn) { + logger.log(Level.WARNING, DataSource.class.getSimpleName() + "(name='" + resourceName() + "') very slow sql cost " + cost + " ms, content: " + Arrays.toString(sqls)); + } + } + + protected String parseNotExistTableName(SQLException e) { + String errmsg = e.getMessage(); + char quote = '"'; + String tableName = null; + int pos = errmsg.indexOf(quote); + if (pos < 0) { + quote = '\''; + pos = errmsg.indexOf(quote); + } + if (pos >= 0) { + int pos2 = errmsg.indexOf(quote, pos + 1); + if (pos2 > pos) { + tableName = errmsg.substring(pos + 1, pos2); + } + } + return tableName; + } + + //解密可能存在的加密字段, 可重载 + protected String decryptProperty(String key, String value) { + return value; + } + + protected void initProperties(Properties props) { + if ("oracle".equals(this.dbtype)) { + props.setProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0"); + props.setProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0"); + if (!props.containsKey(DATA_SOURCE_TABLENOTEXIST_SQLSTATES)) { + props.setProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42000;42S02"); + } + if (!props.containsKey(DATA_SOURCE_TABLECOPY_SQLTEMPLATE)) { + //注意:此语句复制表结构会导致默认值和主键信息的丢失 + props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2"); + } + } else if ("sqlserver".equals(this.dbtype)) { + props.setProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0"); + props.setProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0"); + } else if ("postgresql".equals(this.dbtype)) { + if (!props.containsKey(DATA_SOURCE_TABLECOPY_SQLTEMPLATE)) { //注意:此语句复制表结构会导致默认值和主键信息的丢失 + //注意:postgresql不支持跨库复制表结构 + //props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS (SELECT * FROM ${oldtable} LIMIT 0)"); + props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} (LIKE ${oldtable} INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING INDEXES)"); + } + if (!props.containsKey(DATA_SOURCE_TABLENOTEXIST_SQLSTATES)) { + props.setProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42P01;3F000"); + } + } + } + + @Override + public String toString() { + if (readConfProps == null) { //compileMode模式下会为null + return getClass().getSimpleName() + "{}"; + } + if (readConfProps == writeConfProps) { + String url = readConfProps.getProperty(DATA_SOURCE_URL); + int pos = url.indexOf('?'); + if (pos > 0) { + url = url.substring(0, pos) + "..."; + } + return getClass().getSimpleName() + "{url=" + url + "}"; + } else { + String readUrl = readConfProps.getProperty(DATA_SOURCE_URL); + int pos = readUrl.indexOf('?'); + if (pos > 0) { + readUrl = readUrl.substring(0, pos) + "..."; + } + String writeUrl = writeConfProps.getProperty(DATA_SOURCE_URL); + pos = writeUrl.indexOf('?'); + if (pos > 0) { + writeUrl = writeUrl.substring(0, pos) + "..."; + } + return getClass().getSimpleName() + "{readurl=" + readUrl + ",writeurl=" + writeUrl + "}"; + } + } + + //生成创建表的SQL + protected String[] createTableSqls(EntityInfo info) { + if (info == null || !autoDDL) { + return null; + } + Table table = info.getType().getAnnotation(Table.class); + if ("mysql".equals(dbtype())) { //mysql + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE IF NOT EXISTS `").append(info.getOriginTable()).append("`(\n"); + EntityColumn primary = null; + T one = info.constructorAttributes == null ? info.getCreator().create() : null; + for (EntityColumn column : info.getDDLColumns()) { + if (column.primary) { + primary = column; + } + String sqltype = "VARCHAR(" + column.length + ")"; + String sqlnull = column.primary ? "NOT NULL" : "NULL"; + if (column.type == boolean.class || column.type == Boolean.class) { + sqltype = "TINYINT(1)"; + Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); + } else if (column.type == byte.class || column.type == Byte.class) { + sqltype = "TINYINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); + } else if (column.type == short.class || column.type == Short.class) { + sqltype = "SMALLINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == char.class || column.type == Character.class) { + sqltype = "SMALLINT UNSIGNED"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); + } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { + sqltype = "INT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { + sqltype = "BIGINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == float.class || column.type == Float.class) { + sqltype = "FLOAT"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == double.class || column.type == Double.class) { + sqltype = "DOUBLE"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigInteger.class) { + sqltype = "DECIMAL"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } else { + sqltype += "(19,2)"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigDecimal.class) { + sqltype = "DECIMAL"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == String.class) { + if (column.length < 65535) { + String val = one == null ? null : (String) info.getAttribute(column.field).get(one); + if (val != null) { + sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; + } else if (column.primary) { + sqlnull = "NOT NULL DEFAULT ''"; + } + } else if (column.length == 65535) { + sqltype = "TEXT"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } else if (column.length <= 16777215) { + sqltype = "MEDIUMTEXT"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } else { + sqltype = "LONGTEXT"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } + } else if (column.type == byte[].class) { + if (column.length <= 65535) { + sqltype = "BLOB"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } else if (column.length <= 16777215) { + sqltype = "MEDIUMBLOB"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } else { + sqltype = "LONGBLOB"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } + } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { + sqltype = "DATE"; + } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { + sqltype = "TIME"; + } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { + sqltype = "DATETIME"; + } else { //JavaBean + sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); + sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; + } + sb.append(" `").append(column.column).append("` ").append(sqltype).append(" ").append(sqlnull); + if (column.comment != null && !column.comment.isEmpty()) { + sb.append(" COMMENT '").append(column.comment.replace('\'', '"')).append("'"); + } + sb.append(",\n"); + } + sb.append(" PRIMARY KEY (`").append(primary.column).append("`)\n"); + sb.append(")ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4"); + if (table != null && !table.comment().isEmpty()) { + sb.append(" COMMENT '").append(table.comment().replace('\'', '"')).append("'"); + } + return Utility.ofArray(sb.toString()); + } else if ("postgresql".equals(dbtype())) { //postgresql + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE IF NOT EXISTS ").append(info.getOriginTable()).append("(\n"); + EntityColumn primary = null; + T one = info.constructorAttributes == null ? info.getCreator().create() : null; + List comments = new ArrayList<>(); + if (table != null && !table.comment().isEmpty()) { + comments.add("COMMENT ON TABLE " + info.getOriginTable() + " IS '" + table.comment().replace('\'', '"') + "'"); + } + for (EntityColumn column : info.getDDLColumns()) { + if (column.primary) { + primary = column; + } + String sqltype = "VARCHAR(" + column.length + ")"; + String sqlnull = column.primary ? "NOT NULL" : "NULL"; + if (column.type == boolean.class || column.type == Boolean.class) { + sqltype = "BOOL"; + Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); + } else if (column.type == byte.class || column.type == Byte.class) { + sqltype = "INT2"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); + } else if (column.type == short.class || column.type == Short.class) { + sqltype = "INT2"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == char.class || column.type == Character.class) { + sqltype = "INT2 UNSIGNED"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); + } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { + sqltype = "INT4"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { + sqltype = "INT8"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == float.class || column.type == Float.class) { + sqltype = "FLOAT4"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == double.class || column.type == Double.class) { + sqltype = "FLOAT8"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigInteger.class) { + sqltype = "NUMERIC"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } else { + sqltype += "(19,2)"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigDecimal.class) { + sqltype = "NUMERIC"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == String.class) { + if (column.length < 65535) { + String val = one == null ? null : (String) info.getAttribute(column.field).get(one); + if (val != null) { + sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; + } else if (column.primary) { + sqlnull = "NOT NULL DEFAULT ''"; + } + } else { + sqltype = "TEXT"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } + } else if (column.type == byte[].class) { + sqltype = "BYTEA"; + if (!column.nullable) { + sqlnull = "NOT NULL"; + } + } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { + sqltype = "DATE"; + } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { + sqltype = "TIME"; + } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { + sqltype = "TIMESTAMP"; + } else { //JavaBean + sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); + sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; + } + sb.append(" ").append(column.column).append(" ").append(sqltype).append(" ").append(sqlnull); + if (column.comment != null && !column.comment.isEmpty()) { + //postgresql不支持DDL中直接带comment + comments.add("COMMENT ON COLUMN " + info.getOriginTable() + "." + column.column + " IS '" + column.comment.replace('\'', '"') + "'"); + } + sb.append(",\n"); + } + sb.append(" PRIMARY KEY (").append(primary.column).append(")\n"); + sb.append(")"); + return Utility.append(Utility.ofArray(sb.toString()), comments); + } + return null; + } + + @Local + protected boolean isTableNotExist(EntityInfo info, String sqlCode) { + return sqlCode != null && !sqlCode.isEmpty() && tableNotExistSqlstates.contains(';' + sqlCode + ';'); + } + + @Local + protected String getTableCopySQL(EntityInfo info, String newTable) { + return tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", info.table); + } + + @Local + protected Serializable getSQLAttrValue(EntityInfo info, Attribute attr, Serializable val) { + if (val != null && !(val instanceof Number) && !(val instanceof CharSequence) && !(val instanceof java.util.Date) + && !val.getClass().getName().startsWith("java.sql.") && !val.getClass().getName().startsWith("java.time.")) { + val = info.jsonConvert.convertTo(attr.genericType(), val); + } else if (val == null && info.isNotNullJson(attr)) { + val = ""; + } + return val; + } + + @Local + protected Map> getInsertQuestionPrepareInfo(EntityInfo info, T... entitys) { + Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap + for (T entity : entitys) { + String table = info.getTable(entity); + map.computeIfAbsent(table, t -> new PrepareInfo(info.getInsertQuestionPrepareSQL(entity))).addEntity(entity); + } + return map; + } + + @Local + protected Map> getInsertDollarPrepareInfo(EntityInfo info, T... entitys) { + Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap + for (T entity : entitys) { + String table = info.getTable(entity); + map.computeIfAbsent(table, t -> new PrepareInfo(info.getInsertDollarPrepareSQL(entity))).addEntity(entity); + } + return map; + } + + @Local + protected Map> getUpdateQuestionPrepareInfo(EntityInfo info, T... entitys) { + Map> map = new LinkedHashMap<>(); //一定要是LinkedHashMap + for (T entity : entitys) { + String table = info.getTable(entity); + map.computeIfAbsent(table, t -> new PrepareInfo(info.getUpdateQuestionPrepareSQL(entity))).addEntity(entity); + } + return map; + } + + @Local + protected Map> getUpdateDollarPrepareInfo(EntityInfo info, T... entitys) { + Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap + for (T entity : entitys) { + String table = info.getTable(entity); + map.computeIfAbsent(table, t -> new PrepareInfo(info.getUpdateDollarPrepareSQL(entity))).addEntity(entity); + } + return map; + } + + @Local + protected Serializable getEntityAttrValue(EntityInfo info, Attribute attr, T entity) { + Serializable val = info.getSQLValue(attr, entity); + Class clazz = attr.type(); + if (clazz == String.class + || clazz == int.class || clazz == long.class + || clazz == Integer.class || clazz == Long.class + || clazz == short.class || clazz == Short.class + || clazz == float.class || clazz == Float.class + || clazz == double.class || clazz == Double.class) { + return val; + } + return getSQLAttrValue(info, attr, val); + } + + @Override + public void destroy(AnyValue config) { + super.destroy(config); + } + + @Override + @Local + public void compile(Class clazz) { + EntityInfo.compile(clazz, this); + } + + @Local + public final String dbtype() { + if (dbtype == null) { + throw new NullPointerException("dbtype is null"); + } + return dbtype; + } + + @Local + public final boolean autoddl() { + return autoDDL; + } + + @Local + public abstract int directExecute(String sql); + + @Local + public abstract int[] directExecute(String... sqls); + + @Local + public abstract V directQuery(String sql, Function handler); + + //是否异步 + protected abstract boolean isAsync(); + + //index从1开始 + protected abstract String prepareParamSign(int index); + + //插入纪录 + protected abstract CompletableFuture insertDBAsync(final EntityInfo info, T... entitys); + + //删除记录 + protected abstract CompletableFuture deleteDBAsync(final EntityInfo info, String[] tables, Flipper flipper, FilterNode node, Map> pkmap, final String... sqls); + + //清空表 + protected abstract CompletableFuture clearTableDBAsync(final EntityInfo info, String[] tables, FilterNode node, final String... sqls); + + //建表 + protected abstract CompletableFuture createTableDBAsync(final EntityInfo info, String copyTableSql, Serializable pk, final String... sqls); + + //删除表 + protected abstract CompletableFuture dropTableDBAsync(final EntityInfo info, String[] tables, FilterNode node, final String... sqls); + + //更新纪录 + protected abstract CompletableFuture updateEntityDBAsync(final EntityInfo info, T... entitys); + + //更新纪录 + protected abstract CompletableFuture updateColumnDBAsync(final EntityInfo info, Flipper flipper, final UpdateSqlInfo sql); + + //查询Number Map数据 + protected abstract CompletableFuture> getNumberMapDBAsync(final EntityInfo info, String[] tables, final String sql, final FilterNode node, final FilterFuncColumn... columns); + + //查询Number数据 + protected abstract CompletableFuture getNumberResultDBAsync(final EntityInfo info, String[] tables, final String sql, final FilterFunc func, final Number defVal, final String column, final FilterNode node); + + //查询Map数据 + protected abstract CompletableFuture> queryColumnMapDBAsync(final EntityInfo info, String[] tables, final String sql, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node); + + //查询Map数据 + protected abstract CompletableFuture> queryColumnMapDBAsync(final EntityInfo info, String[] tables, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); + + //查询单条记录 + protected abstract CompletableFuture findDBAsync(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final SelectColumn selects, final Serializable pk, final FilterNode node); + + //查询单条记录的单个字段 + protected abstract CompletableFuture findColumnDBAsync(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final String column, final Serializable defValue, final Serializable pk, final FilterNode node); + + //判断记录是否存在 + protected abstract CompletableFuture existsDBAsync(final EntityInfo info, final String[] tables, final String sql, final boolean onlypk, final Serializable pk, final FilterNode node); + + //查询一页数据 + protected abstract CompletableFuture> querySheetDBAsync(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + //插入纪录 + protected int insertDB(final EntityInfo info, T... entitys) { + return insertDBAsync(info, entitys).join(); + } + + //删除记录 + protected int deleteDB(final EntityInfo info, String[] tables, Flipper flipper, FilterNode node, Map> pkmap, final String... sqls) { + return deleteDBAsync(info, tables, flipper, node, pkmap, sqls).join(); + } + + //清空表 + protected int clearTableDB(final EntityInfo info, String[] tables, FilterNode node, final String... sqls) { + return clearTableDBAsync(info, tables, node, sqls).join(); + } + + //建表 + protected int createTableDB(final EntityInfo info, String copyTableSql, Serializable pk, final String... sqls) { + return createTableDBAsync(info, copyTableSql, pk, sqls).join(); + } + + //删除表 + protected int dropTableDB(final EntityInfo info, String[] tables, FilterNode node, final String... sqls) { + return dropTableDBAsync(info, tables, node, sqls).join(); + } + + //更新纪录 + protected int updateEntityDB(final EntityInfo info, T... entitys) { + return updateEntityDBAsync(info, entitys).join(); + } + + //更新纪录 + protected int updateColumnDB(final EntityInfo info, Flipper flipper, final UpdateSqlInfo sql) { + return updateColumnDBAsync(info, flipper, sql).join(); + } + + //查询Number Map数据 + protected Map getNumberMapDB(final EntityInfo info, String[] tables, final String sql, final FilterNode node, final FilterFuncColumn... columns) { + return (Map) getNumberMapDBAsync(info, tables, sql, node, columns).join(); + } + + //查询Number数据 + protected Number getNumberResultDB(final EntityInfo info, String[] tables, final String sql, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node).join(); + } + + //查询Map数据 + protected Map queryColumnMapDB(final EntityInfo info, String[] tables, final String sql, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + return (Map) queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node).join(); + } + + //查询Map数据 + protected Map queryColumnMapDB(final EntityInfo info, String[] tables, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + return (Map) queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node).join(); + } + + //查询单条记录 + protected T findDB(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final SelectColumn selects, final Serializable pk, final FilterNode node) { + return findDBAsync(info, tables, sql, onlypk, selects, pk, node).join(); + } + + //查询单条记录的单个字段 + protected Serializable findColumnDB(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final String column, final Serializable defValue, final Serializable pk, final FilterNode node) { + return findColumnDBAsync(info, tables, sql, onlypk, column, defValue, pk, node).join(); + } + + //判断记录是否存在 + protected boolean existsDB(final EntityInfo info, final String[] tables, final String sql, final boolean onlypk, final Serializable pk, final FilterNode node) { + return existsDBAsync(info, tables, sql, onlypk, pk, node).join(); + } + + //查询一页数据 + protected Sheet querySheetDB(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheetDBAsync(info, readcache, needtotal, distinct, selects, flipper, node).join(); + } + + protected CharSequence createSQLJoin(FilterNode node, final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { + return node == null ? null : node.createSQLJoin(func, update, joinTabalis, haset, info); + } + + protected CharSequence createSQLExpress(FilterNode node, final EntityInfo info, final Map joinTabalis) { + return node == null ? null : node.createSQLExpress(this, info, joinTabalis); + } + + @Local + @Override + public String getType() { + return "sql"; + } + + @Override + public final String resourceName() { + return name; + } + + @Local + @Override + public EntityInfo apply(Class t) { + return loadEntityInfo(t); + } + + @Local + @Override + public void close() throws Exception { + } + + protected EntityInfo loadEntityInfo(Class clazz) { + EntityInfo info = this.currEntityInfo; + if (info != null && info.getType() == clazz) { + return info; + } + info = loadEntityInfo(clazz, this.cacheForbidden, readConfProps, fullloader); + this.currEntityInfo = info; + return info; + } + + public EntityCache loadCache(Class clazz) { + EntityInfo info = loadEntityInfo(clazz); + return info.getCache(); + } + + /** + * 将entity的对象全部加载到Cache中去,如果clazz没有被@org.redkale.persistence.Cacheable注解则不做任何事 + * + * @param Entity类泛型 + * @param clazz Entity类 + */ + public void refreshCache(Class clazz) { + EntityInfo info = loadEntityInfo(clazz); + EntityCache cache = info.getCache(); + if (cache == null) { + return; + } + cache.fullLoadAsync(); + } + + protected CharSequence formatValueToString(final EntityInfo info, Object value) { + if (value == null) { + return null; + } + if ("mysql".equals(dbtype)) { + if (value instanceof CharSequence) { + return new StringBuilder().append('\'').append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } else if (!(value instanceof Number) && !(value instanceof java.util.Date) + && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { + return new StringBuilder().append('\'').append(info.getJsonConvert().convertTo(value).replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } + return String.valueOf(value); + } else if (value != null && value instanceof CharSequence && "postgresql".equals(dbtype)) { + String s = String.valueOf(value); + int pos = s.indexOf('\''); + if (pos >= 0) { + return new StringBuilder().append("E'").append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } + } + return info.formatSQLValue(value, null); + } + + //----------------------------- insert ----------------------------- + /** + * 新增对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + @Override + public int insert(T... entitys) { + if (entitys.length == 0) { + return 0; + } + checkEntity("insert", entitys); + final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); + if (isOnlyCache(info)) { + return insertCache(info, entitys); + } + if (isAsync()) { + int rs = insertDBAsync(info, entitys).join(); + insertCache(info, entitys); + return rs; + } else { + int rs = insertDB(info, entitys); + insertCache(info, entitys); + return rs; + } + } + + @Override + public CompletableFuture insertAsync(T... entitys) { + if (entitys.length == 0) { + return CompletableFuture.completedFuture(0); + } + checkEntity("insert", entitys); + final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(insertCache(info, entitys)); + } + if (isAsync()) { + return insertDBAsync(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + insertCache(info, entitys); + } + }); + } else { + return supplyAsync(() -> insertDB(info, entitys)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + insertCache(info, entitys); + } + }); + } + } + + protected int insertCache(final EntityInfo info, T... entitys) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return 0; + } + int c = 0; + for (final T value : entitys) { + c += cache.insert(value); + } + return c; + } + + //----------------------------- deleteCompose ----------------------------- + /** + * 删除对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 删除的数据条数 + */ + @Override + public int delete(T... entitys) { + if (entitys.length == 0) { + return -1; + } + checkEntity("delete", entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Serializable[] ids = new Serializable[entitys.length]; + int i = 0; + for (final T value : entitys) { + ids[i++] = (Serializable) primary.get(value); + } + return delete(clazz, ids); + } + + @Override + public CompletableFuture deleteAsync(final T... entitys) { + if (entitys.length == 0) { + return CompletableFuture.completedFuture(-1); + } + checkEntity("delete", entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Serializable[] ids = new Serializable[entitys.length]; + int i = 0; + for (final T value : entitys) { + ids[i++] = (Serializable) primary.get(value); + } + return deleteAsync(clazz, ids); + } + + @Override + public int delete(Class clazz, Serializable... pks) { + if (pks.length == 0) { + return -1; + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return deleteCache(info, -1, pks); + } + Map> pkmap = info.getTableMap(pks); + String[] tables = pkmap.keySet().toArray(new String[pkmap.size()]); + String[] sqls = deleteSql(info, pkmap); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + int rs = deleteDBAsync(info, tables, null, null, pkmap, sqls).join(); + deleteCache(info, rs, pks); + return rs; + } else { + int rs = deleteDB(info, tables, null, null, pkmap, sqls); + deleteCache(info, rs, pks); + return rs; + } + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks) { + if (pks.length == 0) { + return CompletableFuture.completedFuture(-1); + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(deleteCache(info, -1, pks)); + } + Map> pkmap = info.getTableMap(pks); + String[] tables = pkmap.keySet().toArray(new String[pkmap.size()]); + String[] sqls = deleteSql(info, pkmap); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + return deleteDBAsync(info, tables, null, null, pkmap, sqls).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, pks); + } + }); + } else { + return supplyAsync(() -> deleteDB(info, tables, null, null, pkmap, sqls)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, pks); + } + }); + } + } + + @Override + public int delete(Class clazz, FilterNode node) { + return delete(clazz, (Flipper) null, node); + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { + return deleteAsync(clazz, (Flipper) null, node); + } + + @Override + public int delete(Class clazz, final Flipper flipper, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return deleteCache(info, -1, flipper, node); + } + String[] tables = info.getTables(node); + String[] sqls = deleteSql(info, tables, flipper, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + int rs = deleteDBAsync(info, tables, flipper, node, null, sqls).join(); + deleteCache(info, rs, flipper, sqls); + return rs; + } else { + int rs = deleteDB(info, tables, flipper, node, null, sqls); + deleteCache(info, rs, flipper, sqls); + return rs; + } + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(deleteCache(info, -1, flipper, node)); + } + String[] tables = info.getTables(node); + String[] sqls = deleteSql(info, tables, flipper, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + return deleteDBAsync(info, tables, flipper, node, null, sqls).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, flipper, node); + } + }); + } else { + return supplyAsync(() -> deleteDB(info, tables, flipper, node, null, sqls)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, flipper, node); + } + }); + } + } + + protected String[] deleteSql(final EntityInfo info, final Map> pkmap) { + List sqls = new ArrayList<>(); + final String pkSQLColumn = info.getPrimarySQLColumn(); + pkmap.forEach((table, pks) -> { + String sql; + if (pks.size() == 1) { + sql = "DELETE FROM " + table + " WHERE " + pkSQLColumn + " = " + info.formatSQLValue(pkSQLColumn, pks.get(0), sqlFormatter); + } else { + sql = "DELETE FROM " + table + " WHERE " + pkSQLColumn + " IN ("; + for (int i = 0; i < pks.size(); i++) { + if (i > 0) { + sql += ','; + } + sql += info.formatSQLValue(pkSQLColumn, pks.get(i), sqlFormatter); + } + sql += ")"; + } + sqls.add(sql); + }); + return sqls.toArray(new String[sqls.size()]); + } + + protected String[] deleteSql(final EntityInfo info, String[] tables, final Flipper flipper, final FilterNode node) { + Map joinTabalis = null; + CharSequence join = null; + CharSequence where = null; + if (node != null) { + joinTabalis = node.getJoinTabalis(); + join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + where = node.createSQLExpress(this, info, joinTabalis); + } + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + final String join2AndWhere = ((where == null || where.length() == 0) ? (join2 == null ? "" + : (" WHERE " + join2)) : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + + if ("postgresql".equals(dbtype()) && flipper != null && flipper.getLimit() > 0) { + List sqls = new ArrayList<>(); + for (String table : tables) { + String sql = "DELETE FROM " + table + " a" + (join1 == null ? "" : (", " + join1)) + " WHERE " + info.getPrimarySQLColumn() + " IN (SELECT " + info.getPrimaryField() + " FROM " + table + + join2AndWhere + info.createSQLOrderby(flipper) + " OFFSET 0 LIMIT " + flipper.getLimit() + ")"; + sqls.add(sql); + } + return sqls.toArray(new String[sqls.size()]); + } else { + boolean mysql = "mysql".equals(dbtype()); + List sqls = new ArrayList<>(); + for (String table : tables) { + String sql = "DELETE " + (mysql ? "a" : "") + " FROM " + table + " a" + (join1 == null ? "" : (", " + join1)) + join2AndWhere + info.createSQLOrderby(flipper) + + ((mysql && flipper != null && flipper.getLimit() > 0) ? (" LIMIT " + flipper.getLimit()) : ""); + sqls.add(sql); + } + return sqls.toArray(new String[sqls.size()]); + } + } + + //----------------------------- clearTableCompose ----------------------------- + @Override + public int clearTable(Class clazz) { + return clearTable(clazz, (FilterNode) null); + } + + @Override + public int clearTable(Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return clearTableCache(info, node); + } + final String[] tables = info.getTables(node); + String[] sqls = clearTableSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " clearTable sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + int rs = clearTableDBAsync(info, tables, node, sqls).join(); + clearTableCache(info, node); + return rs; + } else { + int rs = clearTableDB(info, tables, node, sqls); + clearTableCache(info, node); + return rs; + } + } + + @Override + public CompletableFuture clearTableAsync(final Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(clearTableCache(info, node)); + } + final String[] tables = info.getTables(node); + String[] sqls = clearTableSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " clearTable sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + return clearTableDBAsync(info, tables, node, sqls).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + clearTableCache(info, node); + } + }); + } else { + return supplyAsync(() -> clearTableDB(info, tables, node, sqls)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + clearTableCache(info, node); + } + }); + } + } + + protected String[] clearTableSql(final EntityInfo info, String[] tables, final FilterNode node) { + List sqls = new ArrayList<>(); + for (String table : tables) { + sqls.add("TRUNCATE TABLE " + table); + } + return sqls.toArray(new String[sqls.size()]); + } + + //----------------------------- dropTable ----------------------------- + @Override + public int createTable(final Class clazz, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final String[] sqls = createTableSqls(info); + if (sqls == null) { + return -1; + } + String copyTableSql = info.getTableStrategy() == null ? null : getTableCopySQL(info, info.getTable(pk)); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " createTable sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + int rs = createTableDBAsync(info, copyTableSql, pk, sqls).join(); + return rs; + } else { + int rs = createTableDB(info, copyTableSql, pk, sqls); + return rs; + } + } + + @Override + public CompletableFuture createTableAsync(final Class clazz, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final String[] sqls = createTableSqls(info); + if (sqls == null) { + return CompletableFuture.completedFuture(-1); + } + String copyTableSql = info.getTableStrategy() == null ? null : getTableCopySQL(info, info.getTable(pk)); + if (copyTableSql == null) { + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " createTable sql=" + Arrays.toString(sqls)); + } + } else { + if (info.isLoggable(logger, Level.FINEST, copyTableSql)) { + logger.finest(info.getType().getSimpleName() + " createTable sql=" + copyTableSql); + } + } + if (isAsync()) { + return createTableDBAsync(info, copyTableSql, pk, sqls).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } + }); + } else { + return supplyAsync(() -> createTableDB(info, copyTableSql, pk, sqls)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } + }); + } + } + + //----------------------------- dropTable ----------------------------- + @Override + public int dropTable(Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return dropTableCache(info, node); + } + final String[] tables = info.getTables(node); + String[] sqls = dropTableSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " dropTable sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + int rs = dropTableDBAsync(info, tables, node, sqls).join(); + dropTableCache(info, node); + return rs; + } else { + int rs = dropTableDB(info, tables, node, sqls); + dropTableCache(info, node); + return rs; + } + } + + @Override + public CompletableFuture dropTableAsync(final Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(dropTableCache(info, node)); + } + final String[] tables = info.getTables(node); + String[] sqls = dropTableSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sqls[0])) { + logger.finest(info.getType().getSimpleName() + " dropTable sql=" + Arrays.toString(sqls)); + } + if (isAsync()) { + return dropTableDBAsync(info, tables, node, sqls).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + dropTableCache(info, node); + } + }); + } else { + return supplyAsync(() -> dropTableDB(info, tables, node, sqls)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + dropTableCache(info, node); + } + }); + } + } + + protected String[] dropTableSql(final EntityInfo info, String[] tables, final FilterNode node) { + List sqls = new ArrayList<>(); + for (String table : tables) { + sqls.add("DROP TABLE IF EXISTS " + table); + } + return sqls.toArray(new String[sqls.size()]); + } + + protected int clearTableCache(final EntityInfo info, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + return cache.clear(); + } + + protected int dropTableCache(final EntityInfo info, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + return cache.drop(); + } + + protected int deleteCache(final EntityInfo info, int count, Flipper flipper, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + Serializable[] ids = cache.delete(flipper, node); + return count >= 0 ? count : (ids == null ? 0 : ids.length); + } + + protected int deleteCache(final EntityInfo info, int count, Serializable... pks) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + int c = 0; + for (Serializable key : pks) { + c += cache.delete(key); + } + return count >= 0 ? count : c; + } + + protected static StringBuilder multiSplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) { + if (str == null) { + return sb; + } + int pos1 = str.indexOf(ch1, from); + if (pos1 < 0) { + return sb; + } + int pos2 = str.indexOf(ch2, from); + if (pos2 < 0) { + return sb; + } + if (sb.length() > 0) { + sb.append(split); + } + sb.append(str.substring(pos1 + 1, pos2)); + return multiSplit(ch1, ch2, split, sb, str, pos2 + 1); + } + + //---------------------------- update ---------------------------- + /** + * 更新对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 更新的数据条数 + */ + @Override + public int update(T... entitys) { + if (entitys.length == 0) { + return -1; + } + checkEntity("update", entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return updateCache(info, -1, entitys); + } + if (isAsync()) { + int rs = updateEntityDBAsync(info, entitys).join(); + updateCache(info, rs, entitys); + return rs; + } else { + int rs = updateEntityDB(info, entitys); + updateCache(info, rs, entitys); + return rs; + } + } + + @Override + public CompletableFuture updateAsync(final T... entitys) { + if (entitys.length == 0) { + return CompletableFuture.completedFuture(-1); + } + checkEntity("update", entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, entitys)); + } + if (isAsync()) { + return updateEntityDBAsync(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, entitys); + } + }); + } else { + return supplyAsync(() -> updateEntityDB(info, entitys)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, entitys); + } + }); + } + } + + /** + * 根据主键值更新对象的column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param pk 主键值 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(Class clazz, Serializable pk, String column, Serializable colval) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return updateCache(info, -1, pk, column, colval); + } + + UpdateSqlInfo sql = updateColumnSql(info, pk, column, colval); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, pk, column, colval); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, pk, column, colval); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable colval) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, pk, column, colval)); + } + + UpdateSqlInfo sql = updateColumnSql(info, pk, column, colval); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, column, colval); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, column, colval); + } + }); + } + } + + protected UpdateSqlInfo updateColumnSql(final EntityInfo info, Serializable pk, String column, final Serializable colval) { + Attribute attr = info.getAttribute(column); + Serializable val = getSQLAttrValue(info, attr, colval); + if (val instanceof byte[]) { + return new UpdateSqlInfo(true, "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter), (byte[]) val); + } else { + return new UpdateSqlInfo(false, "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + + info.formatSQLValue(column, val, sqlFormatter) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter)); + } + } + + /** + * 根据主键值更新对象的column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * @param node 过滤node 不能为null + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(Class clazz, String column, Serializable colval, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return updateCache(info, -1, column, colval, node); + } + + UpdateSqlInfo sql = updateColumnSql(info, column, colval, node); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, column, colval, node); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, column, colval, node); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable colval, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, column, colval, node)); + } + UpdateSqlInfo sql = updateColumnSql(info, column, colval, node); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, column, colval, node); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, column, colval, node); + } + }); + } + } + + protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final String column, final Serializable colval, final FilterNode node) { + Map joinTabalis = node.getJoinTabalis(); + CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node.createSQLExpress(this, info, joinTabalis); + + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + Attribute attr = info.getAttribute(column); + Serializable val = getSQLAttrValue(info, attr, colval); + String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + String[] tables = info.getTables(node); + String sql; + if (val instanceof byte[]) { + sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + + " SET " + info.getSQLColumn(alias, column) + "=" + prepareParamSign(1) + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + return new UpdateSqlInfo(true, sql, tables.length == 1 ? null : tables, (byte[]) val); + } else { + sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + + " SET " + info.getSQLColumn(alias, column) + "=" + info.formatSQLValue(val, sqlFormatter) + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + return new UpdateSqlInfo(false, sql, tables.length == 1 ? null : tables); + } + } + + /** + * 根据主键值更新对象的多个column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param pk 主键值 + * @param values 字段值 + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values) { + if (values == null || values.length < 1) { + return -1; + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return updateCache(info, -1, pk, values); + } + + UpdateSqlInfo sql = updateColumnSql(info, pk, values); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, pk, values); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, pk, values); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values) { + if (values == null || values.length < 1) { + return CompletableFuture.completedFuture(-1); + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, pk, values)); + } + UpdateSqlInfo sql = updateColumnSql(info, pk, values); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, values); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, values); + } + }); + } + } + + protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final Serializable pk, final ColumnValue... values) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + for (ColumnValue col : values) { + if (col == null) { + continue; + } + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) { + throw new SourceException(info.getType() + " cannot found column " + col.getColumn()); + } + if (setsql.length() > 0) { + setsql.append(", "); + } + String sqlColumn = info.getSQLColumn(null, col.getColumn()); + if (col.getValue() instanceof byte[]) { + if (blobs == null) { + blobs = new ArrayList<>(); + } + blobs.add((byte[]) col.getValue()); + setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); + } else { + setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); + } + } + if (setsql.length() < 1) { + throw new SourceException("update non column-value array"); + } + String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + return new UpdateSqlInfo(false, sql, blobs); + } + + @Override + public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + if (values == null || values.length < 1) { + return -1; + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return updateCache(info, -1, node, flipper, values); + } + UpdateSqlInfo sql = updateColumnSql(info, node, flipper, values); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, node, flipper, values); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, node, flipper, values); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + if (values == null || values.length < 1) { + return CompletableFuture.completedFuture(-1); + } + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, node, flipper, values)); + } + UpdateSqlInfo sql = updateColumnSql(info, node, flipper, values); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, node, flipper, values); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, node, flipper, values); + } + }); + } + } + + protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + boolean pgsql = "postgresql".equals(dbtype()); + String alias = pgsql ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + for (ColumnValue col : values) { + if (col == null) { + continue; + } + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) { + continue; + } + if (setsql.length() > 0) { + setsql.append(", "); + } + String sqlColumn = info.getSQLColumn(alias, col.getColumn()); + if (col.getValue() instanceof byte[]) { + if (blobs == null) { + blobs = new ArrayList<>(); + } + blobs.add((byte[]) col.getValue()); + setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); + } else { + setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); + } + } + if (setsql.length() < 1) { + throw new SourceException("update non column-value array"); + } + Map joinTabalis = node == null ? null : node.getJoinTabalis(); + CharSequence join = node == null ? null : node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String sql; + String[] tables = info.getTables(node); + if (pgsql && flipper != null && flipper.getLimit() > 0) { + String wherestr = ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql + + " WHERE " + info.getPrimarySQLColumn() + " IN (SELECT " + info.getPrimaryField() + " FROM " + tables[0] + + wherestr + info.createSQLOrderby(flipper) + " OFFSET 0 LIMIT " + flipper.getLimit() + ")"; + + } else { + sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + + info.createSQLOrderby(flipper) + + (("mysql".equals(dbtype()) && flipper != null && flipper.getLimit() > 0) ? (" LIMIT " + flipper.getLimit()) : ""); + } + return new UpdateSqlInfo(blobs != null, sql, tables.length == 1 ? null : tables, blobs); + } + + //返回不存在的字段名,null表示字段都合法; + protected String checkIllegalColumn(final EntityInfo info, SelectColumn selects) { + if (selects == null) { + return null; + } + String[] columns = selects.getColumns(); + if (columns == null) { + return null; + } + for (String col : columns) { + if (info.getAttribute(col) == null) { + return col; + } + } + return null; + } + + @Override + public int updateColumn(final T entity, final SelectColumn selects) { + if (entity == null || selects == null) { + return -1; + } + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + String illegalColumn = checkIllegalColumn(info, selects); + if (illegalColumn != null) { + throw new SourceException(info.getType() + " cannot found column " + illegalColumn); + } + if (isOnlyCache(info)) { + return updateCache(info, -1, false, entity, null, selects); + } + + UpdateSqlInfo sql = updateColumnSql(info, false, entity, null, selects); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, false, entity, null, selects); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, false, entity, null, selects); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { + if (entity == null || selects == null) { + return CompletableFuture.completedFuture(-1); + } + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + String illegalColumn = checkIllegalColumn(info, selects); + if (illegalColumn != null) { + return CompletableFuture.failedFuture(new SourceException(info.getType() + " cannot found column " + illegalColumn)); + } + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, false, entity, null, selects)); + } + + UpdateSqlInfo sql = updateColumnSql(info, false, entity, null, selects); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, false, entity, null, selects); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, false, entity, null, selects); + } + }); + } + } + + @Override + public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects) { + if (entity == null || node == null || selects == null) { + return -1; + } + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + String illegalColumn = checkIllegalColumn(info, selects); + if (illegalColumn != null) { + throw new SourceException(info.getType() + " cannot found column " + illegalColumn); + } + if (isOnlyCache(info)) { + return updateCache(info, -1, true, entity, node, selects); + } + + UpdateSqlInfo sql = updateColumnSql(info, true, entity, node, selects); + if (isAsync()) { + int rs = updateColumnDBAsync(info, null, sql).join(); + updateCache(info, rs, true, entity, node, selects); + return rs; + } else { + int rs = updateColumnDB(info, null, sql); + updateCache(info, rs, true, entity, node, selects); + return rs; + } + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects) { + if (entity == null || node == null || selects == null) { + return CompletableFuture.completedFuture(-1); + } + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + String illegalColumn = checkIllegalColumn(info, selects); + if (illegalColumn != null) { + return CompletableFuture.failedFuture(new SourceException(info.getType() + " cannot found column " + illegalColumn)); + } + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, true, entity, node, selects)); + } + + UpdateSqlInfo sql = updateColumnSql(info, true, entity, node, selects); + if (isAsync()) { + return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, true, entity, node, selects); + } + }); + } else { + return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { + if (t != null) { + errorCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, true, entity, node, selects); + } + }); + } + } + + protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final boolean needNode, final T entity, final FilterNode node, final SelectColumn selects) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + for (Attribute attr : info.updateAttributes) { + if (!selects.test(attr.field())) { + continue; + } + if (setsql.length() > 0) { + setsql.append(", "); + } + setsql.append(info.getSQLColumn(alias, attr.field())); + Serializable val = info.getFieldValue(attr, entity); + if (val instanceof byte[]) { + if (blobs == null) { + blobs = new ArrayList<>(); + } + blobs.add((byte[]) val); + setsql.append("=").append(prepareParamSign(++index)); + } else { + CharSequence sqlval = info.formatSQLValue(val, sqlFormatter); + if (sqlval == null && info.isNotNullJson(attr)) { + sqlval = "''"; + } + setsql.append("=").append(sqlval); + } + } + if (needNode) { + Map joinTabalis = node.getJoinTabalis(); + CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node.createSQLExpress(this, info, joinTabalis); + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String sql; + String[] tables = info.getTables(node); + sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + return new UpdateSqlInfo(blobs != null, sql, tables.length == 1 ? null : tables, blobs); + } else { + final Serializable id = (Serializable) info.getSQLValue(info.getPrimary(), entity); + String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(id, sqlFormatter); + return new UpdateSqlInfo(blobs != null, sql, blobs); + } + } + + protected int updateCache(final EntityInfo info, int count, final boolean needNode, final T entity, final FilterNode node, final SelectColumn selects) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return count; + } + final List> attrs = new ArrayList<>(); + for (Attribute attr : info.updateAttributes) { + if (!selects.test(attr.field())) { + continue; + } + attrs.add(attr); + } + if (needNode) { + T[] rs = cache.update(entity, attrs, node); + return count >= 0 ? count : (rs == null ? 0 : rs.length); + } else { + T rs = cache.update(entity, attrs); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + } + + protected int updateCache(final EntityInfo info, int count, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return count; + } + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + for (ColumnValue col : values) { + if (col == null) { + continue; + } + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) { + continue; + } + attrs.add(attr); + cols.add(col); + } + T[] rs = cache.updateColumn(node, flipper, attrs, cols); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, final Serializable pk, final ColumnValue... values) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return count; + } + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + for (ColumnValue col : values) { + if (col == null) { + continue; + } + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) { + continue; + } + attrs.add(attr); + cols.add(col); + } + T rs = cache.updateColumn(pk, attrs, cols); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, String column, final Serializable colval, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return count; + } + T[] rs = cache.update(info.getAttribute(column), colval, node); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, final Serializable pk, final String column, final Serializable colval) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return count; + } + T rs = cache.update(pk, info.getAttribute(column), colval); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, T... entitys) { + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + int c2 = 0; + for (final T value : entitys) { + c2 += cache.update(value); + } + return count >= 0 ? count : c2; + } + + public int reloadCache(Class clazz, Serializable... pks) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache == null) { + return -1; + } + String column = info.getPrimary().field(); + int c = 0; + for (Serializable id : pks) { + Sheet sheet = querySheet(false, true, false, clazz, null, FLIPPER_ONE, FilterNode.create(column, id)); + T value = sheet.isEmpty() ? null : sheet.list().get(0); + if (value != null) { + c += cache.update(value); + } + } + return c; + } + + //------------------------- getNumberMapCompose ------------------------- + @Override + public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + final Map map = new HashMap<>(); + if (node == null || isCacheUseable(node, this)) { + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node)); + } + } + return map; + } + } + final String[] tables = info.getTables(node); + String sql = getNumberMapSql(info, tables, node, columns); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " getNumberMap sql=" + sql); + } + if (isAsync()) { + return (Map) getNumberMapDBAsync(info, tables, sql, node, columns).join(); + } else { + return getNumberMapDB(info, tables, sql, node, columns); + } + } + + @Override + public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + final Map map = new HashMap<>(); + if (node == null || isCacheUseable(node, this)) { + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + map.put(ffc.col(col), cache.getNumberResult(ffc.getFunc(), ffc.getDefvalue(), col, node)); + } + } + return CompletableFuture.completedFuture(map); + } + } + final String[] tables = info.getTables(node); + String sql = getNumberMapSql(info, tables, node, columns); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " getNumberMap sql=" + sql); + } + if (isAsync()) { + return getNumberMapDBAsync(info, tables, sql, node, columns); + } else { + return supplyAsync(() -> getNumberMapDB(info, tables, sql, node, columns)); + } + } + + protected String getNumberMapSql(final EntityInfo info, final String[] tables, final FilterNode node, final FilterFuncColumn... columns) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + StringBuilder sb = new StringBuilder(); + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col)))); + } + } + final String sql = "SELECT " + sb + " FROM " + tables[0] + " a" + + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + return sql; + } + + @Local + protected CompletableFuture> getNumberMapDBApply(EntityInfo info, CompletableFuture future, FilterFuncColumn... columns) { + return future.thenApply((DataResultSet dataset) -> { + final Map map = new HashMap<>(); + if (dataset.next()) { + int index = 0; + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + Object o = dataset.getObject(++index); + Number rs = ffc.getDefvalue(); + if (o != null) { + rs = (Number) o; + } + map.put(ffc.col(col), rs); + } + } + } + dataset.close(); + return map; + }); + } + + //------------------------ getNumberResultCompose ----------------------- + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.getNumberResult(func, defVal, column, node); + } + } + + final String[] tables = info.getTables(node); + String sql = getNumberResultSql(info, entityClass, tables, func, defVal, column, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " getNumberResult sql=" + sql); + } + if (isAsync()) { + return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node).join(); + } else { + return getNumberResultDB(info, tables, sql, func, defVal, column, node); + } + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.getNumberResult(func, defVal, column, node)); + } + } + final String[] tables = info.getTables(node); + String sql = getNumberResultSql(info, entityClass, tables, func, defVal, column, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " getNumberResult sql=" + sql); + } + if (isAsync()) { + return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node); + } else { + return supplyAsync(() -> getNumberResultDB(info, tables, sql, func, defVal, column, node)); + } + } + + protected String getNumberResultSql(final EntityInfo info, final Class entityClass, final String[] tables, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + tables[0] + " a" + + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + return sql; + } + + @Local + protected CompletableFuture getNumberResultDBApply(EntityInfo info, CompletableFuture future, Number defVal, String column) { + return future.thenApply((DataResultSet dataset) -> { + Number rs = defVal; + if (dataset.next()) { + Object o = dataset.getObject(1); + if (o != null) { + rs = (Number) o; + } + } + dataset.close(); + return rs; + }); + } + + //------------------------ queryColumnMapCompose ------------------------ + @Override + public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.queryColumnMap(keyColumn, func, funcColumn, node); + } + } + + final String[] tables = info.getTables(node); + String sql = queryColumnMapSql(info, tables, keyColumn, func, funcColumn, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); + } + if (isAsync()) { + return (Map) queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node).join(); + } else { + return queryColumnMapDB(info, tables, sql, keyColumn, func, funcColumn, node); + } + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.queryColumnMap(keyColumn, func, funcColumn, node)); + } + } + final String[] tables = info.getTables(node); + String sql = queryColumnMapSql(info, tables, keyColumn, func, funcColumn, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); + } + if (isAsync()) { + return queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node); + } else { + return supplyAsync(() -> queryColumnMapDB(info, tables, sql, keyColumn, func, funcColumn, node)); + } + } + + protected String queryColumnMapSql(final EntityInfo info, final String[] tables, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final String keySqlColumn = info.getSQLColumn(null, keyColumn); + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String funcSqlColumn = func == null ? info.getSQLColumn("a", funcColumn) : func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn))); + + String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + String sql; + if (tables.length == 1) { + sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn + " FROM " + tables[0] + " a" + joinAndWhere; + } else { + int b = 0; + StringBuilder union = new StringBuilder(); + for (String table : tables) { + if (union.length() > 0) { + union.append(" UNION ALL "); + } + union.append("SELECT a.").append(keySqlColumn).append(", ").append(funcSqlColumn) + .append(" FROM ").append(table).append(" a").append(joinAndWhere); + } + sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn + " FROM (" + (union) + ") a"; + } + return sql; + } + + @Local + protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final String keyColumn) { + return future.thenApply((DataResultSet dataset) -> { + Map rs = new LinkedHashMap<>(); + while (dataset.next()) { + rs.put((K) dataset.getObject(1), (N) dataset.getObject(2)); + } + dataset.close(); + return rs; + }); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { + Map map = queryColumnMap(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); + final Map rs = new LinkedHashMap<>(); + map.forEach((keys, values) -> rs.put(keys[0], values)); + return rs; + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { + CompletableFuture> future = queryColumnMapAsync(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); + return future.thenApply(map -> { + final Map rs = new LinkedHashMap<>(); + map.forEach((keys, values) -> rs.put(keys[0], values)); + return rs; + }); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.queryColumnMap(funcNodes, groupByColumns, node); + } + } + final String[] tables = info.getTables(node); + String sql = queryColumnMapSql(info, tables, funcNodes, groupByColumns, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); + } + if (isAsync()) { + return (Map) queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node).join(); + } else { + return queryColumnMapDB(info, tables, sql, funcNodes, groupByColumns, node); + } + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.queryColumnMap(funcNodes, groupByColumns, node)); + } + } + final String[] tables = info.getTables(node); + String sql = queryColumnMapSql(info, tables, funcNodes, groupByColumns, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); + } + if (isAsync()) { + return queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node); + } else { + return supplyAsync(() -> queryColumnMapDB(info, tables, sql, funcNodes, groupByColumns, node)); + } + } + + protected String queryColumnMapSql(final EntityInfo info, final String[] tables, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final StringBuilder groupBySqlColumns = new StringBuilder(); + if (groupByColumns != null && groupByColumns.length > 0) { + for (int i = 0; i < groupByColumns.length; i++) { + if (groupBySqlColumns.length() > 0) { + groupBySqlColumns.append(", "); + } + groupBySqlColumns.append(info.getSQLColumn("a", groupByColumns[i])); + } + } + final StringBuilder funcSqlColumns = new StringBuilder(); + for (int i = 0; i < funcNodes.length; i++) { + if (funcSqlColumns.length() > 0) { + funcSqlColumns.append(", "); + } + if (funcNodes[i] instanceof ColumnFuncNode) { + funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnFuncNode) funcNodes[i], sqlFormatter)); + } else { + funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnNodeValue) funcNodes[i], sqlFormatter)); + } + } + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + + String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + String sql; + if (tables.length == 1) { + sql = "SELECT "; + if (groupBySqlColumns.length() > 0) { + sql += groupBySqlColumns + ", "; + } + sql += funcSqlColumns + " FROM " + tables[0] + " a" + joinAndWhere; + } else { + StringBuilder union = new StringBuilder(); + for (String table : tables) { + if (union.length() > 0) { + union.append(" UNION ALL "); + } + String subsql = "SELECT "; + if (groupBySqlColumns.length() > 0) { + subsql += groupBySqlColumns.toString() + ", "; + } + subsql += funcSqlColumns.toString() + " FROM " + table + " a" + joinAndWhere; + union.append(subsql); + } + sql = "SELECT "; + if (groupBySqlColumns.length() > 0) { + sql += groupBySqlColumns + ", "; + } + sql += funcSqlColumns + " FROM (" + (union) + ") a"; + } + if (groupBySqlColumns.length() > 0) { + sql += " GROUP BY " + groupBySqlColumns; + } + return sql; + } + + @Local + protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return future.thenApply((DataResultSet dataset) -> { + Map rs = new LinkedHashMap<>(); + while (dataset.next()) { + int index = 0; + Serializable[] keys = new Serializable[groupByColumns.length]; + for (int i = 0; i < keys.length; i++) { + keys[i] = (Serializable) dataset.getObject(++index); + } + Number[] vals = new Number[funcNodes.length]; + for (int i = 0; i < vals.length; i++) { + vals[i] = (Number) dataset.getObject(++index); + } + rs.put(keys, vals); + } + dataset.close(); + return rs; + }); + } + + //----------------------------- find ----------------------------- + @Override + public T[] finds(Class clazz, final SelectColumn selects, Serializable... pks) { + return findsAsync(clazz, selects, pks).join(); + } + + @Override + public CompletableFuture findsAsync(Class clazz, final SelectColumn selects, Serializable... pks) { + final EntityInfo info = loadEntityInfo(clazz); + if (pks == null || pks.length == 0) { + return CompletableFuture.completedFuture(info.getArrayer().apply(0)); + } + final EntityCache cache = info.getCache(); + if (cache != null) { + T[] rs = selects == null ? cache.finds(pks) : cache.finds(selects, pks); + if (cache.isFullLoaded() || rs != null) { + return CompletableFuture.completedFuture(rs); + } + } + return findsDBAsync(info, selects, pks); + } + + protected CompletableFuture findsDBAsync(final EntityInfo info, final SelectColumn selects, Serializable... pks) { + final Attribute primary = info.getPrimary(); + return queryListAsync(info.getType(), selects, null, FilterNode.create(info.getPrimarySQLColumn(), FilterExpress.IN, pks)).thenApply(list -> { + T[] rs = info.getArrayer().apply(pks.length); + for (int i = 0; i < rs.length; i++) { + T t = null; + Serializable pk = pks[i]; + for (T item : list) { + if (pk.equals(primary.get(item))) { + t = item; + break; + } + } + rs[i] = t; + } + return rs; + }); + } + + @Override + public List findsList(Class clazz, Stream pks) { + return findsListAsync(clazz, pks).join(); + } + + @Override + public CompletableFuture> findsListAsync(final Class clazz, final Stream pks) { + final EntityInfo info = loadEntityInfo(clazz); + Serializable[] ids = pks.toArray(v -> new Serializable[v]); + return queryListAsync(info.getType(), null, null, FilterNode.create(info.getPrimarySQLColumn(), FilterExpress.IN, ids)); + } + + @Override + public T find(Class clazz, final SelectColumn selects, Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); + if (cache.isFullLoaded() || rs != null) { + return rs; + } + } + String[] tables = info.getTableOneArray(pk); + String sql = findSql(info, selects, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + } + if (isAsync()) { + return findDBAsync(info, tables, sql, true, selects, pk, null).join(); + } else { + return findDB(info, tables, sql, true, selects, pk, null); + } + } + + @Override + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); + if (cache.isFullLoaded() || rs != null) { + return CompletableFuture.completedFuture(rs); + } + } + String[] tables = info.getTableOneArray(pk); + String sql = findSql(info, selects, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + } + if (isAsync()) { + return findDBAsync(info, tables, sql, true, selects, pk, null); + } else { + return supplyAsync(() -> findDB(info, tables, sql, true, selects, pk, null)); + } + } + + protected String findSql(final EntityInfo info, final SelectColumn selects, Serializable pk) { + String column = info.getPrimarySQLColumn(); + final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter); + return sql; + } + + @Override + public T find(final Class clazz, final SelectColumn selects, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) { + return cache.find(selects, node); + } + final String[] tables = info.getTables(node); + String sql = findSql(info, tables, selects, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + } + if (isAsync()) { + return findDBAsync(info, tables, sql, false, selects, null, node).join(); + } else { + return findDB(info, tables, sql, false, selects, null, node); + } + } + + @Override + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) { + return CompletableFuture.completedFuture(cache.find(selects, node)); + } + final String[] tables = info.getTables(node); + String sql = findSql(info, tables, selects, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + } + if (isAsync()) { + return findDBAsync(info, tables, sql, false, selects, null, node); + } else { + return supplyAsync(() -> findDB(info, tables, sql, false, selects, null, node)); + } + } + + protected String findSql(final EntityInfo info, final String[] tables, final SelectColumn selects, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + String sql; + if (tables.length == 1) { + sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + tables[0] + " a" + joinAndWhere; + } else { + StringBuilder union = new StringBuilder(); + for (String table : tables) { + if (union.length() > 0) { + union.append(" UNION ALL "); + } + union.append("SELECT ").append(info.getQueryColumns("a", selects)).append(" FROM ").append(table).append(" a").append(joinAndWhere); + } + sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM (" + (union) + ") a"; + } + return sql; + } + + @Local + protected CompletableFuture findDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, SelectColumn selects) { + return future.thenApply((DataResultSet pgset) -> { + T rs = pgset.next() ? (onlypk && selects == null ? getEntityValue(info, null, pgset) : getEntityValue(info, selects, pgset)) : null; + pgset.close(); + return rs; + }); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, pk); + if (cache.isFullLoaded() || val != null) { + return val; + } + } + String[] tables = info.getTableOneArray(pk); + String sql = findColumnSql(info, column, defValue, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); + } + if (isAsync()) { + return findColumnDBAsync(info, tables, sql, true, column, defValue, pk, null).join(); + } else { + return findColumnDB(info, tables, sql, true, column, defValue, pk, null); + } + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, pk); + if (cache.isFullLoaded() || val != null) { + return CompletableFuture.completedFuture(val); + } + } + String[] tables = info.getTableOneArray(pk); + String sql = findColumnSql(info, column, defValue, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); + } + if (isAsync()) { + return findColumnDBAsync(info, tables, sql, true, column, defValue, pk, null); + } else { + return supplyAsync(() -> findColumnDB(info, tables, sql, true, column, defValue, pk, null)); + } + } + + protected String findColumnSql(final EntityInfo info, String column, final Serializable defValue, final Serializable pk) { + return "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, node); + if (cache.isFullLoaded() || val != null) { + return val; + } + } + final String[] tables = info.getTables(node); + String sql = findColumnSql(info, tables, column, defValue, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); + } + if (isAsync()) { + return findColumnDBAsync(info, tables, sql, false, column, defValue, null, node).join(); + } else { + return findColumnDB(info, tables, sql, false, column, defValue, null, node); + } + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, node); + if (cache.isFullLoaded() || val != null) { + return CompletableFuture.completedFuture(val); + } + } + final String[] tables = info.getTables(node); + String sql = findColumnSql(info, tables, column, defValue, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); + } + if (isAsync()) { + return findColumnDBAsync(info, tables, sql, false, column, defValue, null, node); + } else { + return supplyAsync(() -> findColumnDB(info, tables, sql, false, column, defValue, null, node)); + } + } + + protected String findColumnSql(final EntityInfo info, String[] tables, String column, final Serializable defValue, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + String sql; + if (tables.length == 1) { + sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + tables[0] + " a" + joinAndWhere; + } else { + StringBuilder union = new StringBuilder(); + for (String table : tables) { + if (union.length() > 0) { + union.append(" UNION ALL "); + } + union.append("SELECT ").append(info.getSQLColumn("a", column)).append(" FROM ").append(table).append(" a").append(joinAndWhere); + } + sql = "SELECT " + info.getSQLColumn("a", column) + " FROM (" + (union) + ") a"; + } + return sql; + } + + @Local + protected CompletableFuture findColumnDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, String column, Serializable defValue) { + return future.thenApply((DataResultSet dataset) -> { + Serializable val = defValue; + if (dataset.next()) { + final Attribute attr = info.getAttribute(column); + val = dataset.getObject(attr, 1, null); + } + dataset.close(); + return val == null ? defValue : val; + }); + } + + //---------------------------- existsCompose ---------------------------- + @Override + public boolean exists(Class clazz, Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(pk); + if (rs || cache.isFullLoaded()) { + return rs; + } + } + String[] tables = info.getTableOneArray(pk); + String sql = existsSql(info, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + } + if (isAsync()) { + return existsDBAsync(info, tables, sql, true, pk, null).join(); + } else { + return existsDB(info, tables, sql, true, pk, null); + } + } + + @Override + public CompletableFuture existsAsync(final Class clazz, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(pk); + if (rs || cache.isFullLoaded()) { + return CompletableFuture.completedFuture(rs); + } + } + String[] tables = info.getTableOneArray(pk); + String sql = existsSql(info, pk); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + } + if (isAsync()) { + return existsDBAsync(info, tables, sql, true, pk, null); + } else { + return supplyAsync(() -> existsDB(info, tables, sql, true, pk, null)); + } + } + + protected String existsSql(final EntityInfo info, Serializable pk) { + return "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + } + + @Override + public boolean exists(final Class clazz, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(node); + if (rs || cache.isFullLoaded()) { + return rs; + } + } + final String[] tables = info.getTables(node); + String sql = existsSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + } + if (isAsync()) { + return existsDBAsync(info, tables, sql, false, null, node).join(); + } else { + return existsDB(info, tables, sql, false, null, node); + } + } + + @Override + public CompletableFuture existsAsync(final Class clazz, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(node); + if (rs || cache.isFullLoaded()) { + return CompletableFuture.completedFuture(rs); + } + } + + final String[] tables = info.getTables(node); + String sql = existsSql(info, tables, node); + if (info.isLoggable(logger, Level.FINEST, sql)) { + logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + } + if (isAsync()) { + return existsDBAsync(info, tables, sql, false, null, node); + } else { + return supplyAsync(() -> existsDB(info, tables, sql, false, null, node)); + } + } + + protected String existsSql(final EntityInfo info, String[] tables, FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + String sql; + if (tables.length == 1) { + sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + tables[0] + " a" + joinAndWhere; + } else { + StringBuilder union = new StringBuilder(); + for (String table : tables) { + if (union.length() > 0) { + union.append(" UNION ALL "); + } + union.append("SELECT ").append(info.getPrimarySQLColumn("a")).append(" FROM ").append(table).append(" a").append(joinAndWhere); + } + sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM (" + (union) + ") a"; + } + return sql; + } + + @Local + protected CompletableFuture existsDBApply(EntityInfo info, CompletableFuture future, boolean onlypk) { + return future.thenApply((DataResultSet pgset) -> { + boolean rs = pgset.next() ? (((Number) pgset.getObject(1)).intValue() > 0) : false; + pgset.close(); + return rs; + }); + } + + //-----------------------list set---------------------------- + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + final Set list = querySet(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final Set rs = new LinkedHashSet<>(); + if (list.isEmpty()) { + return rs; + } + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return querySetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Set list) -> { + final Set rs = new LinkedHashSet<>(); + if (list.isEmpty()) { + return rs; + } + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + }); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + final List list = queryList(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final List rs = new ArrayList<>(); + if (list.isEmpty()) { + return rs; + } + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return queryListAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((List list) -> { + final List rs = new ArrayList<>(); + if (list.isEmpty()) { + return rs; + } + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + }); + } + + @Override + public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + Sheet sheet = querySheet(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final Sheet rs = new Sheet<>(); + if (sheet.isEmpty()) { + return rs; + } + rs.setTotal(sheet.getTotal()); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + final List list = new ArrayList<>(); + for (T t : sheet.getRows()) { + list.add(selected.get(t)); + } + rs.setRows(list); + return rs; + } + + @Override + public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Sheet sheet) -> { + final Sheet rs = new Sheet<>(); + if (sheet.isEmpty()) { + return rs; + } + rs.setTotal(sheet.getTotal()); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + final List list = new ArrayList<>(); + for (T t : sheet.getRows()) { + list.add(selected.get(t)); + } + rs.setRows(list); + return rs; + }); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
+ * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
+ * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param keyStream 主键Stream + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream) { + if (keyStream == null) { + return new LinkedHashMap<>(); + } + final EntityInfo info = loadEntityInfo(clazz); + final ArrayList ids = new ArrayList<>(); + keyStream.forEach(k -> ids.add(k)); + final Attribute primary = info.getPrimary(); + List rs = queryList(clazz, FilterNode.create(primary.field(), ids)); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) { + return new LinkedHashMap<>(); + } + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream) { + if (keyStream == null) { + return CompletableFuture.completedFuture(new LinkedHashMap<>()); + } + final EntityInfo info = loadEntityInfo(clazz); + final ArrayList pks = new ArrayList<>(); + keyStream.forEach(k -> pks.add(k)); + final Attribute primary = info.getPrimary(); + return queryListAsync(clazz, FilterNode.create(primary.field(), pks)).thenApply((List rs) -> { + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) { + return new LinkedHashMap<>(); + } + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + }); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
+ * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
+ * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node FilterNode + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node) { + List rs = queryList(clazz, selects, node); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) { + return new LinkedHashMap<>(); + } + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + return queryListAsync(clazz, selects, node).thenApply((List rs) -> { + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) { + return new LinkedHashMap<>(); + } + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + }); + } + + @Override + public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))).join(); + } else { + return new LinkedHashSet<>(querySheet(true, false, true, clazz, selects, flipper, node).list(true)); + } + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); + } else { + return supplyAsync(() -> querySheet(true, false, true, clazz, selects, flipper, node)).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); + } + } + + @Override + public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)).join(); + } else { + return querySheet(true, false, false, clazz, selects, flipper, node).list(true); + } + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)); + } else { + return supplyAsync(() -> querySheet(true, false, false, clazz, selects, flipper, node)).thenApply((rs) -> rs.list(true)); + } + } + + @Override + public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, true, false, clazz, selects, flipper, node).join(); + } else { + return querySheet(true, true, false, clazz, selects, flipper, node); + } + } + + @Override + public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) { + return querySheetAsync(true, true, false, clazz, selects, flipper, node); + } else { + return supplyAsync(() -> querySheet(true, true, false, clazz, selects, flipper, node)); + } + } + + protected Sheet querySheet(final boolean readCache, final boolean needTotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (readCache && cache != null && cache.isFullLoaded()) { + if (node == null || isCacheUseable(node, this)) { + if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) { + logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : createPredicate(node, cache))); + } + return cache.querySheet(needTotal, distinct, selects, flipper, node); + } + } + return querySheetDB(info, readCache, needTotal, distinct, selects, flipper, node); + } + + protected CompletableFuture> querySheetAsync(final boolean readCache, final boolean needTotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (readCache && cache != null && cache.isFullLoaded()) { + if (node == null || isCacheUseable(node, this)) { + if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) { + logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : createPredicate(node, cache))); + } + return CompletableFuture.completedFuture(cache.querySheet(needTotal, distinct, selects, flipper, node)); + } + } + return querySheetDBAsync(info, readCache, needTotal, distinct, selects, flipper, node); + } + + protected static class UpdateSqlInfo { + + public String sql; //prepare-sql时表名参数只能是最后一个 + + public String[] tables; //存在值则长度必然大于1,sql为[0]构建的sql + + public List blobs; //要么null,要么有内容,不能是empty-list + + public boolean prepare; //是否PreparedStatement SQL + + public UpdateSqlInfo(boolean prepare, String sql, byte[]... blobs) { + this(prepare, sql, null, blobs); + } + + public UpdateSqlInfo(boolean prepare, String sql, List blobs) { + this(prepare, sql, null, blobs); + } + + public UpdateSqlInfo(boolean prepare, String sql, String[] tables, byte[]... blobs) { + this.prepare = prepare; + this.sql = sql; + this.tables = tables; + if (blobs.length > 0) { + this.blobs = new ArrayList<>(); + for (byte[] bs : blobs) { + this.blobs.add(bs); + } + } + } + + public UpdateSqlInfo(boolean prepare, String sql, String[] tables, List blobs) { + this.prepare = prepare; + this.sql = sql; + this.tables = tables; + this.blobs = blobs == null || blobs.isEmpty() ? null : blobs; + } + + } + + protected static class PrepareInfo { + + public String prepareSql; + + public List entitys; + + public PrepareInfo(String prepareSql) { + this.prepareSql = prepareSql; + } + + public void addEntity(T entity) { + if (entitys == null) { + entitys = new ArrayList<>(); + } + entitys.add(entity); + } + } +} diff --git a/src/main/java/org/redkale/source/DataJdbcSource.java b/src/main/java/org/redkale/source/DataJdbcSource.java index 0d2499d16..1f26f5c4f 100644 --- a/src/main/java/org/redkale/source/DataJdbcSource.java +++ b/src/main/java/org/redkale/source/DataJdbcSource.java @@ -30,7 +30,7 @@ import org.redkale.util.*; @AutoLoad(false) @SuppressWarnings("unchecked") @ResourceType(DataSource.class) -public class DataJdbcSource extends DataSqlSource { +public class DataJdbcSource extends AbstractDataSqlSource { protected ConnectionPool readPool; diff --git a/src/main/java/org/redkale/source/DataMemorySource.java b/src/main/java/org/redkale/source/DataMemorySource.java index 67d0171ca..ce4cf4f03 100644 --- a/src/main/java/org/redkale/source/DataMemorySource.java +++ b/src/main/java/org/redkale/source/DataMemorySource.java @@ -33,7 +33,7 @@ import org.redkale.util.*; @AutoLoad(false) @SuppressWarnings("unchecked") @ResourceType(DataSource.class) -public class DataMemorySource extends DataSqlSource implements SearchSource { +public class DataMemorySource extends AbstractDataSqlSource implements SearchSource { public DataMemorySource(String name) { this.name = name; diff --git a/src/main/java/org/redkale/source/DataSqlSource.java b/src/main/java/org/redkale/source/DataSqlSource.java index 4b48dd63f..3e0f09e2c 100644 --- a/src/main/java/org/redkale/source/DataSqlSource.java +++ b/src/main/java/org/redkale/source/DataSqlSource.java @@ -1,3283 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.math.*; -import java.sql.SQLException; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.Stream; -import org.redkale.annotation.AutoLoad; -import org.redkale.annotation.*; -import org.redkale.annotation.ResourceListener; -import org.redkale.annotation.ResourceType; -import static org.redkale.boot.Application.*; -import org.redkale.net.AsyncGroup; -import org.redkale.persistence.Table; -import org.redkale.service.Local; -import org.redkale.source.EntityInfo.EntityColumn; -import org.redkale.util.*; - -/** - * DataSource的SQL抽象实现类
- * 注意: 所有的操作只能作用在一张表上,不能同时变更多张表 - * - *

- * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Local -@AutoLoad(false) -@SuppressWarnings("unchecked") -@ResourceType(DataSource.class) -public abstract class DataSqlSource extends AbstractDataSource implements Function { - - //不存在分表时最大重试次数 - protected static final int MAX_RETRYS = 3; - - protected static final Flipper FLIPPER_ONE = new Flipper(1); - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected String name; - - protected boolean cacheForbidden; - - protected String dbtype; - - private boolean autoDDL; - - protected Properties readConfProps; - - protected Properties writeConfProps; - - @Resource(name = RESNAME_APP_CLIENT_ASYNCGROUP, required = false) - protected AsyncGroup clientAsyncGroup; - - //配置 APP_EXECUTOR资源为null - @Resource(name = RESNAME_APP_EXECUTOR, required = false) - protected ExecutorService workExecutor; - - protected BiFunction sqlFormatter; - - protected BiConsumer errorCompleteConsumer = (r, t) -> { - //if (t != null) logger.log(Level.INFO, "CompletableFuture complete error", (Throwable) t); - }; - - protected final BiFunction> fullloader = (s, i) - -> ((CompletableFuture) querySheetDBAsync(i, false, false, false, null, null, (FilterNode) null)).thenApply(e -> e == null ? new ArrayList() : e.list(true)); - - //超过多少毫秒视为较慢, 会打印警告级别的日志, 默认值: 2000 - protected long slowmsWarn; - - //超过多少毫秒视为很慢, 会打印错误级别的日志, 默认值: 3000 - protected long slowmsError; - - //用于反向LIKE使用 - protected String containSQL; - - //用于反向LIKE使用 - protected String notContainSQL; - - //用于判断表不存在的使用, 多个SQLState用;隔开 - protected String tableNotExistSqlstates; - - //用于复制表结构使用, sql语句必须包含IF NOT EXISTS判断,确保重复执行不会报错 - protected String tablecopySQL; - - protected AnyValue config; - - private EntityInfo currEntityInfo; - - public DataSqlSource() { - } - - @Override - public void init(AnyValue conf) { - super.init(conf); - this.config = conf; - if (conf.getAnyValue("read") == null) { //没有读写分离 - Properties rwConf = new Properties(); - conf.forEach((k, v) -> rwConf.put(k, decryptProperty(k, v))); - this.dbtype = parseDbtype(rwConf.getProperty(DATA_SOURCE_URL)); - initProperties(rwConf); - this.readConfProps = rwConf; - this.writeConfProps = rwConf; - } else { //读写分离 - Properties readConf = new Properties(); - Properties writeConf = new Properties(); - conf.getAnyValue("read").forEach((k, v) -> readConf.put(k, decryptProperty(k, v))); - conf.getAnyValue("write").forEach((k, v) -> writeConf.put(k, decryptProperty(k, v))); - this.dbtype = parseDbtype(readConf.getProperty(DATA_SOURCE_URL)); - initProperties(readConf); - initProperties(writeConf); - this.readConfProps = readConf; - this.writeConfProps = writeConf; - } - this.name = conf.getValue("name", ""); - this.sqlFormatter = (info, val) -> formatValueToString(info, val); - afterResourceChange(); - } - - protected void afterResourceChange() { - this.autoDDL = "true".equals(readConfProps.getProperty(DATA_SOURCE_TABLE_AUTODDL, "false").trim()); - - this.containSQL = readConfProps.getProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0"); - this.notContainSQL = readConfProps.getProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0"); - - this.tableNotExistSqlstates = ";" + readConfProps.getProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";"; - this.tablecopySQL = readConfProps.getProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}"); - - this.cacheForbidden = "NONE".equalsIgnoreCase(readConfProps.getProperty(DATA_SOURCE_CACHEMODE)); - this.slowmsWarn = Integer.parseInt(readConfProps.getProperty(DATA_SOURCE_SLOWMS_WARN, "2000").trim()); - this.slowmsError = Integer.parseInt(readConfProps.getProperty(DATA_SOURCE_SLOWMS_ERROR, "3000").trim()); - } - - @Override - @ResourceListener - public void onResourceChange(ResourceEvent[] events) { - if (events == null || events.length < 1) { - return; - } - //不支持读写分离模式的动态切换 - if (readConfProps == writeConfProps && (events[0].name().startsWith("read.") || events[0].name().startsWith("write."))) { - throw new SourceException("DataSource(name=" + resourceName() + ") not support to change to read/write separation mode"); - } - if (readConfProps != writeConfProps && (!events[0].name().startsWith("read.") && !events[0].name().startsWith("write."))) { - throw new SourceException("DataSource(name=" + resourceName() + ") not support to change to non read/write separation mode"); - } - - StringBuilder sb = new StringBuilder(); - if (readConfProps == writeConfProps) { - List allEvents = new ArrayList<>(); - Properties newProps = new Properties(); - newProps.putAll(this.readConfProps); - for (ResourceEvent event : events) { //可能需要解密 - String newValue = decryptProperty(event.name(), event.newValue().toString()); - allEvents.add(ResourceEvent.create(event.name(), newValue, event.oldValue())); - newProps.put(event.name(), newValue); - sb.append("DataSource(name=").append(resourceName()).append(") change '") - .append(event.name()).append("' to '").append(event.coverNewValue()).append("'\r\n"); - } - updateOneResourceChange(newProps, allEvents.toArray(new ResourceEvent[allEvents.size()])); - for (ResourceEvent event : allEvents) { - this.readConfProps.put(event.name(), event.newValue()); - } - } else { - List readEvents = new ArrayList<>(); - List writeEvents = new ArrayList<>(); - Properties newReadProps = new Properties(); - newReadProps.putAll(this.readConfProps); - Properties newWriteProps = new Properties(); - newWriteProps.putAll(this.writeConfProps); - for (ResourceEvent event : events) { - if (event.name().startsWith("read.")) { - String newName = event.name().substring("read.".length()); - String newValue = decryptProperty(event.name(), event.newValue().toString()); - readEvents.add(ResourceEvent.create(newName, newValue, event.oldValue())); - newReadProps.put(event.name(), newValue); - } else { - String newName = event.name().substring("write.".length()); - String newValue = decryptProperty(event.name(), event.newValue().toString()); - writeEvents.add(ResourceEvent.create(newName, newValue, event.oldValue())); - newWriteProps.put(event.name(), newValue); - } - sb.append("DataSource(name=").append(resourceName()).append(") change '") - .append(event.name()).append("' to '").append(event.coverNewValue()).append("'\r\n"); - } - if (!readEvents.isEmpty()) { - updateReadResourceChange(newReadProps, readEvents.toArray(new ResourceEvent[readEvents.size()])); - } - if (!writeEvents.isEmpty()) { - updateWriteResourceChange(newWriteProps, writeEvents.toArray(new ResourceEvent[writeEvents.size()])); - } - //更新Properties - if (!readEvents.isEmpty()) { - for (ResourceEvent event : readEvents) { - this.readConfProps.put(event.name(), event.newValue()); - } - } - if (!writeEvents.isEmpty()) { - for (ResourceEvent event : writeEvents) { - this.writeConfProps.put(event.name(), event.newValue()); - } - } - } - afterResourceChange(); - if (sb.length() > 0) { - logger.log(Level.INFO, sb.toString()); - } - } - - protected void updateOneResourceChange(Properties newProps, ResourceEvent[] events) { - throw new UnsupportedOperationException("Not supported yet."); - } - - protected void updateReadResourceChange(Properties newReadProps, ResourceEvent[] events) { - throw new UnsupportedOperationException("Not supported yet."); - } - - protected void updateWriteResourceChange(Properties newWriteProps, ResourceEvent[] events) { - throw new UnsupportedOperationException("Not supported yet."); - } - - protected void slowLog(long startTime, String... sqls) { - long cost = System.currentTimeMillis() - startTime; - if (slowmsError > 0 && cost > slowmsError) { - logger.log(Level.SEVERE, DataSource.class.getSimpleName() + "(name='" + resourceName() + "') slow sql cost " + cost + " ms, content: " + Arrays.toString(sqls)); - } else if (slowmsWarn > 0 && cost > slowmsWarn) { - logger.log(Level.WARNING, DataSource.class.getSimpleName() + "(name='" + resourceName() + "') very slow sql cost " + cost + " ms, content: " + Arrays.toString(sqls)); - } - } - - protected String parseNotExistTableName(SQLException e) { - String errmsg = e.getMessage(); - char quote = '"'; - String tableName = null; - int pos = errmsg.indexOf(quote); - if (pos < 0) { - quote = '\''; - pos = errmsg.indexOf(quote); - } - if (pos >= 0) { - int pos2 = errmsg.indexOf(quote, pos + 1); - if (pos2 > pos) { - tableName = errmsg.substring(pos + 1, pos2); - } - } - return tableName; - } - - //解密可能存在的加密字段, 可重载 - protected String decryptProperty(String key, String value) { - return value; - } - - protected void initProperties(Properties props) { - if ("oracle".equals(this.dbtype)) { - props.setProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0"); - props.setProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0"); - if (!props.containsKey(DATA_SOURCE_TABLENOTEXIST_SQLSTATES)) { - props.setProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42000;42S02"); - } - if (!props.containsKey(DATA_SOURCE_TABLECOPY_SQLTEMPLATE)) { - //注意:此语句复制表结构会导致默认值和主键信息的丢失 - props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2"); - } - } else if ("sqlserver".equals(this.dbtype)) { - props.setProperty(DATA_SOURCE_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0"); - props.setProperty(DATA_SOURCE_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0"); - } else if ("postgresql".equals(this.dbtype)) { - if (!props.containsKey(DATA_SOURCE_TABLECOPY_SQLTEMPLATE)) { //注意:此语句复制表结构会导致默认值和主键信息的丢失 - //注意:postgresql不支持跨库复制表结构 - //props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS (SELECT * FROM ${oldtable} LIMIT 0)"); - props.setProperty(DATA_SOURCE_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} (LIKE ${oldtable} INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING INDEXES)"); - } - if (!props.containsKey(DATA_SOURCE_TABLENOTEXIST_SQLSTATES)) { - props.setProperty(DATA_SOURCE_TABLENOTEXIST_SQLSTATES, "42P01;3F000"); - } - } - } - - @Override - public String toString() { - if (readConfProps == null) { //compileMode模式下会为null - return getClass().getSimpleName() + "{}"; - } - if (readConfProps == writeConfProps) { - String url = readConfProps.getProperty(DATA_SOURCE_URL); - int pos = url.indexOf('?'); - if (pos > 0) { - url = url.substring(0, pos) + "..."; - } - return getClass().getSimpleName() + "{url=" + url + "}"; - } else { - String readUrl = readConfProps.getProperty(DATA_SOURCE_URL); - int pos = readUrl.indexOf('?'); - if (pos > 0) { - readUrl = readUrl.substring(0, pos) + "..."; - } - String writeUrl = writeConfProps.getProperty(DATA_SOURCE_URL); - pos = writeUrl.indexOf('?'); - if (pos > 0) { - writeUrl = writeUrl.substring(0, pos) + "..."; - } - return getClass().getSimpleName() + "{readurl=" + readUrl + ",writeurl=" + writeUrl + "}"; - } - } - - //生成创建表的SQL - protected String[] createTableSqls(EntityInfo info) { - if (info == null || !autoDDL) { - return null; - } - Table table = info.getType().getAnnotation(Table.class); - if ("mysql".equals(dbtype())) { //mysql - StringBuilder sb = new StringBuilder(); - sb.append("CREATE TABLE IF NOT EXISTS `").append(info.getOriginTable()).append("`(\n"); - EntityColumn primary = null; - T one = info.constructorAttributes == null ? info.getCreator().create() : null; - for (EntityColumn column : info.getDDLColumns()) { - if (column.primary) { - primary = column; - } - String sqltype = "VARCHAR(" + column.length + ")"; - String sqlnull = column.primary ? "NOT NULL" : "NULL"; - if (column.type == boolean.class || column.type == Boolean.class) { - sqltype = "TINYINT(1)"; - Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); - } else if (column.type == byte.class || column.type == Byte.class) { - sqltype = "TINYINT"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); - } else if (column.type == short.class || column.type == Short.class) { - sqltype = "SMALLINT"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == char.class || column.type == Character.class) { - sqltype = "SMALLINT UNSIGNED"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); - } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { - sqltype = "INT"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { - sqltype = "BIGINT"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == float.class || column.type == Float.class) { - sqltype = "FLOAT"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == double.class || column.type == Double.class) { - sqltype = "DOUBLE"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == BigInteger.class) { - sqltype = "DECIMAL"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } else { - sqltype += "(19,2)"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == BigDecimal.class) { - sqltype = "DECIMAL"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == String.class) { - if (column.length < 65535) { - String val = one == null ? null : (String) info.getAttribute(column.field).get(one); - if (val != null) { - sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; - } else if (column.primary) { - sqlnull = "NOT NULL DEFAULT ''"; - } - } else if (column.length == 65535) { - sqltype = "TEXT"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } else if (column.length <= 16777215) { - sqltype = "MEDIUMTEXT"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } else { - sqltype = "LONGTEXT"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } - } else if (column.type == byte[].class) { - if (column.length <= 65535) { - sqltype = "BLOB"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } else if (column.length <= 16777215) { - sqltype = "MEDIUMBLOB"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } else { - sqltype = "LONGBLOB"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } - } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { - sqltype = "DATE"; - } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { - sqltype = "TIME"; - } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { - sqltype = "DATETIME"; - } else { //JavaBean - sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); - sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; - } - sb.append(" `").append(column.column).append("` ").append(sqltype).append(" ").append(sqlnull); - if (column.comment != null && !column.comment.isEmpty()) { - sb.append(" COMMENT '").append(column.comment.replace('\'', '"')).append("'"); - } - sb.append(",\n"); - } - sb.append(" PRIMARY KEY (`").append(primary.column).append("`)\n"); - sb.append(")ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4"); - if (table != null && !table.comment().isEmpty()) { - sb.append(" COMMENT '").append(table.comment().replace('\'', '"')).append("'"); - } - return Utility.ofArray(sb.toString()); - } else if ("postgresql".equals(dbtype())) { //postgresql - StringBuilder sb = new StringBuilder(); - sb.append("CREATE TABLE IF NOT EXISTS ").append(info.getOriginTable()).append("(\n"); - EntityColumn primary = null; - T one = info.constructorAttributes == null ? info.getCreator().create() : null; - List comments = new ArrayList<>(); - if (table != null && !table.comment().isEmpty()) { - comments.add("COMMENT ON TABLE " + info.getOriginTable() + " IS '" + table.comment().replace('\'', '"') + "'"); - } - for (EntityColumn column : info.getDDLColumns()) { - if (column.primary) { - primary = column; - } - String sqltype = "VARCHAR(" + column.length + ")"; - String sqlnull = column.primary ? "NOT NULL" : "NULL"; - if (column.type == boolean.class || column.type == Boolean.class) { - sqltype = "BOOL"; - Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); - } else if (column.type == byte.class || column.type == Byte.class) { - sqltype = "INT2"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); - } else if (column.type == short.class || column.type == Short.class) { - sqltype = "INT2"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == char.class || column.type == Character.class) { - sqltype = "INT2 UNSIGNED"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); - } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { - sqltype = "INT4"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { - sqltype = "INT8"; - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == float.class || column.type == Float.class) { - sqltype = "FLOAT4"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == double.class || column.type == Double.class) { - sqltype = "FLOAT8"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == BigInteger.class) { - sqltype = "NUMERIC"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } else { - sqltype += "(19,2)"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == BigDecimal.class) { - sqltype = "NUMERIC"; - if (column.precision > 0) { - sqltype += "(" + column.precision + "," + column.scale + ")"; - } - Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); - sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); - } else if (column.type == String.class) { - if (column.length < 65535) { - String val = one == null ? null : (String) info.getAttribute(column.field).get(one); - if (val != null) { - sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; - } else if (column.primary) { - sqlnull = "NOT NULL DEFAULT ''"; - } - } else { - sqltype = "TEXT"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } - } else if (column.type == byte[].class) { - sqltype = "BYTEA"; - if (!column.nullable) { - sqlnull = "NOT NULL"; - } - } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { - sqltype = "DATE"; - } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { - sqltype = "TIME"; - } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { - sqltype = "TIMESTAMP"; - } else { //JavaBean - sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); - sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; - } - sb.append(" ").append(column.column).append(" ").append(sqltype).append(" ").append(sqlnull); - if (column.comment != null && !column.comment.isEmpty()) { - //postgresql不支持DDL中直接带comment - comments.add("COMMENT ON COLUMN " + info.getOriginTable() + "." + column.column + " IS '" + column.comment.replace('\'', '"') + "'"); - } - sb.append(",\n"); - } - sb.append(" PRIMARY KEY (").append(primary.column).append(")\n"); - sb.append(")"); - return Utility.append(Utility.ofArray(sb.toString()), comments); - } - return null; - } - - @Local - protected boolean isTableNotExist(EntityInfo info, String sqlCode) { - return sqlCode != null && !sqlCode.isEmpty() && tableNotExistSqlstates.contains(';' + sqlCode + ';'); - } - - @Local - protected String getTableCopySQL(EntityInfo info, String newTable) { - return tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", info.table); - } - - @Local - protected Serializable getSQLAttrValue(EntityInfo info, Attribute attr, Serializable val) { - if (val != null && !(val instanceof Number) && !(val instanceof CharSequence) && !(val instanceof java.util.Date) - && !val.getClass().getName().startsWith("java.sql.") && !val.getClass().getName().startsWith("java.time.")) { - val = info.jsonConvert.convertTo(attr.genericType(), val); - } else if (val == null && info.isNotNullJson(attr)) { - val = ""; - } - return val; - } - - @Local - protected Map> getInsertQuestionPrepareInfo(EntityInfo info, T... entitys) { - Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap - for (T entity : entitys) { - String table = info.getTable(entity); - map.computeIfAbsent(table, t -> new PrepareInfo(info.getInsertQuestionPrepareSQL(entity))).addEntity(entity); - } - return map; - } - - @Local - protected Map> getInsertDollarPrepareInfo(EntityInfo info, T... entitys) { - Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap - for (T entity : entitys) { - String table = info.getTable(entity); - map.computeIfAbsent(table, t -> new PrepareInfo(info.getInsertDollarPrepareSQL(entity))).addEntity(entity); - } - return map; - } - - @Local - protected Map> getUpdateQuestionPrepareInfo(EntityInfo info, T... entitys) { - Map> map = new LinkedHashMap<>(); //一定要是LinkedHashMap - for (T entity : entitys) { - String table = info.getTable(entity); - map.computeIfAbsent(table, t -> new PrepareInfo(info.getUpdateQuestionPrepareSQL(entity))).addEntity(entity); - } - return map; - } - - @Local - protected Map> getUpdateDollarPrepareInfo(EntityInfo info, T... entitys) { - Map> map = new LinkedHashMap<>();//一定要是LinkedHashMap - for (T entity : entitys) { - String table = info.getTable(entity); - map.computeIfAbsent(table, t -> new PrepareInfo(info.getUpdateDollarPrepareSQL(entity))).addEntity(entity); - } - return map; - } - - @Local - protected Serializable getEntityAttrValue(EntityInfo info, Attribute attr, T entity) { - Serializable val = info.getSQLValue(attr, entity); - Class clazz = attr.type(); - if (clazz == String.class - || clazz == int.class || clazz == long.class - || clazz == Integer.class || clazz == Long.class - || clazz == short.class || clazz == Short.class - || clazz == float.class || clazz == Float.class - || clazz == double.class || clazz == Double.class) { - return val; - } - return getSQLAttrValue(info, attr, val); - } - - @Override - public void destroy(AnyValue config) { - super.destroy(config); - } - - @Override - @Local - public void compile(Class clazz) { - EntityInfo.compile(clazz, this); - } - - @Local - public final String dbtype() { - if (dbtype == null) { - throw new NullPointerException("dbtype is null"); - } - return dbtype; - } - - @Local - public final boolean autoddl() { - return autoDDL; - } - - @Local - public abstract int directExecute(String sql); - - @Local - public abstract int[] directExecute(String... sqls); - - @Local - public abstract V directQuery(String sql, Function handler); - - //是否异步 - protected abstract boolean isAsync(); - - //index从1开始 - protected abstract String prepareParamSign(int index); - - //插入纪录 - protected abstract CompletableFuture insertDBAsync(final EntityInfo info, T... entitys); - - //删除记录 - protected abstract CompletableFuture deleteDBAsync(final EntityInfo info, String[] tables, Flipper flipper, FilterNode node, Map> pkmap, final String... sqls); - - //清空表 - protected abstract CompletableFuture clearTableDBAsync(final EntityInfo info, String[] tables, FilterNode node, final String... sqls); - - //建表 - protected abstract CompletableFuture createTableDBAsync(final EntityInfo info, String copyTableSql, Serializable pk, final String... sqls); - - //删除表 - protected abstract CompletableFuture dropTableDBAsync(final EntityInfo info, String[] tables, FilterNode node, final String... sqls); - - //更新纪录 - protected abstract CompletableFuture updateEntityDBAsync(final EntityInfo info, T... entitys); - - //更新纪录 - protected abstract CompletableFuture updateColumnDBAsync(final EntityInfo info, Flipper flipper, final UpdateSqlInfo sql); - - //查询Number Map数据 - protected abstract CompletableFuture> getNumberMapDBAsync(final EntityInfo info, String[] tables, final String sql, final FilterNode node, final FilterFuncColumn... columns); - - //查询Number数据 - protected abstract CompletableFuture getNumberResultDBAsync(final EntityInfo info, String[] tables, final String sql, final FilterFunc func, final Number defVal, final String column, final FilterNode node); - - //查询Map数据 - protected abstract CompletableFuture> queryColumnMapDBAsync(final EntityInfo info, String[] tables, final String sql, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node); - - //查询Map数据 - protected abstract CompletableFuture> queryColumnMapDBAsync(final EntityInfo info, String[] tables, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); - - //查询单条记录 - protected abstract CompletableFuture findDBAsync(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final SelectColumn selects, final Serializable pk, final FilterNode node); - - //查询单条记录的单个字段 - protected abstract CompletableFuture findColumnDBAsync(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final String column, final Serializable defValue, final Serializable pk, final FilterNode node); - - //判断记录是否存在 - protected abstract CompletableFuture existsDBAsync(final EntityInfo info, final String[] tables, final String sql, final boolean onlypk, final Serializable pk, final FilterNode node); - - //查询一页数据 - protected abstract CompletableFuture> querySheetDBAsync(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - //插入纪录 - protected int insertDB(final EntityInfo info, T... entitys) { - return insertDBAsync(info, entitys).join(); - } - - //删除记录 - protected int deleteDB(final EntityInfo info, String[] tables, Flipper flipper, FilterNode node, Map> pkmap, final String... sqls) { - return deleteDBAsync(info, tables, flipper, node, pkmap, sqls).join(); - } - - //清空表 - protected int clearTableDB(final EntityInfo info, String[] tables, FilterNode node, final String... sqls) { - return clearTableDBAsync(info, tables, node, sqls).join(); - } - - //建表 - protected int createTableDB(final EntityInfo info, String copyTableSql, Serializable pk, final String... sqls) { - return createTableDBAsync(info, copyTableSql, pk, sqls).join(); - } - - //删除表 - protected int dropTableDB(final EntityInfo info, String[] tables, FilterNode node, final String... sqls) { - return dropTableDBAsync(info, tables, node, sqls).join(); - } - - //更新纪录 - protected int updateEntityDB(final EntityInfo info, T... entitys) { - return updateEntityDBAsync(info, entitys).join(); - } - - //更新纪录 - protected int updateColumnDB(final EntityInfo info, Flipper flipper, final UpdateSqlInfo sql) { - return updateColumnDBAsync(info, flipper, sql).join(); - } - - //查询Number Map数据 - protected Map getNumberMapDB(final EntityInfo info, String[] tables, final String sql, final FilterNode node, final FilterFuncColumn... columns) { - return (Map) getNumberMapDBAsync(info, tables, sql, node, columns).join(); - } - - //查询Number数据 - protected Number getNumberResultDB(final EntityInfo info, String[] tables, final String sql, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node).join(); - } - - //查询Map数据 - protected Map queryColumnMapDB(final EntityInfo info, String[] tables, final String sql, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - return (Map) queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node).join(); - } - - //查询Map数据 - protected Map queryColumnMapDB(final EntityInfo info, String[] tables, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - return (Map) queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node).join(); - } - - //查询单条记录 - protected T findDB(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final SelectColumn selects, final Serializable pk, final FilterNode node) { - return findDBAsync(info, tables, sql, onlypk, selects, pk, node).join(); - } - - //查询单条记录的单个字段 - protected Serializable findColumnDB(final EntityInfo info, String[] tables, final String sql, final boolean onlypk, final String column, final Serializable defValue, final Serializable pk, final FilterNode node) { - return findColumnDBAsync(info, tables, sql, onlypk, column, defValue, pk, node).join(); - } - - //判断记录是否存在 - protected boolean existsDB(final EntityInfo info, final String[] tables, final String sql, final boolean onlypk, final Serializable pk, final FilterNode node) { - return existsDBAsync(info, tables, sql, onlypk, pk, node).join(); - } - - //查询一页数据 - protected Sheet querySheetDB(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheetDBAsync(info, readcache, needtotal, distinct, selects, flipper, node).join(); - } - - protected CharSequence createSQLJoin(FilterNode node, final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { - return node == null ? null : node.createSQLJoin(func, update, joinTabalis, haset, info); - } - - protected CharSequence createSQLExpress(FilterNode node, final EntityInfo info, final Map joinTabalis) { - return node == null ? null : node.createSQLExpress(this, info, joinTabalis); - } - - @Local - @Override - public String getType() { - return "sql"; - } - - @Override - public final String resourceName() { - return name; - } - - @Local - @Override - public EntityInfo apply(Class t) { - return loadEntityInfo(t); - } - - @Local - @Override - public void close() throws Exception { - } - - protected EntityInfo loadEntityInfo(Class clazz) { - EntityInfo info = this.currEntityInfo; - if (info != null && info.getType() == clazz) { - return info; - } - info = loadEntityInfo(clazz, this.cacheForbidden, readConfProps, fullloader); - this.currEntityInfo = info; - return info; - } - - public EntityCache loadCache(Class clazz) { - EntityInfo info = loadEntityInfo(clazz); - return info.getCache(); - } - - /** - * 将entity的对象全部加载到Cache中去,如果clazz没有被@org.redkale.persistence.Cacheable注解则不做任何事 - * - * @param Entity类泛型 - * @param clazz Entity类 - */ - public void refreshCache(Class clazz) { - EntityInfo info = loadEntityInfo(clazz); - EntityCache cache = info.getCache(); - if (cache == null) { - return; - } - cache.fullLoadAsync(); - } - - protected CharSequence formatValueToString(final EntityInfo info, Object value) { - if (value == null) { - return null; - } - if ("mysql".equals(dbtype)) { - if (value instanceof CharSequence) { - return new StringBuilder().append('\'').append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); - } else if (!(value instanceof Number) && !(value instanceof java.util.Date) - && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { - return new StringBuilder().append('\'').append(info.getJsonConvert().convertTo(value).replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); - } - return String.valueOf(value); - } else if (value != null && value instanceof CharSequence && "postgresql".equals(dbtype)) { - String s = String.valueOf(value); - int pos = s.indexOf('\''); - if (pos >= 0) { - return new StringBuilder().append("E'").append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); - } - } - return info.formatSQLValue(value, null); - } - - //----------------------------- insert ----------------------------- - /** - * 新增对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - @Override - public int insert(T... entitys) { - if (entitys.length == 0) { - return 0; - } - checkEntity("insert", entitys); - final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); - if (isOnlyCache(info)) { - return insertCache(info, entitys); - } - if (isAsync()) { - int rs = insertDBAsync(info, entitys).join(); - insertCache(info, entitys); - return rs; - } else { - int rs = insertDB(info, entitys); - insertCache(info, entitys); - return rs; - } - } - - @Override - public CompletableFuture insertAsync(T... entitys) { - if (entitys.length == 0) { - return CompletableFuture.completedFuture(0); - } - checkEntity("insert", entitys); - final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(insertCache(info, entitys)); - } - if (isAsync()) { - return insertDBAsync(info, entitys).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - insertCache(info, entitys); - } - }); - } else { - return supplyAsync(() -> insertDB(info, entitys)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - insertCache(info, entitys); - } - }); - } - } - - protected int insertCache(final EntityInfo info, T... entitys) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return 0; - } - int c = 0; - for (final T value : entitys) { - c += cache.insert(value); - } - return c; - } - - //----------------------------- deleteCompose ----------------------------- - /** - * 删除对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 删除的数据条数 - */ - @Override - public int delete(T... entitys) { - if (entitys.length == 0) { - return -1; - } - checkEntity("delete", entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Serializable[] ids = new Serializable[entitys.length]; - int i = 0; - for (final T value : entitys) { - ids[i++] = (Serializable) primary.get(value); - } - return delete(clazz, ids); - } - - @Override - public CompletableFuture deleteAsync(final T... entitys) { - if (entitys.length == 0) { - return CompletableFuture.completedFuture(-1); - } - checkEntity("delete", entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Serializable[] ids = new Serializable[entitys.length]; - int i = 0; - for (final T value : entitys) { - ids[i++] = (Serializable) primary.get(value); - } - return deleteAsync(clazz, ids); - } - - @Override - public int delete(Class clazz, Serializable... pks) { - if (pks.length == 0) { - return -1; - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return deleteCache(info, -1, pks); - } - Map> pkmap = info.getTableMap(pks); - String[] tables = pkmap.keySet().toArray(new String[pkmap.size()]); - String[] sqls = deleteSql(info, pkmap); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - int rs = deleteDBAsync(info, tables, null, null, pkmap, sqls).join(); - deleteCache(info, rs, pks); - return rs; - } else { - int rs = deleteDB(info, tables, null, null, pkmap, sqls); - deleteCache(info, rs, pks); - return rs; - } - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks) { - if (pks.length == 0) { - return CompletableFuture.completedFuture(-1); - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(deleteCache(info, -1, pks)); - } - Map> pkmap = info.getTableMap(pks); - String[] tables = pkmap.keySet().toArray(new String[pkmap.size()]); - String[] sqls = deleteSql(info, pkmap); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - return deleteDBAsync(info, tables, null, null, pkmap, sqls).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, pks); - } - }); - } else { - return supplyAsync(() -> deleteDB(info, tables, null, null, pkmap, sqls)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, pks); - } - }); - } - } - - @Override - public int delete(Class clazz, FilterNode node) { - return delete(clazz, (Flipper) null, node); - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { - return deleteAsync(clazz, (Flipper) null, node); - } - - @Override - public int delete(Class clazz, final Flipper flipper, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return deleteCache(info, -1, flipper, node); - } - String[] tables = info.getTables(node); - String[] sqls = deleteSql(info, tables, flipper, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - int rs = deleteDBAsync(info, tables, flipper, node, null, sqls).join(); - deleteCache(info, rs, flipper, sqls); - return rs; - } else { - int rs = deleteDB(info, tables, flipper, node, null, sqls); - deleteCache(info, rs, flipper, sqls); - return rs; - } - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(deleteCache(info, -1, flipper, node)); - } - String[] tables = info.getTables(node); - String[] sqls = deleteSql(info, tables, flipper, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " delete sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - return deleteDBAsync(info, tables, flipper, node, null, sqls).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, flipper, node); - } - }); - } else { - return supplyAsync(() -> deleteDB(info, tables, flipper, node, null, sqls)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, flipper, node); - } - }); - } - } - - protected String[] deleteSql(final EntityInfo info, final Map> pkmap) { - List sqls = new ArrayList<>(); - final String pkSQLColumn = info.getPrimarySQLColumn(); - pkmap.forEach((table, pks) -> { - String sql; - if (pks.size() == 1) { - sql = "DELETE FROM " + table + " WHERE " + pkSQLColumn + " = " + info.formatSQLValue(pkSQLColumn, pks.get(0), sqlFormatter); - } else { - sql = "DELETE FROM " + table + " WHERE " + pkSQLColumn + " IN ("; - for (int i = 0; i < pks.size(); i++) { - if (i > 0) { - sql += ','; - } - sql += info.formatSQLValue(pkSQLColumn, pks.get(i), sqlFormatter); - } - sql += ")"; - } - sqls.add(sql); - }); - return sqls.toArray(new String[sqls.size()]); - } - - protected String[] deleteSql(final EntityInfo info, String[] tables, final Flipper flipper, final FilterNode node) { - Map joinTabalis = null; - CharSequence join = null; - CharSequence where = null; - if (node != null) { - joinTabalis = node.getJoinTabalis(); - join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - where = node.createSQLExpress(this, info, joinTabalis); - } - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - final String join2AndWhere = ((where == null || where.length() == 0) ? (join2 == null ? "" - : (" WHERE " + join2)) : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - - if ("postgresql".equals(dbtype()) && flipper != null && flipper.getLimit() > 0) { - List sqls = new ArrayList<>(); - for (String table : tables) { - String sql = "DELETE FROM " + table + " a" + (join1 == null ? "" : (", " + join1)) + " WHERE " + info.getPrimarySQLColumn() + " IN (SELECT " + info.getPrimaryField() + " FROM " + table - + join2AndWhere + info.createSQLOrderby(flipper) + " OFFSET 0 LIMIT " + flipper.getLimit() + ")"; - sqls.add(sql); - } - return sqls.toArray(new String[sqls.size()]); - } else { - boolean mysql = "mysql".equals(dbtype()); - List sqls = new ArrayList<>(); - for (String table : tables) { - String sql = "DELETE " + (mysql ? "a" : "") + " FROM " + table + " a" + (join1 == null ? "" : (", " + join1)) + join2AndWhere + info.createSQLOrderby(flipper) - + ((mysql && flipper != null && flipper.getLimit() > 0) ? (" LIMIT " + flipper.getLimit()) : ""); - sqls.add(sql); - } - return sqls.toArray(new String[sqls.size()]); - } - } - - //----------------------------- clearTableCompose ----------------------------- - @Override - public int clearTable(Class clazz) { - return clearTable(clazz, (FilterNode) null); - } - - @Override - public int clearTable(Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return clearTableCache(info, node); - } - final String[] tables = info.getTables(node); - String[] sqls = clearTableSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " clearTable sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - int rs = clearTableDBAsync(info, tables, node, sqls).join(); - clearTableCache(info, node); - return rs; - } else { - int rs = clearTableDB(info, tables, node, sqls); - clearTableCache(info, node); - return rs; - } - } - - @Override - public CompletableFuture clearTableAsync(final Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(clearTableCache(info, node)); - } - final String[] tables = info.getTables(node); - String[] sqls = clearTableSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " clearTable sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - return clearTableDBAsync(info, tables, node, sqls).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - clearTableCache(info, node); - } - }); - } else { - return supplyAsync(() -> clearTableDB(info, tables, node, sqls)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - clearTableCache(info, node); - } - }); - } - } - - protected String[] clearTableSql(final EntityInfo info, String[] tables, final FilterNode node) { - List sqls = new ArrayList<>(); - for (String table : tables) { - sqls.add("TRUNCATE TABLE " + table); - } - return sqls.toArray(new String[sqls.size()]); - } - - //----------------------------- dropTable ----------------------------- - @Override - public int createTable(final Class clazz, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final String[] sqls = createTableSqls(info); - if (sqls == null) { - return -1; - } - String copyTableSql = info.getTableStrategy() == null ? null : getTableCopySQL(info, info.getTable(pk)); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " createTable sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - int rs = createTableDBAsync(info, copyTableSql, pk, sqls).join(); - return rs; - } else { - int rs = createTableDB(info, copyTableSql, pk, sqls); - return rs; - } - } - - @Override - public CompletableFuture createTableAsync(final Class clazz, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final String[] sqls = createTableSqls(info); - if (sqls == null) { - return CompletableFuture.completedFuture(-1); - } - String copyTableSql = info.getTableStrategy() == null ? null : getTableCopySQL(info, info.getTable(pk)); - if (copyTableSql == null) { - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " createTable sql=" + Arrays.toString(sqls)); - } - } else { - if (info.isLoggable(logger, Level.FINEST, copyTableSql)) { - logger.finest(info.getType().getSimpleName() + " createTable sql=" + copyTableSql); - } - } - if (isAsync()) { - return createTableDBAsync(info, copyTableSql, pk, sqls).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } - }); - } else { - return supplyAsync(() -> createTableDB(info, copyTableSql, pk, sqls)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } - }); - } - } - - //----------------------------- dropTable ----------------------------- - @Override - public int dropTable(Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return dropTableCache(info, node); - } - final String[] tables = info.getTables(node); - String[] sqls = dropTableSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " dropTable sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - int rs = dropTableDBAsync(info, tables, node, sqls).join(); - dropTableCache(info, node); - return rs; - } else { - int rs = dropTableDB(info, tables, node, sqls); - dropTableCache(info, node); - return rs; - } - } - - @Override - public CompletableFuture dropTableAsync(final Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(dropTableCache(info, node)); - } - final String[] tables = info.getTables(node); - String[] sqls = dropTableSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sqls[0])) { - logger.finest(info.getType().getSimpleName() + " dropTable sql=" + Arrays.toString(sqls)); - } - if (isAsync()) { - return dropTableDBAsync(info, tables, node, sqls).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - dropTableCache(info, node); - } - }); - } else { - return supplyAsync(() -> dropTableDB(info, tables, node, sqls)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - dropTableCache(info, node); - } - }); - } - } - - protected String[] dropTableSql(final EntityInfo info, String[] tables, final FilterNode node) { - List sqls = new ArrayList<>(); - for (String table : tables) { - sqls.add("DROP TABLE IF EXISTS " + table); - } - return sqls.toArray(new String[sqls.size()]); - } - - protected int clearTableCache(final EntityInfo info, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - return cache.clear(); - } - - protected int dropTableCache(final EntityInfo info, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - return cache.drop(); - } - - protected int deleteCache(final EntityInfo info, int count, Flipper flipper, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - Serializable[] ids = cache.delete(flipper, node); - return count >= 0 ? count : (ids == null ? 0 : ids.length); - } - - protected int deleteCache(final EntityInfo info, int count, Serializable... pks) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - int c = 0; - for (Serializable key : pks) { - c += cache.delete(key); - } - return count >= 0 ? count : c; - } - - protected static StringBuilder multiSplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) { - if (str == null) { - return sb; - } - int pos1 = str.indexOf(ch1, from); - if (pos1 < 0) { - return sb; - } - int pos2 = str.indexOf(ch2, from); - if (pos2 < 0) { - return sb; - } - if (sb.length() > 0) { - sb.append(split); - } - sb.append(str.substring(pos1 + 1, pos2)); - return multiSplit(ch1, ch2, split, sb, str, pos2 + 1); - } - - //---------------------------- update ---------------------------- - /** - * 更新对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 更新的数据条数 - */ - @Override - public int update(T... entitys) { - if (entitys.length == 0) { - return -1; - } - checkEntity("update", entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return updateCache(info, -1, entitys); - } - if (isAsync()) { - int rs = updateEntityDBAsync(info, entitys).join(); - updateCache(info, rs, entitys); - return rs; - } else { - int rs = updateEntityDB(info, entitys); - updateCache(info, rs, entitys); - return rs; - } - } - - @Override - public CompletableFuture updateAsync(final T... entitys) { - if (entitys.length == 0) { - return CompletableFuture.completedFuture(-1); - } - checkEntity("update", entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, entitys)); - } - if (isAsync()) { - return updateEntityDBAsync(info, entitys).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, entitys); - } - }); - } else { - return supplyAsync(() -> updateEntityDB(info, entitys)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, entitys); - } - }); - } - } - - /** - * 根据主键值更新对象的column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param pk 主键值 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(Class clazz, Serializable pk, String column, Serializable colval) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return updateCache(info, -1, pk, column, colval); - } - - UpdateSqlInfo sql = updateColumnSql(info, pk, column, colval); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, pk, column, colval); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, pk, column, colval); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable colval) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, pk, column, colval)); - } - - UpdateSqlInfo sql = updateColumnSql(info, pk, column, colval); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, column, colval); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, column, colval); - } - }); - } - } - - protected UpdateSqlInfo updateColumnSql(final EntityInfo info, Serializable pk, String column, final Serializable colval) { - Attribute attr = info.getAttribute(column); - Serializable val = getSQLAttrValue(info, attr, colval); - if (val instanceof byte[]) { - return new UpdateSqlInfo(true, "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter), (byte[]) val); - } else { - return new UpdateSqlInfo(false, "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" - + info.formatSQLValue(column, val, sqlFormatter) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter)); - } - } - - /** - * 根据主键值更新对象的column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * @param node 过滤node 不能为null - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(Class clazz, String column, Serializable colval, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return updateCache(info, -1, column, colval, node); - } - - UpdateSqlInfo sql = updateColumnSql(info, column, colval, node); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, column, colval, node); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, column, colval, node); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable colval, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, column, colval, node)); - } - UpdateSqlInfo sql = updateColumnSql(info, column, colval, node); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, column, colval, node); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, column, colval, node); - } - }); - } - } - - protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final String column, final Serializable colval, final FilterNode node) { - Map joinTabalis = node.getJoinTabalis(); - CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node.createSQLExpress(this, info, joinTabalis); - - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - Attribute attr = info.getAttribute(column); - Serializable val = getSQLAttrValue(info, attr, colval); - String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - String[] tables = info.getTables(node); - String sql; - if (val instanceof byte[]) { - sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) - + " SET " + info.getSQLColumn(alias, column) + "=" + prepareParamSign(1) - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - return new UpdateSqlInfo(true, sql, tables.length == 1 ? null : tables, (byte[]) val); - } else { - sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) - + " SET " + info.getSQLColumn(alias, column) + "=" + info.formatSQLValue(val, sqlFormatter) - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - return new UpdateSqlInfo(false, sql, tables.length == 1 ? null : tables); - } - } - - /** - * 根据主键值更新对象的多个column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param pk 主键值 - * @param values 字段值 - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values) { - if (values == null || values.length < 1) { - return -1; - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return updateCache(info, -1, pk, values); - } - - UpdateSqlInfo sql = updateColumnSql(info, pk, values); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, pk, values); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, pk, values); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values) { - if (values == null || values.length < 1) { - return CompletableFuture.completedFuture(-1); - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, pk, values)); - } - UpdateSqlInfo sql = updateColumnSql(info, pk, values); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, values); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, values); - } - }); - } - } - - protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final Serializable pk, final ColumnValue... values) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - for (ColumnValue col : values) { - if (col == null) { - continue; - } - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) { - throw new SourceException(info.getType() + " cannot found column " + col.getColumn()); - } - if (setsql.length() > 0) { - setsql.append(", "); - } - String sqlColumn = info.getSQLColumn(null, col.getColumn()); - if (col.getValue() instanceof byte[]) { - if (blobs == null) { - blobs = new ArrayList<>(); - } - blobs.add((byte[]) col.getValue()); - setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); - } else { - setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); - } - } - if (setsql.length() < 1) { - throw new SourceException("update non column-value array"); - } - String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - return new UpdateSqlInfo(false, sql, blobs); - } - - @Override - public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - if (values == null || values.length < 1) { - return -1; - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return updateCache(info, -1, node, flipper, values); - } - UpdateSqlInfo sql = updateColumnSql(info, node, flipper, values); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, node, flipper, values); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, node, flipper, values); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - if (values == null || values.length < 1) { - return CompletableFuture.completedFuture(-1); - } - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, node, flipper, values)); - } - UpdateSqlInfo sql = updateColumnSql(info, node, flipper, values); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, node, flipper, values); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, node, flipper, values); - } - }); - } - } - - protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - boolean pgsql = "postgresql".equals(dbtype()); - String alias = pgsql ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - for (ColumnValue col : values) { - if (col == null) { - continue; - } - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) { - continue; - } - if (setsql.length() > 0) { - setsql.append(", "); - } - String sqlColumn = info.getSQLColumn(alias, col.getColumn()); - if (col.getValue() instanceof byte[]) { - if (blobs == null) { - blobs = new ArrayList<>(); - } - blobs.add((byte[]) col.getValue()); - setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); - } else { - setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); - } - } - if (setsql.length() < 1) { - throw new SourceException("update non column-value array"); - } - Map joinTabalis = node == null ? null : node.getJoinTabalis(); - CharSequence join = node == null ? null : node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String sql; - String[] tables = info.getTables(node); - if (pgsql && flipper != null && flipper.getLimit() > 0) { - String wherestr = ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql - + " WHERE " + info.getPrimarySQLColumn() + " IN (SELECT " + info.getPrimaryField() + " FROM " + tables[0] - + wherestr + info.createSQLOrderby(flipper) + " OFFSET 0 LIMIT " + flipper.getLimit() + ")"; - - } else { - sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) - + info.createSQLOrderby(flipper) - + (("mysql".equals(dbtype()) && flipper != null && flipper.getLimit() > 0) ? (" LIMIT " + flipper.getLimit()) : ""); - } - return new UpdateSqlInfo(blobs != null, sql, tables.length == 1 ? null : tables, blobs); - } - - //返回不存在的字段名,null表示字段都合法; - protected String checkIllegalColumn(final EntityInfo info, SelectColumn selects) { - if (selects == null) { - return null; - } - String[] columns = selects.getColumns(); - if (columns == null) { - return null; - } - for (String col : columns) { - if (info.getAttribute(col) == null) { - return col; - } - } - return null; - } - - @Override - public int updateColumn(final T entity, final SelectColumn selects) { - if (entity == null || selects == null) { - return -1; - } - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - String illegalColumn = checkIllegalColumn(info, selects); - if (illegalColumn != null) { - throw new SourceException(info.getType() + " cannot found column " + illegalColumn); - } - if (isOnlyCache(info)) { - return updateCache(info, -1, false, entity, null, selects); - } - - UpdateSqlInfo sql = updateColumnSql(info, false, entity, null, selects); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, false, entity, null, selects); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, false, entity, null, selects); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { - if (entity == null || selects == null) { - return CompletableFuture.completedFuture(-1); - } - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - String illegalColumn = checkIllegalColumn(info, selects); - if (illegalColumn != null) { - return CompletableFuture.failedFuture(new SourceException(info.getType() + " cannot found column " + illegalColumn)); - } - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, false, entity, null, selects)); - } - - UpdateSqlInfo sql = updateColumnSql(info, false, entity, null, selects); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, false, entity, null, selects); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, false, entity, null, selects); - } - }); - } - } - - @Override - public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects) { - if (entity == null || node == null || selects == null) { - return -1; - } - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - String illegalColumn = checkIllegalColumn(info, selects); - if (illegalColumn != null) { - throw new SourceException(info.getType() + " cannot found column " + illegalColumn); - } - if (isOnlyCache(info)) { - return updateCache(info, -1, true, entity, node, selects); - } - - UpdateSqlInfo sql = updateColumnSql(info, true, entity, node, selects); - if (isAsync()) { - int rs = updateColumnDBAsync(info, null, sql).join(); - updateCache(info, rs, true, entity, node, selects); - return rs; - } else { - int rs = updateColumnDB(info, null, sql); - updateCache(info, rs, true, entity, node, selects); - return rs; - } - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects) { - if (entity == null || node == null || selects == null) { - return CompletableFuture.completedFuture(-1); - } - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - String illegalColumn = checkIllegalColumn(info, selects); - if (illegalColumn != null) { - return CompletableFuture.failedFuture(new SourceException(info.getType() + " cannot found column " + illegalColumn)); - } - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, true, entity, node, selects)); - } - - UpdateSqlInfo sql = updateColumnSql(info, true, entity, node, selects); - if (isAsync()) { - return updateColumnDBAsync(info, null, sql).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, true, entity, node, selects); - } - }); - } else { - return supplyAsync(() -> updateColumnDB(info, null, sql)).whenComplete((rs, t) -> { - if (t != null) { - errorCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, true, entity, node, selects); - } - }); - } - } - - protected UpdateSqlInfo updateColumnSql(final EntityInfo info, final boolean needNode, final T entity, final FilterNode node, final SelectColumn selects) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - for (Attribute attr : info.updateAttributes) { - if (!selects.test(attr.field())) { - continue; - } - if (setsql.length() > 0) { - setsql.append(", "); - } - setsql.append(info.getSQLColumn(alias, attr.field())); - Serializable val = info.getFieldValue(attr, entity); - if (val instanceof byte[]) { - if (blobs == null) { - blobs = new ArrayList<>(); - } - blobs.add((byte[]) val); - setsql.append("=").append(prepareParamSign(++index)); - } else { - CharSequence sqlval = info.formatSQLValue(val, sqlFormatter); - if (sqlval == null && info.isNotNullJson(attr)) { - sqlval = "''"; - } - setsql.append("=").append(sqlval); - } - } - if (needNode) { - Map joinTabalis = node.getJoinTabalis(); - CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node.createSQLExpress(this, info, joinTabalis); - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multiSplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multiSplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String sql; - String[] tables = info.getTables(node); - sql = "UPDATE " + tables[0] + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - return new UpdateSqlInfo(blobs != null, sql, tables.length == 1 ? null : tables, blobs); - } else { - final Serializable id = (Serializable) info.getSQLValue(info.getPrimary(), entity); - String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(id, sqlFormatter); - return new UpdateSqlInfo(blobs != null, sql, blobs); - } - } - - protected int updateCache(final EntityInfo info, int count, final boolean needNode, final T entity, final FilterNode node, final SelectColumn selects) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return count; - } - final List> attrs = new ArrayList<>(); - for (Attribute attr : info.updateAttributes) { - if (!selects.test(attr.field())) { - continue; - } - attrs.add(attr); - } - if (needNode) { - T[] rs = cache.update(entity, attrs, node); - return count >= 0 ? count : (rs == null ? 0 : rs.length); - } else { - T rs = cache.update(entity, attrs); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - } - - protected int updateCache(final EntityInfo info, int count, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return count; - } - final List> attrs = new ArrayList<>(); - final List cols = new ArrayList<>(); - for (ColumnValue col : values) { - if (col == null) { - continue; - } - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) { - continue; - } - attrs.add(attr); - cols.add(col); - } - T[] rs = cache.updateColumn(node, flipper, attrs, cols); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, final Serializable pk, final ColumnValue... values) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return count; - } - final List> attrs = new ArrayList<>(); - final List cols = new ArrayList<>(); - for (ColumnValue col : values) { - if (col == null) { - continue; - } - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) { - continue; - } - attrs.add(attr); - cols.add(col); - } - T rs = cache.updateColumn(pk, attrs, cols); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, String column, final Serializable colval, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return count; - } - T[] rs = cache.update(info.getAttribute(column), colval, node); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, final Serializable pk, final String column, final Serializable colval) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return count; - } - T rs = cache.update(pk, info.getAttribute(column), colval); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, T... entitys) { - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - int c2 = 0; - for (final T value : entitys) { - c2 += cache.update(value); - } - return count >= 0 ? count : c2; - } - - public int reloadCache(Class clazz, Serializable... pks) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache == null) { - return -1; - } - String column = info.getPrimary().field(); - int c = 0; - for (Serializable id : pks) { - Sheet sheet = querySheet(false, true, false, clazz, null, FLIPPER_ONE, FilterNode.create(column, id)); - T value = sheet.isEmpty() ? null : sheet.list().get(0); - if (value != null) { - c += cache.update(value); - } - } - return c; - } - - //------------------------- getNumberMapCompose ------------------------- - @Override - public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - final Map map = new HashMap<>(); - if (node == null || isCacheUseable(node, this)) { - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node)); - } - } - return map; - } - } - final String[] tables = info.getTables(node); - String sql = getNumberMapSql(info, tables, node, columns); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " getNumberMap sql=" + sql); - } - if (isAsync()) { - return (Map) getNumberMapDBAsync(info, tables, sql, node, columns).join(); - } else { - return getNumberMapDB(info, tables, sql, node, columns); - } - } - - @Override - public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - final Map map = new HashMap<>(); - if (node == null || isCacheUseable(node, this)) { - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - map.put(ffc.col(col), cache.getNumberResult(ffc.getFunc(), ffc.getDefvalue(), col, node)); - } - } - return CompletableFuture.completedFuture(map); - } - } - final String[] tables = info.getTables(node); - String sql = getNumberMapSql(info, tables, node, columns); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " getNumberMap sql=" + sql); - } - if (isAsync()) { - return getNumberMapDBAsync(info, tables, sql, node, columns); - } else { - return supplyAsync(() -> getNumberMapDB(info, tables, sql, node, columns)); - } - } - - protected String getNumberMapSql(final EntityInfo info, final String[] tables, final FilterNode node, final FilterFuncColumn... columns) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - StringBuilder sb = new StringBuilder(); - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - if (sb.length() > 0) { - sb.append(", "); - } - sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col)))); - } - } - final String sql = "SELECT " + sb + " FROM " + tables[0] + " a" - + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - return sql; - } - - @Local - protected CompletableFuture> getNumberMapDBApply(EntityInfo info, CompletableFuture future, FilterFuncColumn... columns) { - return future.thenApply((DataResultSet dataset) -> { - final Map map = new HashMap<>(); - if (dataset.next()) { - int index = 0; - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - Object o = dataset.getObject(++index); - Number rs = ffc.getDefvalue(); - if (o != null) { - rs = (Number) o; - } - map.put(ffc.col(col), rs); - } - } - } - dataset.close(); - return map; - }); - } - - //------------------------ getNumberResultCompose ----------------------- - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return cache.getNumberResult(func, defVal, column, node); - } - } - - final String[] tables = info.getTables(node); - String sql = getNumberResultSql(info, entityClass, tables, func, defVal, column, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " getNumberResult sql=" + sql); - } - if (isAsync()) { - return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node).join(); - } else { - return getNumberResultDB(info, tables, sql, func, defVal, column, node); - } - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return CompletableFuture.completedFuture(cache.getNumberResult(func, defVal, column, node)); - } - } - final String[] tables = info.getTables(node); - String sql = getNumberResultSql(info, entityClass, tables, func, defVal, column, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " getNumberResult sql=" + sql); - } - if (isAsync()) { - return getNumberResultDBAsync(info, tables, sql, func, defVal, column, node); - } else { - return supplyAsync(() -> getNumberResultDB(info, tables, sql, func, defVal, column, node)); - } - } - - protected String getNumberResultSql(final EntityInfo info, final Class entityClass, final String[] tables, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + tables[0] + " a" - + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - return sql; - } - - @Local - protected CompletableFuture getNumberResultDBApply(EntityInfo info, CompletableFuture future, Number defVal, String column) { - return future.thenApply((DataResultSet dataset) -> { - Number rs = defVal; - if (dataset.next()) { - Object o = dataset.getObject(1); - if (o != null) { - rs = (Number) o; - } - } - dataset.close(); - return rs; - }); - } - - //------------------------ queryColumnMapCompose ------------------------ - @Override - public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return cache.queryColumnMap(keyColumn, func, funcColumn, node); - } - } - - final String[] tables = info.getTables(node); - String sql = queryColumnMapSql(info, tables, keyColumn, func, funcColumn, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); - } - if (isAsync()) { - return (Map) queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node).join(); - } else { - return queryColumnMapDB(info, tables, sql, keyColumn, func, funcColumn, node); - } - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return CompletableFuture.completedFuture(cache.queryColumnMap(keyColumn, func, funcColumn, node)); - } - } - final String[] tables = info.getTables(node); - String sql = queryColumnMapSql(info, tables, keyColumn, func, funcColumn, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); - } - if (isAsync()) { - return queryColumnMapDBAsync(info, tables, sql, keyColumn, func, funcColumn, node); - } else { - return supplyAsync(() -> queryColumnMapDB(info, tables, sql, keyColumn, func, funcColumn, node)); - } - } - - protected String queryColumnMapSql(final EntityInfo info, final String[] tables, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final String keySqlColumn = info.getSQLColumn(null, keyColumn); - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - final String funcSqlColumn = func == null ? info.getSQLColumn("a", funcColumn) : func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn))); - - String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - String sql; - if (tables.length == 1) { - sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn + " FROM " + tables[0] + " a" + joinAndWhere; - } else { - int b = 0; - StringBuilder union = new StringBuilder(); - for (String table : tables) { - if (union.length() > 0) { - union.append(" UNION ALL "); - } - union.append("SELECT a.").append(keySqlColumn).append(", ").append(funcSqlColumn) - .append(" FROM ").append(table).append(" a").append(joinAndWhere); - } - sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn + " FROM (" + (union) + ") a"; - } - return sql; - } - - @Local - protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final String keyColumn) { - return future.thenApply((DataResultSet dataset) -> { - Map rs = new LinkedHashMap<>(); - while (dataset.next()) { - rs.put((K) dataset.getObject(1), (N) dataset.getObject(2)); - } - dataset.close(); - return rs; - }); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { - Map map = queryColumnMap(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); - final Map rs = new LinkedHashMap<>(); - map.forEach((keys, values) -> rs.put(keys[0], values)); - return rs; - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { - CompletableFuture> future = queryColumnMapAsync(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); - return future.thenApply(map -> { - final Map rs = new LinkedHashMap<>(); - map.forEach((keys, values) -> rs.put(keys[0], values)); - return rs; - }); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return cache.queryColumnMap(funcNodes, groupByColumns, node); - } - } - final String[] tables = info.getTables(node); - String sql = queryColumnMapSql(info, tables, funcNodes, groupByColumns, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); - } - if (isAsync()) { - return (Map) queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node).join(); - } else { - return queryColumnMapDB(info, tables, sql, funcNodes, groupByColumns, node); - } - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || isCacheUseable(node, this)) { - return CompletableFuture.completedFuture(cache.queryColumnMap(funcNodes, groupByColumns, node)); - } - } - final String[] tables = info.getTables(node); - String sql = queryColumnMapSql(info, tables, funcNodes, groupByColumns, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " queryColumnMap sql=" + sql); - } - if (isAsync()) { - return queryColumnMapDBAsync(info, tables, sql, funcNodes, groupByColumns, node); - } else { - return supplyAsync(() -> queryColumnMapDB(info, tables, sql, funcNodes, groupByColumns, node)); - } - } - - protected String queryColumnMapSql(final EntityInfo info, final String[] tables, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final StringBuilder groupBySqlColumns = new StringBuilder(); - if (groupByColumns != null && groupByColumns.length > 0) { - for (int i = 0; i < groupByColumns.length; i++) { - if (groupBySqlColumns.length() > 0) { - groupBySqlColumns.append(", "); - } - groupBySqlColumns.append(info.getSQLColumn("a", groupByColumns[i])); - } - } - final StringBuilder funcSqlColumns = new StringBuilder(); - for (int i = 0; i < funcNodes.length; i++) { - if (funcSqlColumns.length() > 0) { - funcSqlColumns.append(", "); - } - if (funcNodes[i] instanceof ColumnFuncNode) { - funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnFuncNode) funcNodes[i], sqlFormatter)); - } else { - funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnNodeValue) funcNodes[i], sqlFormatter)); - } - } - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - - String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - String sql; - if (tables.length == 1) { - sql = "SELECT "; - if (groupBySqlColumns.length() > 0) { - sql += groupBySqlColumns + ", "; - } - sql += funcSqlColumns + " FROM " + tables[0] + " a" + joinAndWhere; - } else { - StringBuilder union = new StringBuilder(); - for (String table : tables) { - if (union.length() > 0) { - union.append(" UNION ALL "); - } - String subsql = "SELECT "; - if (groupBySqlColumns.length() > 0) { - subsql += groupBySqlColumns.toString() + ", "; - } - subsql += funcSqlColumns.toString() + " FROM " + table + " a" + joinAndWhere; - union.append(subsql); - } - sql = "SELECT "; - if (groupBySqlColumns.length() > 0) { - sql += groupBySqlColumns + ", "; - } - sql += funcSqlColumns + " FROM (" + (union) + ") a"; - } - if (groupBySqlColumns.length() > 0) { - sql += " GROUP BY " + groupBySqlColumns; - } - return sql; - } - - @Local - protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return future.thenApply((DataResultSet dataset) -> { - Map rs = new LinkedHashMap<>(); - while (dataset.next()) { - int index = 0; - Serializable[] keys = new Serializable[groupByColumns.length]; - for (int i = 0; i < keys.length; i++) { - keys[i] = (Serializable) dataset.getObject(++index); - } - Number[] vals = new Number[funcNodes.length]; - for (int i = 0; i < vals.length; i++) { - vals[i] = (Number) dataset.getObject(++index); - } - rs.put(keys, vals); - } - dataset.close(); - return rs; - }); - } - - //----------------------------- find ----------------------------- - @Override - public T[] finds(Class clazz, final SelectColumn selects, Serializable... pks) { - return findsAsync(clazz, selects, pks).join(); - } - - @Override - public CompletableFuture findsAsync(Class clazz, final SelectColumn selects, Serializable... pks) { - final EntityInfo info = loadEntityInfo(clazz); - if (pks == null || pks.length == 0) { - return CompletableFuture.completedFuture(info.getArrayer().apply(0)); - } - final EntityCache cache = info.getCache(); - if (cache != null) { - T[] rs = selects == null ? cache.finds(pks) : cache.finds(selects, pks); - if (cache.isFullLoaded() || rs != null) { - return CompletableFuture.completedFuture(rs); - } - } - return findsDBAsync(info, selects, pks); - } - - protected CompletableFuture findsDBAsync(final EntityInfo info, final SelectColumn selects, Serializable... pks) { - final Attribute primary = info.getPrimary(); - return queryListAsync(info.getType(), selects, null, FilterNode.create(info.getPrimarySQLColumn(), FilterExpress.IN, pks)).thenApply(list -> { - T[] rs = info.getArrayer().apply(pks.length); - for (int i = 0; i < rs.length; i++) { - T t = null; - Serializable pk = pks[i]; - for (T item : list) { - if (pk.equals(primary.get(item))) { - t = item; - break; - } - } - rs[i] = t; - } - return rs; - }); - } - - @Override - public List findsList(Class clazz, Stream pks) { - return findsListAsync(clazz, pks).join(); - } - - @Override - public CompletableFuture> findsListAsync(final Class clazz, final Stream pks) { - final EntityInfo info = loadEntityInfo(clazz); - Serializable[] ids = pks.toArray(v -> new Serializable[v]); - return queryListAsync(info.getType(), null, null, FilterNode.create(info.getPrimarySQLColumn(), FilterExpress.IN, ids)); - } - - @Override - public T find(Class clazz, final SelectColumn selects, Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); - if (cache.isFullLoaded() || rs != null) { - return rs; - } - } - String[] tables = info.getTableOneArray(pk); - String sql = findSql(info, selects, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - } - if (isAsync()) { - return findDBAsync(info, tables, sql, true, selects, pk, null).join(); - } else { - return findDB(info, tables, sql, true, selects, pk, null); - } - } - - @Override - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); - if (cache.isFullLoaded() || rs != null) { - return CompletableFuture.completedFuture(rs); - } - } - String[] tables = info.getTableOneArray(pk); - String sql = findSql(info, selects, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - } - if (isAsync()) { - return findDBAsync(info, tables, sql, true, selects, pk, null); - } else { - return supplyAsync(() -> findDB(info, tables, sql, true, selects, pk, null)); - } - } - - protected String findSql(final EntityInfo info, final SelectColumn selects, Serializable pk) { - String column = info.getPrimarySQLColumn(); - final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter); - return sql; - } - - @Override - public T find(final Class clazz, final SelectColumn selects, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) { - return cache.find(selects, node); - } - final String[] tables = info.getTables(node); - String sql = findSql(info, tables, selects, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - } - if (isAsync()) { - return findDBAsync(info, tables, sql, false, selects, null, node).join(); - } else { - return findDB(info, tables, sql, false, selects, null, node); - } - } - - @Override - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) { - return CompletableFuture.completedFuture(cache.find(selects, node)); - } - final String[] tables = info.getTables(node); - String sql = findSql(info, tables, selects, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - } - if (isAsync()) { - return findDBAsync(info, tables, sql, false, selects, null, node); - } else { - return supplyAsync(() -> findDB(info, tables, sql, false, selects, null, node)); - } - } - - protected String findSql(final EntityInfo info, final String[] tables, final SelectColumn selects, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - String sql; - if (tables.length == 1) { - sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + tables[0] + " a" + joinAndWhere; - } else { - StringBuilder union = new StringBuilder(); - for (String table : tables) { - if (union.length() > 0) { - union.append(" UNION ALL "); - } - union.append("SELECT ").append(info.getQueryColumns("a", selects)).append(" FROM ").append(table).append(" a").append(joinAndWhere); - } - sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM (" + (union) + ") a"; - } - return sql; - } - - @Local - protected CompletableFuture findDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, SelectColumn selects) { - return future.thenApply((DataResultSet pgset) -> { - T rs = pgset.next() ? (onlypk && selects == null ? getEntityValue(info, null, pgset) : getEntityValue(info, selects, pgset)) : null; - pgset.close(); - return rs; - }); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, pk); - if (cache.isFullLoaded() || val != null) { - return val; - } - } - String[] tables = info.getTableOneArray(pk); - String sql = findColumnSql(info, column, defValue, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); - } - if (isAsync()) { - return findColumnDBAsync(info, tables, sql, true, column, defValue, pk, null).join(); - } else { - return findColumnDB(info, tables, sql, true, column, defValue, pk, null); - } - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, pk); - if (cache.isFullLoaded() || val != null) { - return CompletableFuture.completedFuture(val); - } - } - String[] tables = info.getTableOneArray(pk); - String sql = findColumnSql(info, column, defValue, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); - } - if (isAsync()) { - return findColumnDBAsync(info, tables, sql, true, column, defValue, pk, null); - } else { - return supplyAsync(() -> findColumnDB(info, tables, sql, true, column, defValue, pk, null)); - } - } - - protected String findColumnSql(final EntityInfo info, String column, final Serializable defValue, final Serializable pk) { - return "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, node); - if (cache.isFullLoaded() || val != null) { - return val; - } - } - final String[] tables = info.getTables(node); - String sql = findColumnSql(info, tables, column, defValue, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); - } - if (isAsync()) { - return findColumnDBAsync(info, tables, sql, false, column, defValue, null, node).join(); - } else { - return findColumnDB(info, tables, sql, false, column, defValue, null, node); - } - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, node); - if (cache.isFullLoaded() || val != null) { - return CompletableFuture.completedFuture(val); - } - } - final String[] tables = info.getTables(node); - String sql = findColumnSql(info, tables, column, defValue, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " findColumn sql=" + sql); - } - if (isAsync()) { - return findColumnDBAsync(info, tables, sql, false, column, defValue, null, node); - } else { - return supplyAsync(() -> findColumnDB(info, tables, sql, false, column, defValue, null, node)); - } - } - - protected String findColumnSql(final EntityInfo info, String[] tables, String column, final Serializable defValue, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - String sql; - if (tables.length == 1) { - sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + tables[0] + " a" + joinAndWhere; - } else { - StringBuilder union = new StringBuilder(); - for (String table : tables) { - if (union.length() > 0) { - union.append(" UNION ALL "); - } - union.append("SELECT ").append(info.getSQLColumn("a", column)).append(" FROM ").append(table).append(" a").append(joinAndWhere); - } - sql = "SELECT " + info.getSQLColumn("a", column) + " FROM (" + (union) + ") a"; - } - return sql; - } - - @Local - protected CompletableFuture findColumnDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, String column, Serializable defValue) { - return future.thenApply((DataResultSet dataset) -> { - Serializable val = defValue; - if (dataset.next()) { - final Attribute attr = info.getAttribute(column); - val = dataset.getObject(attr, 1, null); - } - dataset.close(); - return val == null ? defValue : val; - }); - } - - //---------------------------- existsCompose ---------------------------- - @Override - public boolean exists(Class clazz, Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(pk); - if (rs || cache.isFullLoaded()) { - return rs; - } - } - String[] tables = info.getTableOneArray(pk); - String sql = existsSql(info, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - } - if (isAsync()) { - return existsDBAsync(info, tables, sql, true, pk, null).join(); - } else { - return existsDB(info, tables, sql, true, pk, null); - } - } - - @Override - public CompletableFuture existsAsync(final Class clazz, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(pk); - if (rs || cache.isFullLoaded()) { - return CompletableFuture.completedFuture(rs); - } - } - String[] tables = info.getTableOneArray(pk); - String sql = existsSql(info, pk); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - } - if (isAsync()) { - return existsDBAsync(info, tables, sql, true, pk, null); - } else { - return supplyAsync(() -> existsDB(info, tables, sql, true, pk, null)); - } - } - - protected String existsSql(final EntityInfo info, Serializable pk) { - return "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - } - - @Override - public boolean exists(final Class clazz, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(node); - if (rs || cache.isFullLoaded()) { - return rs; - } - } - final String[] tables = info.getTables(node); - String sql = existsSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - } - if (isAsync()) { - return existsDBAsync(info, tables, sql, false, null, node).join(); - } else { - return existsDB(info, tables, sql, false, null, node); - } - } - - @Override - public CompletableFuture existsAsync(final Class clazz, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(node); - if (rs || cache.isFullLoaded()) { - return CompletableFuture.completedFuture(rs); - } - } - - final String[] tables = info.getTables(node); - String sql = existsSql(info, tables, node); - if (info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - } - if (isAsync()) { - return existsDBAsync(info, tables, sql, false, null, node); - } else { - return supplyAsync(() -> existsDB(info, tables, sql, false, null, node)); - } - } - - protected String existsSql(final EntityInfo info, String[] tables, FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); - String joinAndWhere = (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - String sql; - if (tables.length == 1) { - sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + tables[0] + " a" + joinAndWhere; - } else { - StringBuilder union = new StringBuilder(); - for (String table : tables) { - if (union.length() > 0) { - union.append(" UNION ALL "); - } - union.append("SELECT ").append(info.getPrimarySQLColumn("a")).append(" FROM ").append(table).append(" a").append(joinAndWhere); - } - sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM (" + (union) + ") a"; - } - return sql; - } - - @Local - protected CompletableFuture existsDBApply(EntityInfo info, CompletableFuture future, boolean onlypk) { - return future.thenApply((DataResultSet pgset) -> { - boolean rs = pgset.next() ? (((Number) pgset.getObject(1)).intValue() > 0) : false; - pgset.close(); - return rs; - }); - } - - //-----------------------list set---------------------------- - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - final Set list = querySet(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final Set rs = new LinkedHashSet<>(); - if (list.isEmpty()) { - return rs; - } - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return querySetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Set list) -> { - final Set rs = new LinkedHashSet<>(); - if (list.isEmpty()) { - return rs; - } - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - }); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - final List list = queryList(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final List rs = new ArrayList<>(); - if (list.isEmpty()) { - return rs; - } - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return queryListAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((List list) -> { - final List rs = new ArrayList<>(); - if (list.isEmpty()) { - return rs; - } - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - }); - } - - @Override - public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - Sheet sheet = querySheet(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final Sheet rs = new Sheet<>(); - if (sheet.isEmpty()) { - return rs; - } - rs.setTotal(sheet.getTotal()); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - final List list = new ArrayList<>(); - for (T t : sheet.getRows()) { - list.add(selected.get(t)); - } - rs.setRows(list); - return rs; - } - - @Override - public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Sheet sheet) -> { - final Sheet rs = new Sheet<>(); - if (sheet.isEmpty()) { - return rs; - } - rs.setTotal(sheet.getTotal()); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - final List list = new ArrayList<>(); - for (T t : sheet.getRows()) { - list.add(selected.get(t)); - } - rs.setRows(list); - return rs; - }); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
- * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
- * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param keyStream 主键Stream - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream) { - if (keyStream == null) { - return new LinkedHashMap<>(); - } - final EntityInfo info = loadEntityInfo(clazz); - final ArrayList ids = new ArrayList<>(); - keyStream.forEach(k -> ids.add(k)); - final Attribute primary = info.getPrimary(); - List rs = queryList(clazz, FilterNode.create(primary.field(), ids)); - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) { - return new LinkedHashMap<>(); - } - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream) { - if (keyStream == null) { - return CompletableFuture.completedFuture(new LinkedHashMap<>()); - } - final EntityInfo info = loadEntityInfo(clazz); - final ArrayList pks = new ArrayList<>(); - keyStream.forEach(k -> pks.add(k)); - final Attribute primary = info.getPrimary(); - return queryListAsync(clazz, FilterNode.create(primary.field(), pks)).thenApply((List rs) -> { - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) { - return new LinkedHashMap<>(); - } - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - }); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
- * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
- * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node FilterNode - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node) { - List rs = queryList(clazz, selects, node); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) { - return new LinkedHashMap<>(); - } - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - return queryListAsync(clazz, selects, node).thenApply((List rs) -> { - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) { - return new LinkedHashMap<>(); - } - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - }); - } - - @Override - public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))).join(); - } else { - return new LinkedHashSet<>(querySheet(true, false, true, clazz, selects, flipper, node).list(true)); - } - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); - } else { - return supplyAsync(() -> querySheet(true, false, true, clazz, selects, flipper, node)).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); - } - } - - @Override - public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)).join(); - } else { - return querySheet(true, false, false, clazz, selects, flipper, node).list(true); - } - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)); - } else { - return supplyAsync(() -> querySheet(true, false, false, clazz, selects, flipper, node)).thenApply((rs) -> rs.list(true)); - } - } - - @Override - public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, true, false, clazz, selects, flipper, node).join(); - } else { - return querySheet(true, true, false, clazz, selects, flipper, node); - } - } - - @Override - public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) { - return querySheetAsync(true, true, false, clazz, selects, flipper, node); - } else { - return supplyAsync(() -> querySheet(true, true, false, clazz, selects, flipper, node)); - } - } - - protected Sheet querySheet(final boolean readCache, final boolean needTotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (readCache && cache != null && cache.isFullLoaded()) { - if (node == null || isCacheUseable(node, this)) { - if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) { - logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : createPredicate(node, cache))); - } - return cache.querySheet(needTotal, distinct, selects, flipper, node); - } - } - return querySheetDB(info, readCache, needTotal, distinct, selects, flipper, node); - } - - protected CompletableFuture> querySheetAsync(final boolean readCache, final boolean needTotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (readCache && cache != null && cache.isFullLoaded()) { - if (node == null || isCacheUseable(node, this)) { - if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) { - logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : createPredicate(node, cache))); - } - return CompletableFuture.completedFuture(cache.querySheet(needTotal, distinct, selects, flipper, node)); - } - } - return querySheetDBAsync(info, readCache, needTotal, distinct, selects, flipper, node); - } - - protected static class UpdateSqlInfo { - - public String sql; //prepare-sql时表名参数只能是最后一个 - - public String[] tables; //存在值则长度必然大于1,sql为[0]构建的sql - - public List blobs; //要么null,要么有内容,不能是empty-list - - public boolean prepare; //是否PreparedStatement SQL - - public UpdateSqlInfo(boolean prepare, String sql, byte[]... blobs) { - this(prepare, sql, null, blobs); - } - - public UpdateSqlInfo(boolean prepare, String sql, List blobs) { - this(prepare, sql, null, blobs); - } - - public UpdateSqlInfo(boolean prepare, String sql, String[] tables, byte[]... blobs) { - this.prepare = prepare; - this.sql = sql; - this.tables = tables; - if (blobs.length > 0) { - this.blobs = new ArrayList<>(); - for (byte[] bs : blobs) { - this.blobs.add(bs); - } - } - } - - public UpdateSqlInfo(boolean prepare, String sql, String[] tables, List blobs) { - this.prepare = prepare; - this.sql = sql; - this.tables = tables; - this.blobs = blobs == null || blobs.isEmpty() ? null : blobs; - } - - } - - protected static class PrepareInfo { - - public String prepareSql; - - public List entitys; - - public PrepareInfo(String prepareSql) { - this.prepareSql = prepareSql; - } - - public void addEntity(T entity) { - if (entitys == null) { - entitys = new ArrayList<>(); - } - entitys.add(entity); - } - } -} +/* + * + */ +package org.redkale.source; + +import java.util.function.Function; + +/** + * + * 关系型数据库的数据源, 接口与DataSource基本一致。
+ * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.8.0 + */ +public interface DataSqlSource extends DataSource { + + public int directExecute(String sql); + + public int[] directExecute(String... sqls); + + public V directQuery(String sql, Function handler); +} diff --git a/src/main/java/org/redkale/source/FilterJoinNode.java b/src/main/java/org/redkale/source/FilterJoinNode.java index 9227fbea4..6e1de21f1 100644 --- a/src/main/java/org/redkale/source/FilterJoinNode.java +++ b/src/main/java/org/redkale/source/FilterJoinNode.java @@ -126,7 +126,7 @@ public class FilterJoinNode extends FilterNode { } @Override - protected CharSequence createSQLExpress(DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + protected CharSequence createSQLExpress(AbstractDataSqlSource source, final EntityInfo info, final Map joinTabalis) { return super.createSQLExpress(source, this.joinEntity == null ? info : this.joinEntity, joinTabalis); } diff --git a/src/main/java/org/redkale/source/FilterNode.java b/src/main/java/org/redkale/source/FilterNode.java index ed2cd7111..323d478d7 100644 --- a/src/main/java/org/redkale/source/FilterNode.java +++ b/src/main/java/org/redkale/source/FilterNode.java @@ -297,14 +297,14 @@ public class FilterNode { //FilterNode 不能实现Serializable接口, 否则 /** * 该方法需要重载 * - * @param source DataSqlSource + * @param source AbstractDataSqlSource * @param Entity类的泛型 * @param joinTabalis 关联表的集合 * @param info EntityInfo * * @return JOIN的SQL语句 */ - protected CharSequence createSQLExpress(DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + protected CharSequence createSQLExpress(AbstractDataSqlSource source, final EntityInfo info, final Map joinTabalis) { CharSequence sb0 = this.column == null || this.column.isEmpty() || this.column.charAt(0) == '#' || info == null ? null : createElementSQLExpress(source, info, joinTabalis == null ? null : joinTabalis.get(info.getType())); if (this.nodes == null) { @@ -396,7 +396,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口, 否则 return items; } - protected final CharSequence createElementSQLExpress(DataSqlSource source, final EntityInfo info, String talis) { + protected final CharSequence createElementSQLExpress(AbstractDataSqlSource source, final EntityInfo info, String talis) { final Object val0 = getValue(); if (needSplit(val0)) { if (val0 instanceof Collection) { @@ -452,7 +452,7 @@ public class FilterNode { //FilterNode 不能实现Serializable接口, 否则 } - private CharSequence createElementSQLExpress(DataSqlSource source, final EntityInfo info, String talis, Object val0) { + private CharSequence createElementSQLExpress(AbstractDataSqlSource source, final EntityInfo info, String talis, Object val0) { if (column == null || this.column.isEmpty() || this.column.charAt(0) == '#') { return null; } diff --git a/src/test/java/org/redkale/test/source/FilterNodeTest.java b/src/test/java/org/redkale/test/source/FilterNodeTest.java index 78372ed6f..ad398ff33 100644 --- a/src/test/java/org/redkale/test/source/FilterNodeTest.java +++ b/src/test/java/org/redkale/test/source/FilterNodeTest.java @@ -71,9 +71,9 @@ public class FilterNodeTest { } } - private static CharSequence createSQLExpress(FilterNode node, DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + private static CharSequence createSQLExpress(FilterNode node, AbstractDataSqlSource source, final EntityInfo info, final Map joinTabalis) { try { - Method method = FilterNode.class.getDeclaredMethod("createSQLExpress", DataSqlSource.class, EntityInfo.class, Map.class); + Method method = FilterNode.class.getDeclaredMethod("createSQLExpress", AbstractDataSqlSource.class, EntityInfo.class, Map.class); method.setAccessible(true); return (CharSequence) method.invoke(node, source, info, joinTabalis); } catch (Exception e) {