All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkalex.source.mysql.MysqlDataSource Maven / Gradle / Ivy

There is a newer version: 2.7.7
Show newest version
/*
 * 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.redkalex.source.mysql;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.Level;
import java.util.stream.Stream;
import org.redkale.annotation.AutoLoad;
import org.redkale.annotation.ResourceType;
import org.redkale.inject.ResourceEvent;
import org.redkale.net.*;
import org.redkale.net.client.*;
import org.redkale.service.Local;
import org.redkale.source.*;
import static org.redkale.source.DataSources.*;
import org.redkale.util.*;

/**
 * 部分协议格式参考: http://wp1i.cn/archives/78556.html
 *
 * @author zhangjx
 */
@Local
@AutoLoad(false)
@SuppressWarnings("unchecked")
@ResourceType(DataSource.class)
public class MysqlDataSource extends AbstractDataSqlSource {

    static boolean debug =
            false; // System.getProperty("os.name").contains("Window") || System.getProperty("os.name").contains("Mac");

    protected MyClient readPool;

    protected AsyncGroup readGroup;

    protected MyClient writePool;

    protected AsyncGroup writeGroup;

    @Override
    public void init(AnyValue conf) {
        super.init(conf);
        this.dbtype = "mysql";
        this.readPool = createMyPool((readConfProps == writeConfProps) ? "rw" : "read", readConfProps);
        if (readConfProps == writeConfProps) {
            this.writePool = readPool;
            this.writeGroup = this.readGroup;
        } else {
            this.writePool = createMyPool("write", writeConfProps);
        }
    }

    private MyClient createMyPool(String rw, Properties prop) {
        String url = prop.getProperty(DATA_SOURCE_URL);
        SourceUrlInfo info = parseSourceUrl(url);
        info.username = prop.getProperty(DATA_SOURCE_USER, "");
        info.password = prop.getProperty(DATA_SOURCE_PASSWORD, "");
        String encoding = prop.getProperty("characterEncoding");
        if (encoding == null || encoding.isEmpty()) {
            encoding = "UTF8MB4";
        }
        info.encoding = encoding;
        int maxConns = Math.max(1, Integer.decode(prop.getProperty(DATA_SOURCE_MAXCONNS, "" + Utility.cpus())));
        int maxPipelines = Math.max(
                1,
                Integer.decode(prop.getProperty(
                        DATA_SOURCE_PIPELINES, "" + org.redkale.net.client.Client.DEFAULT_MAX_PIPELINES)));
        AsyncGroup ioGroup = clientAsyncGroup;
        if (clientAsyncGroup == null || "write".equalsIgnoreCase(rw)) {
            String f = "Redkalex-MyClient-IOThread-" + resourceName() + "-"
                    + (rw.length() < 3 ? rw.toUpperCase() : Utility.firstCharUpperCase(rw)) + "-%s";
            ioGroup = AsyncGroup.create(
                            f,
                            workExecutor,
                            ByteBufferPool.DEFAULT_BUFFER_TCP_CAPACITY,
                            ByteBufferPool.DEFAULT_BUFFER_POOL_SIZE)
                    .start();
        }
        return new MyClient(
                resourceName(),
                ioGroup,
                resourceName() + "." + rw,
                new ClientAddress(info.servaddr),
                maxConns,
                maxPipelines,
                prop,
                info,
                autoddl(),
                clientNonBlocking,
                info.attributes);
    }

    @Override
    protected void updateOneResourceChange(Properties newProps, ResourceEvent[] events) {
        MyClient oldPool = this.readPool;
        AsyncGroup oldGroup = this.readGroup;
        this.readPool = createMyPool("rw", newProps);
        this.writePool = readPool;
        this.writeGroup = this.readGroup;
        if (oldPool != null) {
            oldPool.close();
        }
        if (oldGroup != null && oldGroup != clientAsyncGroup) {
            oldGroup.close();
        }
    }

    @Override
    protected void updateReadResourceChange(Properties newReadProps, ResourceEvent[] events) {
        MyClient oldPool = this.readPool;
        AsyncGroup oldGroup = this.readGroup;
        this.readPool = createMyPool("read", newReadProps);
        if (oldPool != null) {
            oldPool.close();
        }
        if (oldGroup != null && oldGroup != clientAsyncGroup) {
            oldGroup.close();
        }
    }

    @Override
    protected void updateWriteResourceChange(Properties newWriteProps, ResourceEvent[] events) {
        MyClient oldPool = this.writePool;
        AsyncGroup oldGroup = this.writeGroup;
        this.writePool = createMyPool("write", newWriteProps);
        if (oldPool != null) {
            oldPool.close();
        }
        if (oldGroup != null && oldGroup != clientAsyncGroup) {
            oldGroup.close();
        }
    }

    @Override
    protected int readMaxConns() {
        return readPool.getMaxConns();
    }

    @Override
    protected int writeMaxConns() {
        return writePool.getMaxConns();
    }

    @Override
    public void destroy(AnyValue config) {
        if (readPool != null) {
            readPool.close();
        }
        if (readGroup != null && readGroup != clientAsyncGroup) {
            readGroup.close();
        }
        if (writePool != null && writePool != readPool) {
            writePool.close();
        }
        if (writeGroup != null && writeGroup != clientAsyncGroup && writeGroup != readGroup) {
            readGroup.close();
        }
    }

    @Local
    @Override
    public void close() throws Exception {
        super.close();
        if (readPool != null) {
            readPool.close();
        }
        if (readGroup != null && readGroup != clientAsyncGroup) {
            readGroup.close();
        }
        if (writePool != null && writePool != readPool) {
            writePool.close();
        }
        if (writeGroup != null && writeGroup != clientAsyncGroup && writeGroup != readGroup) {
            readGroup.close();
        }
    }

    @Local
    @Override
    public final  EntityInfo loadEntityInfo(Class clazz) {
        return super.loadEntityInfo(clazz);
    }

    @Local
    protected MyClient readPool() {
        return readPool;
    }

    @Local
    protected MyClient writePool() {
        return writePool;
    }

    @Override
    protected String prepareParamSign(int index) {
        return "?"; // 不能带index
    }

    @Override
    protected final boolean isAsync() {
        return true;
    }

    @Override
    protected  CompletableFuture insertDBAsync(EntityInfo info, T... entitys) {
        final long s = System.currentTimeMillis();
        final Attribute[] attrs = info.getInsertAttributes();
        final Object[][] objs = new Object[entitys.length][];
        for (int i = 0; i < entitys.length; i++) {
            final Object[] params = new Object[attrs.length];
            for (int j = 0; j < attrs.length; j++) {
                params[j] = getEntityAttrValue(info, attrs[j], entitys[i]);
            }
            objs[i] = params;
        }

        MyClient pool = writePool();
        Map> prepareInfos =
                info.getTableStrategy() == null ? null : getInsertQuestionPrepareInfo(info, entitys);
        if ((prepareInfos == null || prepareInfos.size() < 2) && pool.cachePreparedStatements()) {
            String sql = info.isAutoGenerated()
                    ? (info.getInsertQuestionPrepareSQL(entitys[0]) + " RETURNING " + info.getPrimarySQLColumn())
                    : info.getInsertQuestionPrepareSQL(entitys[0]);
            WorkThread workThread = WorkThread.currentWorkThread();
            ObjectRef reqRef = new ObjectRef();
            ObjectRef connRef = new ObjectRef();
            return thenApplyInsertStrategy(
                            info,
                            pool.connect().thenCompose(conn -> {
                                MyReqExtended req = conn.pollReqExtended(workThread, info);
                                req.prepare(MyClientRequest.REQ_TYPE_EXTEND_INSERT, sql, 0, attrs, objs);
                                reqRef.set(req);
                                connRef.set(conn);
                                return pool.writeChannel(conn, req);
                            }),
                            reqRef,
                            connRef,
                            entitys)
                    .thenApply(dataset -> {
                        if (info.isAutoGenerated()) {
                            final Attribute primary = info.getPrimary();
                            int i = -1;
                            while (dataset.next()) {
                                primary.set(entitys[++i], DataResultSet.getRowColumnValue(dataset, primary, 1, null));
                            }
                        }
                        slowLog(s, sql);
                        return dataset.getUpdateEffectCount();
                    });
        } else {
            return executeUpdate(
                    info,
                    new String[] {info.getInsertQuestionPrepareSQL(entitys[0])},
                    entitys,
                    0,
                    MyClientRequest.REQ_TYPE_EXTEND_INSERT,
                    attrs,
                    objs);
        }
    }

    @Override
    protected  CompletableFuture deleteDBAsync(
            EntityInfo info,
            String[] tables,
            Flipper flipper,
            FilterNode node,
            Map> pkmap,
            String... sqls) {
        if (info.isLoggable(logger, Level.FINEST)) {
            if (info.isLoggable(logger, Level.FINEST, sqls[0])) {
                if (sqls.length == 1) {
                    logger.finest(info.getType().getSimpleName() + " delete sql=" + sqls[0]);
                } else {
                    logger.finest(info.getType().getSimpleName() + " delete sqls=" + Arrays.toString(sqls));
                }
            }
        }
        return executeUpdate(info, sqls, null, fetchSize(flipper), 0, null);
    }

    @Override
    protected  CompletableFuture clearTableDBAsync(
            EntityInfo info, final String[] tables, FilterNode node, final String... sqls) {
        if (info.isLoggable(logger, Level.FINEST)) {
            if (info.isLoggable(logger, Level.FINEST, sqls[0])) {
                if (sqls.length == 1) {
                    logger.finest(info.getType().getSimpleName() + " clearTable sql=" + sqls[0]);
                } else {
                    logger.finest(info.getType().getSimpleName() + " clearTable sqls=" + Arrays.toString(sqls));
                }
            }
        }
        return executeUpdate(info, sqls, null, 0, 0, null);
    }

    @Override
    protected  CompletableFuture createTableDBAsync(
            EntityInfo info, String copyTableSql, final Serializable pk, String... sqls) {
        if (copyTableSql == null) {
            return executeUpdate(info, sqls, null, 0, 0, null);
        } else {
            return executeUpdate(info, new String[] {copyTableSql}, null, 0, 0, null);
        }
    }

    @Override
    protected  CompletableFuture dropTableDBAsync(
            EntityInfo info, final String[] tables, FilterNode node, final String... sqls) {
        if (info.isLoggable(logger, Level.FINEST)) {
            if (info.isLoggable(logger, Level.FINEST, sqls[0])) {
                if (sqls.length == 1) {
                    logger.finest(info.getType().getSimpleName() + " dropTable sql=" + sqls[0]);
                } else {
                    logger.finest(info.getType().getSimpleName() + " dropTable sqls=" + Arrays.toString(sqls));
                }
            }
        }
        return executeUpdate(info, sqls, null, 0, 0, null);
    }

    @Override
    protected  CompletableFuture updateEntityDBAsync(EntityInfo info, final T... values) {
        final long s = System.currentTimeMillis();
        final Attribute primary = info.getPrimary();
        final Attribute[] attrs = info.getUpdateAttributes();
        MyClient pool = writePool();
        final String casesql =
                null; // pool.cachePreparedStatements() ? info.getUpdateQuestionPrepareCaseSQL(values) : null;
        // //case会导致executed queries数量不对
        Object[][] objs0;
        if (casesql == null) {
            objs0 = new Object[values.length][];
            for (int i = 0; i < values.length; i++) {
                final Object[] params = new Object[attrs.length + 1];
                for (int j = 0; j < attrs.length; j++) {
                    params[j] = getEntityAttrValue(info, attrs[j], values[i]);
                }
                params[attrs.length] = primary.get(values[i]); // 最后一个是主键
                objs0[i] = params;
            }
        } else {
            int len = values.length;
            int len2 = len * 2;
            objs0 = new Object[1][];
            Object[] params = new Object[len * 3];
            Attribute otherAttr = attrs[0];
            for (int i = 0; i < len2; i += 2) {
                int j = i / 2;
                params[i] = primary.get(values[j]);
                params[i + 1] = getEntityAttrValue(info, otherAttr, values[j]);
                params[j + len2] = params[i];
            }
            objs0[0] = params;
        }
        final Object[][] objs = objs0;
        if (pool.cachePreparedStatements()) {
            String sql = casesql == null ? info.getUpdateQuestionPrepareSQL(values[0]) : casesql;
            WorkThread workThread = WorkThread.currentWorkThread();
            return pool.connect()
                    .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                        MyReqExtended req = conn.pollReqExtended(workThread, info);
                        req.prepare(
                                MyClientRequest.REQ_TYPE_EXTEND_UPDATE,
                                sql,
                                0,
                                casesql == null ? Utility.append(attrs, primary) : null,
                                objs);
                        return pool.writeChannel(conn, req);
                    }))
                    .thenApply(g -> {
                        slowLog(s, sql);
                        return g.getUpdateEffectCount();
                    });
        } else {
            return executeUpdate(
                    info,
                    new String[] {info.getUpdateQuestionPrepareSQL(values[0])},
                    null,
                    0,
                    MyClientRequest.REQ_TYPE_EXTEND_UPDATE,
                    Utility.append(attrs, primary),
                    objs);
        }
    }

    @Override
    protected  CompletableFuture updateColumnDBAsync(
            EntityInfo info, Flipper flipper, UpdateSqlInfo sql) {
        if (info.isLoggable(logger, Level.FINEST)) {
            if (info.isLoggable(logger, Level.FINEST, sql.sql)) {
                logger.finest(info.getType().getSimpleName() + " update sql=" + sql.sql);
            }
        }
        List objs = null;
        if (sql.blobs != null || sql.tables != null) {
            objs = new ArrayList<>();
            if (sql.tables == null) {
                objs.add(sql.blobs.toArray());
            } else {
                for (String table : sql.tables) {
                    if (sql.blobs != null) {
                        List w = new ArrayList(sql.blobs);
                        w.add(table);
                        objs.add(w.toArray());
                    } else {
                        objs.add(new Object[] {table});
                    }
                }
            }
        }
        Object[][] as = objs != null && !objs.isEmpty() ? objs.toArray(new Object[objs.size()][]) : null;
        return executeUpdate(
                info,
                new String[] {sql.sql},
                null,
                fetchSize(flipper),
                sql.prepare ? MyClientRequest.REQ_TYPE_EXTEND_UPDATE : 0,
                null,
                as);
    }

    @Override
    protected  CompletableFuture> getNumberMapDBAsync(
            EntityInfo info, String[] tables, String sql, FilterNode node, FilterFuncColumn... columns) {
        return getNumberMapDBApply(info, executeQuery(info, sql), columns);
    }

    @Override
    protected  CompletableFuture getNumberResultDBAsync(
            EntityInfo info,
            String[] tables,
            String sql,
            FilterFunc func,
            Number defVal,
            String column,
            FilterNode node) {
        return getNumberResultDBApply(info, executeQuery(info, sql), defVal, column);
    }

    @Override
    protected  CompletableFuture> queryColumnMapDBAsync(
            EntityInfo info,
            String[] tables,
            String sql,
            String keyColumn,
            FilterFunc func,
            String funcColumn,
            FilterNode node) {
        return queryColumnMapDBApply(info, executeQuery(info, sql), keyColumn);
    }

    @Override
    protected  CompletableFuture> queryColumnMapDBAsync(
            EntityInfo info,
            String[] tables,
            String sql,
            final ColumnNode[] funcNodes,
            final String[] groupByColumns,
            FilterNode node) {
        return queryColumnMapDBApply(info, executeQuery(info, sql), funcNodes, groupByColumns);
    }

    @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);
            }
        }

        final long s = System.currentTimeMillis();
        MyClient pool = readPool();
        if (info.getTableStrategy() == null && selects == null && pool.cachePreparedStatements()) {
            String sql = info.getFindQuestionPrepareSQL(pk);
            WorkThread workThread = WorkThread.currentWorkThread();
            return pool.connect()
                    .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                        MyReqExtended req = conn.pollReqExtended(workThread, info);
                        req.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, sql, 0, null, new Object[] {pk});
                        return pool.writeChannel(conn, req);
                    }))
                    .thenApply((MyResultSet dataset) -> {
                        T rs = dataset.next() ? getEntityValue(info, selects, dataset) : null;
                        dataset.close();
                        slowLog(s, sql);
                        return rs;
                    });
        }
        String sql = findSql(info, selects, pk);
        if (info.isLoggable(logger, Level.FINEST, sql)) {
            logger.finest(info.getType().getSimpleName() + " find sql=" + sql);
        }
        return findDBApply(info, executeQuery(info, sql), true, selects);
    }

    @Override
    protected  CompletableFuture findDBAsync(
            EntityInfo info,
            String[] tables,
            String sql,
            boolean onlypk,
            SelectColumn selects,
            Serializable pk,
            FilterNode node) {
        return findDBApply(info, executeQuery(info, sql), onlypk, selects);
    }

    @Override
    protected  CompletableFuture findsDBAsync(
            final EntityInfo info, final SelectColumn selects, Serializable... pks) {
        final long s = System.currentTimeMillis();
        MyClient pool = readPool();
        if (info.getTableStrategy() == null && selects == null && pool.cachePreparedStatements()) {
            String sql = info.getFindQuestionPrepareSQL(pks[0]);
            WorkThread workThread = WorkThread.currentWorkThread();
            return pool.connect()
                    .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                        MyReqExtended req = conn.pollReqExtended(workThread, info);
                        Object[][] params = new Object[pks.length][];
                        for (int i = 0; i < params.length; i++) {
                            params[i] = new Object[] {pks[i]};
                        }
                        req.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, sql, 0, null, params);
                        req.finds = true;
                        return pool.writeChannel(conn, req);
                    }))
                    .thenApply((MyResultSet dataset) -> {
                        T[] rs = info.getArrayer().apply(pks.length);
                        int i = -1;
                        while (dataset.next()) {
                            rs[++i] = getEntityValue(info, selects, dataset);
                        }
                        dataset.close();
                        slowLog(s, sql);
                        return rs;
                    });
        } else {
            return super.findsDBAsync(info, selects, pks);
        }
    }

    @Override
    public  CompletableFuture> findsListAsync(
            final Class clazz, final java.util.stream.Stream pks) {
        final long s = System.currentTimeMillis();
        final EntityInfo info = loadEntityInfo(clazz);
        Serializable[] ids = pks.toArray(serialArrayFunc);
        MyClient pool = readPool();
        if (info.getTableStrategy() == null && pool.cachePreparedStatements()) {
            String sql = info.getFindQuestionPrepareSQL(ids[0]);
            WorkThread workThread = WorkThread.currentWorkThread();
            return pool.connect()
                    .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                        MyReqExtended req = conn.pollReqExtended(workThread, info);
                        Object[][] params = new Object[ids.length][];
                        for (int i = 0; i < params.length; i++) {
                            params[i] = new Object[] {ids[i]};
                        }
                        req.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, sql, 0, null, params);
                        req.finds = true;
                        return pool.writeChannel(conn, req);
                    }))
                    .thenApply((MyResultSet dataset) -> {
                        List rs = new ArrayList<>();
                        while (dataset.next()) {
                            rs.add(getEntityValue(info, null, dataset));
                        }
                        dataset.close();
                        slowLog(s, sql);
                        return rs;
                    });
        } else {
            return queryListAsync(
                    info.getType(),
                    (SelectColumn) null,
                    (Flipper) null,
                    FilterNodes.in(info.getPrimarySQLColumn(), ids));
        }
    }

    @Override
    protected  CompletableFuture findColumnDBAsync(
            EntityInfo info,
            String[] tables,
            String sql,
            boolean onlypk,
            String column,
            Serializable defValue,
            Serializable pk,
            FilterNode node) {
        return findColumnDBApply(info, executeQuery(info, sql), onlypk, column, defValue);
    }

    @Override
    protected  CompletableFuture existsDBAsync(
            EntityInfo info, final String[] tables, String sql, boolean onlypk, Serializable pk, FilterNode node) {
        return existsDBApply(info, executeQuery(info, sql), onlypk);
    }

    @Override
    protected  CompletableFuture> querySheetDBAsync(
            EntityInfo info,
            final boolean readCache,
            boolean needTotal,
            final boolean distinct,
            SelectColumn selects,
            Flipper flipper,
            FilterNode node,
            final boolean inCacheLoad) {
        final long s = System.currentTimeMillis();
        final SelectColumn sels = selects;
        final MyClient pool = readPool();
        String[] tables = info.getTables(node);
        final boolean cachePrepared = pool.cachePreparedStatements()
                && info.getTableStrategy() == null
                && sels == null
                && node == null
                && flipper == null
                && !distinct
                && !needTotal;
        PageCountSql sqls = !needTotal && cachePrepared
                ? null
                : createPageCountSql(info, readCache, needTotal, distinct, sels, tables, flipper, node);

        final String pageSql = cachePrepared ? info.getAllQueryPrepareSQL() : sqls.pageSql;
        if (cachePrepared && info.isLoggable(logger, Level.FINEST, pageSql)) {
            logger.finest(info.getType().getSimpleName() + " query sql=" + pageSql);
        }
        if (!needTotal) {
            CompletableFuture listFuture;
            if (cachePrepared) {
                WorkThread workThread = WorkThread.currentWorkThread();
                listFuture = pool.connect()
                        .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                            MyReqExtended req = conn.pollReqExtended(workThread, info);
                            req.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, pageSql, 0, null);
                            return pool.writeChannel(conn, req);
                        }));
            } else {
                listFuture = executeQuery(info, pageSql);
            }
            return listFuture.thenApply((MyResultSet dataset) -> {
                final List list = new ArrayList();
                while (dataset.next()) {
                    list.add(getEntityValue(info, sels, dataset));
                }
                dataset.close();
                slowLog(s, pageSql);
                return Sheet.asSheet(list);
            });
        }
        return getNumberResultDBAsync(
                        info,
                        null,
                        sqls.countSql,
                        distinct ? FilterFunc.DISTINCTCOUNT : FilterFunc.COUNT,
                        0,
                        null,
                        node)
                .thenCompose(total -> {
                    if (total.longValue() <= 0) {
                        return CompletableFuture.completedFuture(new Sheet<>(0, new ArrayList()));
                    }
                    return executeQuery(info, pageSql).thenApply((MyResultSet dataset) -> {
                        final List list = new ArrayList();
                        while (dataset.next()) {
                            list.add(getEntityValue(info, sels, dataset));
                        }
                        dataset.close();
                        slowLog(s, pageSql);
                        return new Sheet(total.longValue(), list);
                    });
                });
    }

    private static int fetchSize(Flipper flipper) {
        return Flipper.hasLimit(flipper) ? flipper.getLimit() : 0;
    }

    protected  CompletableFuture thenApplyQueryUpdateStrategy(
            final EntityInfo info,
            MyClientConnection conn,
            final Function> futureFunc) {
        if (info == null || (info.getTableStrategy() == null && !autoddl())) {
            return futureFunc.apply(conn);
        }
        final CompletableFuture rs = new CompletableFuture<>();
        futureFunc.apply(conn).whenComplete((g, t) -> {
            if (t != null) {
                while (t instanceof CompletionException) t = t.getCause();
            }
            if (t == null) {
                rs.complete(g);
            } else if (isTableNotExist(info, t, t instanceof SQLException ? ((SQLException) t).getSQLState() : null)) {
                if (info.getTableStrategy() == null) {
                    String[] tablesqls = createTableSqls(info);
                    if (tablesqls == null) { // 没有建表DDL
                        rs.completeExceptionally(t);
                    } else {
                        // 执行一遍建表操作
                        final MyReqUpdate createTableReq = new MyReqUpdate();
                        createTableReq.prepare(tablesqls[0]); // mysql只会有一条sql
                        writePool().writeChannel(conn, createTableReq).whenComplete((g2, t2) -> {
                            if (t2 == null) {
                                g2.close();
                                rs.complete(MyResultSet.EMPTY);
                            } else {
                                rs.completeExceptionally(t2);
                            }
                        });
                    }
                } else {
                    rs.complete(MyResultSet.EMPTY);
                }
            } else {
                rs.completeExceptionally(t);
            }
        });
        return rs;
    }

    protected  CompletableFuture thenApplyInsertStrategy(
            final EntityInfo info,
            final CompletableFuture future,
            final ObjectRef reqRef,
            final ObjectRef connRef,
            final T[] values) {
        if (info == null || (info.getTableStrategy() == null && !autoddl())) {
            return future;
        }
        final CompletableFuture rs = new CompletableFuture<>();
        future.whenComplete((g, t) -> {
            if (t != null) {
                while (t instanceof CompletionException) t = t.getCause();
            }
            if (t == null) {
                rs.complete(g);
            } else if (isTableNotExist(
                    info, t, t instanceof SQLException ? ((SQLException) t).getSQLState() : null)) { // 表不存在
                if (info.getTableStrategy() == null) { // 单表模式
                    String[] tablesqls = createTableSqls(info);
                    if (tablesqls == null) { // 没有建表DDL
                        rs.completeExceptionally(t);
                    } else {
                        // 执行一遍建表操作
                        final MyReqUpdate createTableReq = new MyReqUpdate();
                        createTableReq.prepare(tablesqls[0]); // mysql只会有一条sql
                        writePool().writeChannel(connRef.get(), createTableReq).whenComplete((g2, t2) -> {
                            if (t2 != null) {
                                while (t2 instanceof CompletionException) t2 = t2.getCause();
                            }
                            if (t2 == null) { // 建表成功
                                // 执行一遍新增操作
                                writePool()
                                        .writeChannel(
                                                connRef.get(), reqRef.get().reuse())
                                        .whenComplete((g3, t3) -> {
                                            if (t3 == null) {
                                                rs.complete(g3);
                                            } else {
                                                rs.completeExceptionally(t3);
                                            }
                                        });
                            } else {
                                rs.completeExceptionally(t2);
                            }
                        });
                    }
                } else { // 分表模式
                    // 执行一遍复制表操作
                    final String newTable = info.getTable(values[0]);
                    final MyReqUpdate copyTableReq = new MyReqUpdate();
                    copyTableReq.prepare(getTableCopySql(info, newTable));
                    writePool().writeChannel(connRef.get(), copyTableReq).whenComplete((g2, t2) -> {
                        if (t2 != null) {
                            while (t2 instanceof CompletionException) t2 = t2.getCause();
                        }
                        if (t2 == null) {
                            // 执行一遍新增操作
                            writePool()
                                    .writeChannel(connRef.get(), reqRef.get().reuse())
                                    .whenComplete((g3, t3) -> {
                                        if (t3 == null) {
                                            rs.complete(g3);
                                        } else {
                                            rs.completeExceptionally(t3);
                                        }
                                    });
                        } else if (isTableNotExist(
                                info,
                                t2,
                                t2 instanceof SQLException
                                        ? ((SQLException) t2).getSQLState()
                                        : null)) { // 还是没有表: 1、没有原始表; 2:没有库
                            if (newTable.indexOf('.') < 0) { // 没有原始表需要建表
                                String[] tablesqls = createTableSqls(info);
                                if (tablesqls == null) { // 没有建表DDL
                                    rs.completeExceptionally(t2);
                                } else {
                                    // 执行一遍建表操作
                                    final MyReqUpdate createTableReq = new MyReqUpdate();
                                    createTableReq.prepare(tablesqls[0]); // mysql只会有一条sql
                                    writePool()
                                            .writeChannel(connRef.get(), createTableReq)
                                            .whenComplete((g4, t4) -> {
                                                if (t4 == null) { // 建表成功
                                                    // 再执行一遍复制表操作
                                                    final MyReqUpdate copyTableReq2 = new MyReqUpdate();
                                                    copyTableReq2.prepare(getTableCopySql(info, newTable));
                                                    writePool()
                                                            .writeChannel(connRef.get(), copyTableReq2)
                                                            .whenComplete((g5, t5) -> {
                                                                if (t5 == null) {
                                                                    // 再执行一遍新增操作
                                                                    writePool()
                                                                            .writeChannel(
                                                                                    connRef.get(),
                                                                                    reqRef.get()
                                                                                            .reuse())
                                                                            .whenComplete((g6, t6) -> {
                                                                                if (t6 == null) {
                                                                                    rs.complete(g6);
                                                                                } else {
                                                                                    rs.completeExceptionally(t6);
                                                                                }
                                                                            });
                                                                } else {
                                                                    rs.completeExceptionally(t5);
                                                                }
                                                            });
                                                } else {
                                                    rs.completeExceptionally(t4);
                                                }
                                            });
                                }
                            } else { // 没有库需要建库
                                final MyReqUpdate createDatabaseReq = new MyReqUpdate();
                                createDatabaseReq.prepare("CREATE DATABASE IF NOT EXISTS "
                                        + newTable.substring(0, newTable.indexOf('.')));
                                writePool()
                                        .writeChannel(connRef.get(), createDatabaseReq)
                                        .whenComplete((g4, t4) -> {
                                            if (t4 == null) { // 建库成功
                                                // 再执行一遍复制表操作
                                                final MyReqUpdate copyTableReq2 = new MyReqUpdate();
                                                copyTableReq2.prepare(getTableCopySql(info, newTable));
                                                writePool()
                                                        .writeChannel(connRef.get(), copyTableReq2)
                                                        .whenComplete((g5, t5) -> {
                                                            if (t5 != null) {
                                                                while (t5 instanceof CompletionException)
                                                                    t5 = t5.getCause();
                                                            }
                                                            if (t5 == null) {
                                                                // 再执行一遍新增操作
                                                                writePool()
                                                                        .writeChannel(
                                                                                connRef.get(),
                                                                                reqRef.get()
                                                                                        .reuse())
                                                                        .whenComplete((g6, t6) -> {
                                                                            if (t6 == null) {
                                                                                rs.complete(g6);
                                                                            } else {
                                                                                rs.completeExceptionally(t6);
                                                                            }
                                                                        });
                                                            } else if (isTableNotExist(
                                                                    info,
                                                                    t5,
                                                                    t5 instanceof SQLException
                                                                            ? ((SQLException) t5).getSQLState()
                                                                            : null)) { // 没有原始表需要建表
                                                                String[] tablesqls = createTableSqls(info);
                                                                if (tablesqls == null) { // 没有建表DDL
                                                                    rs.completeExceptionally(t5);
                                                                } else {
                                                                    // 执行一遍建表操作
                                                                    final MyReqUpdate createTableReq =
                                                                            new MyReqUpdate();
                                                                    createTableReq.prepare(
                                                                            tablesqls[0]); // mysql只会有一条sql
                                                                    writePool()
                                                                            .writeChannel(connRef.get(), createTableReq)
                                                                            .whenComplete((g6, t6) -> {
                                                                                if (t6 == null) { // 建表成功
                                                                                    // 再再执行一遍复制表操作
                                                                                    final MyReqUpdate copyTableReq3 =
                                                                                            new MyReqUpdate();
                                                                                    copyTableReq3.prepare(
                                                                                            getTableCopySql(
                                                                                                    info, newTable));
                                                                                    writePool()
                                                                                            .writeChannel(
                                                                                                    connRef.get(),
                                                                                                    copyTableReq3)
                                                                                            .whenComplete((g7, t7) -> {
                                                                                                if (t7 == null) {
                                                                                                    // 再执行一遍新增操作
                                                                                                    writePool()
                                                                                                            .writeChannel(
                                                                                                                    connRef
                                                                                                                            .get(),
                                                                                                                    reqRef.get()
                                                                                                                            .reuse())
                                                                                                            .whenComplete(
                                                                                                                    (g8,
                                                                                                                            t8) -> {
                                                                                                                        if (t8
                                                                                                                                == null) {
                                                                                                                            rs
                                                                                                                                    .complete(
                                                                                                                                            g8);
                                                                                                                        } else {
                                                                                                                            rs
                                                                                                                                    .completeExceptionally(
                                                                                                                                            t8);
                                                                                                                        }
                                                                                                                    });
                                                                                                } else {
                                                                                                    rs
                                                                                                            .completeExceptionally(
                                                                                                                    t7);
                                                                                                }
                                                                                            });
                                                                                } else {
                                                                                    rs.completeExceptionally(t6);
                                                                                }
                                                                            });
                                                                }
                                                            } else {
                                                                rs.completeExceptionally(t5);
                                                            }
                                                        });
                                            } else {
                                                rs.completeExceptionally(t4);
                                            }
                                        });
                            }
                        } else {
                            rs.completeExceptionally(t2);
                        }
                    });
                }
            } else {
                rs.completeExceptionally(t);
            }
        });
        return rs;
    }

    protected  CompletableFuture executeUpdate(
            final EntityInfo info,
            final String[] sqls,
            final T[] values,
            int fetchSize,
            final int extendType,
            final Attribute[] attrs,
            final Object[]... parameters) {
        final long s = System.currentTimeMillis();
        final MyClient pool = writePool();
        WorkThread workThread = WorkThread.currentWorkThread();
        ObjectRef reqRef = new ObjectRef();
        ObjectRef connRef = new ObjectRef();
        Function> futureFunc = conn -> {
            connRef.set(conn);
            if (sqls.length == 1) {
                if (extendType > 0) {
                    MyReqExtended req = conn.pollReqExtended(workThread, info);
                    req.prepare(extendType, sqls[0], 0, attrs, parameters);
                    reqRef.set(req);
                    return pool.writeChannel(conn, req);
                } else {
                    MyReqUpdate req = conn.pollReqUpdate(workThread, info);
                    req.prepare(sqls[0], fetchSize, attrs, parameters);
                    reqRef.set(req);
                    return pool.writeChannel(conn, req);
                }
            } else {
                MyReqBatch req = new MyReqBatch();
                req.prepare(sqls);
                return pool.writeChannel(conn, req);
            }
        };
        if (info == null || (info.getTableStrategy() == null && !autoddl())) {
            return pool.connect().thenCompose(futureFunc).thenApply(g -> {
                slowLog(s, sqls);
                return g.getUpdateEffectCount();
            });
        }
        if (extendType == MyReqExtended.REQ_TYPE_EXTEND_INSERT) {
            return thenApplyInsertStrategy(info, pool.connect().thenCompose(futureFunc), reqRef, connRef, values)
                    .thenApply(g -> {
                        slowLog(s, sqls);
                        return g.getUpdateEffectCount();
                    });
        }
        return pool.connect()
                .thenCompose(conn -> thenApplyQueryUpdateStrategy(info, conn, futureFunc))
                .thenApply(g -> {
                    slowLog(s, sqls);
                    return g.getUpdateEffectCount();
                });
    }

    // info可以为null,供directQuery
    protected  CompletableFuture executeQuery(final EntityInfo info, final String sql) {
        final long s = System.currentTimeMillis();
        final MyClient pool = readPool();
        WorkThread workThread = WorkThread.currentWorkThread();
        return pool.connect()
                .thenCompose(c -> thenApplyQueryUpdateStrategy(info, c, conn -> {
                    MyReqQuery req = conn.pollReqQuery(workThread, info);
                    req.prepare(sql);
                    return pool.writeChannel(conn, req);
                }))
                .thenApply(rs -> {
                    slowLog(s, sql);
                    return rs;
                });
    }

    @Local
    @Override
    public CompletableFuture nativeUpdateAsync(String sql) {
        final long s = System.currentTimeMillis();
        final MyClient pool = writePool();
        WorkThread workThread = WorkThread.currentWorkThread();
        CompletableFuture future = pool.connect().thenCompose(conn -> {
            MyReqUpdate req = conn.pollReqUpdate(workThread, null);
            return pool.writeChannel(conn, req.prepare(sql));
        });
        return future.thenApply(g -> {
            slowLog(s, sql);
            return g.getUpdateEffectCount();
        });
    }

    @Local
    @Override
    public CompletableFuture nativeUpdatesAsync(String... sqls) {
        if (sqls.length == 1) {
            return nativeUpdateAsync(sqls[0]).thenApply(v -> new int[] {v});
        }
        final long s = System.currentTimeMillis();
        final MyClient pool = writePool();
        CompletableFuture future = pool.connect().thenCompose(conn -> {
            MyReqBatch req = new MyReqBatch();
            return pool.writeChannel(conn, req.prepare(sqls));
        });
        return future.thenApply(g -> {
            slowLog(s, sqls);
            return g.getBatchEffectCounts();
        });
    }

    @Local
    @Override
    public  CompletableFuture nativeQueryAsync(
            String sql, BiConsumer consumer, Function handler) {
        final long s = System.currentTimeMillis();
        return executeQuery(null, sql).thenApply((DataResultSet dataset) -> {
            V rs = handler.apply(dataset);
            dataset.close();
            slowLog(s, sql);
            return rs;
        });
    }

    @Local
    @Override
    public CompletableFuture nativeUpdateAsync(String sql, Map params) {
        long s = System.currentTimeMillis();
        DataNativeSqlStatement sinfo = super.nativeParse(sql, false, null, params);
        final WorkThread workThread = WorkThread.currentWorkThread();
        MyClient pool = writePool();
        if (!sinfo.isEmptyNamed()) {
            return pool.connect().thenCompose(conn -> {
                MyReqExtended req = conn.pollReqExtended(workThread, null);
                Stream pstream = sinfo.getParamNames().stream().map(n -> params.get(n));
                req.prepare(MyClientRequest.REQ_TYPE_EXTEND_UPDATE, sinfo.getNativeSql(), 0, pstream);
                Function transfer = dataset -> {
                    int rs = dataset.getUpdateEffectCount();
                    slowLog(s, sinfo.getNativeSql());
                    return rs;
                };
                return pool.writeChannel(conn, req).thenApply(transfer);
            });
        } else {
            return pool.connect().thenCompose(conn -> {
                MyReqExtended req = conn.pollReqExtended(workThread, null);
                req.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, sinfo.getNativeSql(), 0, (Stream) null);
                Function transfer = dataset -> {
                    int rs = dataset.getUpdateEffectCount();
                    slowLog(s, sinfo.getNativeSql());
                    return rs;
                };
                return pool.writeChannel(conn, req).thenApply(transfer);
            });
        }
    }

    @Local
    @Override
    public  CompletableFuture nativeQueryAsync(
            String sql,
            BiConsumer consumer,
            Function handler,
            Map params) {
        long s = System.currentTimeMillis();
        DataNativeSqlStatement sinfo = super.nativeParse(sql, false, null, params);
        final WorkThread workThread = WorkThread.currentWorkThread();
        MyClient pool = readPool();
        Function transfer = dataset -> {
            V rs = handler.apply(dataset);
            slowLog(s, sinfo.getNativeSql());
            return rs;
        };
        if (!sinfo.isEmptyNamed()) {
            return pool.connect().thenCompose(conn -> {
                MyReqExtended req = conn.pollReqExtended(workThread, null);
                Stream pstream = sinfo.getParamNames().stream().map(n -> params.get(n));
                req.prepare(MyClientRequest.REQ_TYPE_EXTEND_UPDATE, sinfo.getNativeSql(), 0, pstream);
                return pool.writeChannel(conn, req).thenApply(transfer);
            });
        } else {
            return pool.connect().thenCompose(conn -> {
                MyReqQuery req = conn.pollReqQuery(workThread, null);
                req.prepare(sinfo.getNativeSql());
                return pool.writeChannel(conn, req).thenApply(transfer);
            });
        }
    }

    public  CompletableFuture> nativeQuerySheetAsync(
            Class type, String sql, RowBound round, Map params) {
        long s = System.currentTimeMillis();
        DataNativeSqlStatement sinfo = super.nativeParse(sql, true, round, params);
        final WorkThread workThread = WorkThread.currentWorkThread();
        MyClient pool = readPool();
        final String countSql = sinfo.getNativeCountSql();
        Function countTransfer = dataset -> {
            long rs = dataset.next() ? dataset.getLong(1, 0L) : 0;
            slowLog(s, countSql);
            return rs;
        };
        if (!sinfo.isEmptyNamed()) {
            return pool.connect().thenCompose(conn -> {
                MyReqExtended countReq = conn.pollReqExtended(workThread, null);
                Stream pstream = sinfo.getParamNames().stream().map(n -> (Serializable) params.get(n));
                countReq.prepare(MyClientRequest.REQ_TYPE_EXTEND_QUERY, countSql, 0, pstream);
                return pool.writeChannel(conn, countTransfer, countReq).thenCompose(total -> {
                    if (total < 1) {
                        return CompletableFuture.completedFuture(new Sheet(total, new ArrayList<>()));
                    } else {
                        long s2 = System.currentTimeMillis();
                        String pageSql = sinfo.getNativePageSql();
                        MyReqExtended req = conn.pollReqExtended(workThread, null);
                        Stream pstream2 = sinfo.getParamNames().stream().map(n -> (Serializable) params.get(n));
                        req.prepare(
                                MyClientRequest.REQ_TYPE_EXTEND_QUERY,
                                pageSql,
                                round == null ? 0 : round.getLimit(),
                                pstream2);
                        Function> sheetTransfer = dataset -> {
                            List list = EntityBuilder.getListValue(type, dataset);
                            slowLog(s2, pageSql);
                            return new Sheet<>(total, list);
                        };
                        return pool.writeChannel(conn, sheetTransfer, req);
                    }
                });
            });
        } else {
            return pool.connect().thenCompose(conn -> {
                MyReqQuery countReq = conn.pollReqQuery(workThread, null);
                countReq.prepare(countSql);
                return pool.writeChannel(conn, countTransfer, countReq).thenCompose(total -> {
                    if (total < 1) {
                        return CompletableFuture.completedFuture(new Sheet(total, new ArrayList<>()));
                    } else {
                        long s2 = System.currentTimeMillis();
                        String pageSql = sinfo.getNativePageSql();
                        MyReqQuery req = conn.pollReqQuery(workThread, null);
                        req.prepare(pageSql);
                        Function> sheetTransfer = dataset -> {
                            List list = EntityBuilder.getListValue(type, dataset);
                            slowLog(s2, pageSql);
                            return new Sheet<>(total, list);
                        };
                        return pool.writeChannel(conn, sheetTransfer, req);
                    }
                });
            });
        }
    }
}