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

com.landawn.abacus.util.JdbcUtil Maven / Gradle / Ivy

There is a newer version: 1.10.1
Show newest version
/*
 * Copyright (c) 2015, Haiyang Li.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.landawn.abacus.util;

import static com.landawn.abacus.dataSource.DataSourceConfiguration.DRIVER;
import static com.landawn.abacus.dataSource.DataSourceConfiguration.PASSWORD;
import static com.landawn.abacus.dataSource.DataSourceConfiguration.URL;
import static com.landawn.abacus.dataSource.DataSourceConfiguration.USER;
import static com.landawn.abacus.util.IOUtil.DEFAULT_QUEUE_SIZE_FOR_ROW_PARSER;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Date;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLType;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import javax.xml.parsers.DocumentBuilder;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.landawn.abacus.DataSet;
import com.landawn.abacus.DataSource;
import com.landawn.abacus.DataSourceManager;
import com.landawn.abacus.DataSourceSelector;
import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.IsolationLevel;
import com.landawn.abacus.SliceSelector;
import com.landawn.abacus.Transaction;
import com.landawn.abacus.Transaction.Status;
import com.landawn.abacus.annotation.Column;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.condition.ConditionFactory.L;
import com.landawn.abacus.core.RowDataSet;
import com.landawn.abacus.dataSource.DataSourceConfiguration;
import com.landawn.abacus.dataSource.DataSourceManagerConfiguration;
import com.landawn.abacus.dataSource.NonSliceSelector;
import com.landawn.abacus.dataSource.SQLDataSource;
import com.landawn.abacus.dataSource.SQLDataSourceManager;
import com.landawn.abacus.dataSource.SimpleSourceSelector;
import com.landawn.abacus.exception.AbacusException;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.ParseException;
import com.landawn.abacus.exception.UncheckedIOException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.EntityInfo;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.ExceptionalStream.ExceptionalIterator;
import com.landawn.abacus.util.Fn.FN;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.SQLBuilder.NAC;
import com.landawn.abacus.util.SQLBuilder.NLC;
import com.landawn.abacus.util.SQLBuilder.NSC;
import com.landawn.abacus.util.SQLBuilder.PAC;
import com.landawn.abacus.util.SQLBuilder.PLC;
import com.landawn.abacus.util.SQLBuilder.PSC;
import com.landawn.abacus.util.SQLBuilder.SP;
import com.landawn.abacus.util.SQLExecutor.JdbcSettings;
import com.landawn.abacus.util.SQLExecutor.StatementSetter;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.Try.BiFunction;
import com.landawn.abacus.util.Tuple.Tuple2;
import com.landawn.abacus.util.Tuple.Tuple3;
import com.landawn.abacus.util.Tuple.Tuple4;
import com.landawn.abacus.util.Tuple.Tuple5;
import com.landawn.abacus.util.u.Nullable;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalBoolean;
import com.landawn.abacus.util.u.OptionalByte;
import com.landawn.abacus.util.u.OptionalChar;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalFloat;
import com.landawn.abacus.util.u.OptionalInt;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.u.OptionalShort;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.stream.Collector;
import com.landawn.abacus.util.stream.Stream;
import com.landawn.abacus.util.stream.Stream.StreamEx;

/**
 *
 * @since 0.8
 * 
 * @author Haiyang Li 
 * 
 * @see {@link com.landawn.abacus.annotation.ReadOnly}
 * @see {@link com.landawn.abacus.annotation.ReadOnlyId}
 * @see {@link com.landawn.abacus.annotation.NonUpdatable}
 * @see {@link com.landawn.abacus.annotation.Transient}
 * @see {@link com.landawn.abacus.annotation.Table}
 * @see {@link com.landawn.abacus.annotation.Column}
 * 
 * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html
 * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html
 * @see http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html
 * @see http://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html
 */
public final class JdbcUtil {
    private static final Logger logger = LoggerFactory.getLogger(JdbcUtil.class);

    // ...
    private static final String CURRENT_DIR_PATH = "./";

    private static final JdbcUtil.BiParametersSetter DEFAULT_STMT_SETTER = new JdbcUtil.BiParametersSetter() {
        @Override
        public void accept(PreparedStatement stmt, Object[] parameters) throws SQLException {
            for (int i = 0, len = parameters.length; i < len; i++) {
                stmt.setObject(i + 1, parameters[i]);
            }
        }
    };

    private static final Set sqlStateForTableNotExists = new HashSet<>();

    static {
        sqlStateForTableNotExists.add("42S02"); // for MySQL.
        sqlStateForTableNotExists.add("42P01"); // for PostgreSQL.
        sqlStateForTableNotExists.add("42501"); // for HSQLDB.
    }

    private JdbcUtil() {
        // singleton
    }

    public static DBVersion getDBVersion(final Connection conn) throws UncheckedSQLException {
        try {
            String dbProudctName = conn.getMetaData().getDatabaseProductName();
            String dbProudctVersion = conn.getMetaData().getDatabaseProductVersion();

            DBVersion dbVersion = DBVersion.OTHERS;

            String upperCaseProductName = dbProudctName.toUpperCase();
            if (upperCaseProductName.contains("H2")) {
                dbVersion = DBVersion.H2;
            } else if (upperCaseProductName.contains("HSQL")) {
                dbVersion = DBVersion.HSQLDB;
            } else if (upperCaseProductName.contains("MYSQL")) {
                if (dbProudctVersion.startsWith("5.5")) {
                    dbVersion = DBVersion.MYSQL_5_5;
                } else if (dbProudctVersion.startsWith("5.6")) {
                    dbVersion = DBVersion.MYSQL_5_6;
                } else if (dbProudctVersion.startsWith("5.7")) {
                    dbVersion = DBVersion.MYSQL_5_7;
                } else if (dbProudctVersion.startsWith("5.8")) {
                    dbVersion = DBVersion.MYSQL_5_8;
                } else if (dbProudctVersion.startsWith("5.9")) {
                    dbVersion = DBVersion.MYSQL_5_9;
                } else if (dbProudctVersion.startsWith("6")) {
                    dbVersion = DBVersion.MYSQL_6;
                } else if (dbProudctVersion.startsWith("7")) {
                    dbVersion = DBVersion.MYSQL_7;
                } else if (dbProudctVersion.startsWith("8")) {
                    dbVersion = DBVersion.MYSQL_8;
                } else if (dbProudctVersion.startsWith("9")) {
                    dbVersion = DBVersion.MYSQL_9;
                } else if (dbProudctVersion.startsWith("10")) {
                    dbVersion = DBVersion.MYSQL_10;
                } else {
                    dbVersion = DBVersion.MYSQL_OTHERS;
                }
            } else if (upperCaseProductName.contains("POSTGRESQL")) {
                if (dbProudctVersion.startsWith("9.2")) {
                    dbVersion = DBVersion.POSTGRESQL_9_2;
                } else if (dbProudctVersion.startsWith("9.3")) {
                    dbVersion = DBVersion.POSTGRESQL_9_3;
                } else if (dbProudctVersion.startsWith("9.4")) {
                    dbVersion = DBVersion.POSTGRESQL_9_4;
                } else if (dbProudctVersion.startsWith("9.5")) {
                    dbVersion = DBVersion.POSTGRESQL_9_5;
                } else if (dbProudctVersion.startsWith("10")) {
                    dbVersion = DBVersion.POSTGRESQL_10;
                } else if (dbProudctVersion.startsWith("11")) {
                    dbVersion = DBVersion.POSTGRESQL_11;
                } else if (dbProudctVersion.startsWith("12")) {
                    dbVersion = DBVersion.POSTGRESQL_12;
                } else {
                    dbVersion = DBVersion.POSTGRESQL_OTHERS;
                }
            } else if (upperCaseProductName.contains("ORACLE")) {
                dbVersion = DBVersion.ORACLE;
            } else if (upperCaseProductName.contains("DB2")) {
                dbVersion = DBVersion.DB2;
            } else if (upperCaseProductName.contains("SQL SERVER")) {
                dbVersion = DBVersion.SQL_SERVER;
            }

            return dbVersion;
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        }
    }

    /**
     * 
     * @param dataSourceXmlFile
     * @return DataSourceManager
     * 
     * @see DataSource.xsd
     */
    public static DataSourceManager createDataSourceManager(final String dataSourceXmlFile) throws UncheckedIOException, UncheckedSQLException {
        InputStream is = null;
        try {
            is = new FileInputStream(Configuration.findFile(dataSourceXmlFile));
            return createDataSourceManager(is, dataSourceXmlFile);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } finally {
            IOUtil.close(is);
        }

    }

    /**
     * 
     * @param dataSourceXmlInputStream
     * @return DataSourceManager
     * 
     * @see DataSource.xsd
     */
    public static DataSourceManager createDataSourceManager(final InputStream dataSourceXmlInputStream) throws UncheckedIOException, UncheckedSQLException {
        return createDataSourceManager(dataSourceXmlInputStream, CURRENT_DIR_PATH);
    }

    private static final String PROPERTIES = "properties";

    private static final String RESOURCE = "resource";

    private static DataSourceManager createDataSourceManager(final InputStream dataSourceXmlInputStream, final String dataSourceXmlFile)
            throws UncheckedIOException, UncheckedSQLException {
        DocumentBuilder domParser = XMLUtil.createDOMParser();
        Document doc = null;

        try {
            doc = domParser.parse(dataSourceXmlInputStream);

            Element rootElement = doc.getDocumentElement();

            final Map props = new HashMap<>();
            List propertiesElementList = XMLUtil.getElementsByTagName(rootElement, PROPERTIES);

            if (N.notNullOrEmpty(propertiesElementList)) {
                for (Element propertiesElement : propertiesElementList) {
                    File resourcePropertiesFile = Configuration.findFileByFile(new File(dataSourceXmlFile), propertiesElement.getAttribute(RESOURCE));
                    java.util.Properties properties = new java.util.Properties();
                    InputStream is = null;

                    try {
                        is = new FileInputStream(resourcePropertiesFile);

                        if (resourcePropertiesFile.getName().endsWith(".xml")) {
                            properties.loadFromXML(is);
                        } else {
                            properties.load(is);
                        }
                    } finally {
                        IOUtil.close(is);
                    }

                    for (Object key : properties.keySet()) {
                        props.put((String) key, (String) properties.get(key));
                    }
                }
            }

            String nodeName = rootElement.getNodeName();
            if (nodeName.equals(DataSourceManagerConfiguration.DATA_SOURCE_MANAGER)) {
                DataSourceManagerConfiguration config = new DataSourceManagerConfiguration(rootElement, props);
                return new SQLDataSourceManager(config);
            } else if (nodeName.equals(DataSourceConfiguration.DATA_SOURCE)) {
                DataSourceConfiguration config = new DataSourceConfiguration(rootElement, props);
                return new SimpleDataSourceManager(new SQLDataSource(config));
            } else {
                throw new AbacusException("Unknown xml format with root element: " + nodeName);
            }
        } catch (SAXException e) {
            throw new ParseException(e);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * 
     * @param dataSourceFile
     * @return
     * @throws UncheckedIOException
     * @throws UncheckedSQLException
     * @see DataSource.xsd
     */
    public static DataSource createDataSource(final String dataSourceFile) throws UncheckedIOException, UncheckedSQLException {
        InputStream is = null;
        try {
            is = new FileInputStream(Configuration.findFile(dataSourceFile));
            return createDataSource(is, dataSourceFile);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        } finally {
            IOUtil.close(is);
        }
    }

    /**
     * 
     * @param dataSourceInputStream
     * @return
     * @throws UncheckedIOException
     * @throws UncheckedSQLException
     * @see DataSource.xsd
     */
    public static DataSource createDataSource(final InputStream dataSourceInputStream) throws UncheckedIOException, UncheckedSQLException {
        return createDataSource(dataSourceInputStream, CURRENT_DIR_PATH);
    }

    private static DataSource createDataSource(final InputStream dataSourceInputStream, final String dataSourceFile)
            throws UncheckedIOException, UncheckedSQLException {
        final String dataSourceString = IOUtil.readString(dataSourceInputStream);

        if (CURRENT_DIR_PATH.equals(dataSourceFile) || dataSourceFile.endsWith(".xml")) {
            try {
                return createDataSourceManager(new ByteArrayInputStream(dataSourceString.getBytes())).getPrimaryDataSource();
            } catch (ParseException e) {
                // ignore.
            } catch (UncheckedIOException e) {
                // ignore.
            }
        }

        final Map newProps = new HashMap<>();
        final java.util.Properties properties = new java.util.Properties();

        try {
            properties.load(new ByteArrayInputStream(dataSourceString.getBytes()));

            Object value = null;

            for (Object key : properties.keySet()) {
                value = properties.get(key);
                newProps.put(key.toString().trim(), value.toString().trim());
            }

        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }

        return new SQLDataSource(newProps);
    }

    public static DataSource createDataSource(final String url, final String user, final String password) throws UncheckedSQLException {
        return createDataSource(getDriverClasssByUrl(url), url, user, password);
    }

    public static DataSource createDataSource(final String driver, final String url, final String user, final String password) throws UncheckedSQLException {
        final Class driverClass = ClassUtil.forClass(driver);

        return createDataSource(driverClass, url, user, password);
    }

    public static DataSource createDataSource(final Class driverClass, final String url, final String user, final String password)
            throws UncheckedSQLException {
        final Map props = new HashMap<>();

        props.put(DRIVER, driverClass.getCanonicalName());
        props.put(URL, url);
        props.put(USER, user);
        props.put(PASSWORD, password);

        return createDataSource(props);
    }

    /**
     * 
     * @param props refer to Connection.xsd for the supported properties.
     * @return
     */
    public static DataSource createDataSource(final Map props) throws UncheckedSQLException {
        final String driver = (String) props.get(DRIVER);

        if (N.isNullOrEmpty(driver)) {
            final String url = (String) props.get(URL);

            if (N.isNullOrEmpty(url)) {
                throw new IllegalArgumentException("Url is not specified");
            }

            final Map tmp = new HashMap<>(props);

            tmp.put(DRIVER, getDriverClasssByUrl(url).getCanonicalName());

            return new SQLDataSource(tmp);
        } else {
            return new SQLDataSource(props);
        }
    }

    /**
     * 
     * @param sqlDataSource
     * @return
     * @deprecated
     */
    @Deprecated
    public static DataSource wrap(final javax.sql.DataSource sqlDataSource) {
        return sqlDataSource instanceof DataSource ? ((DataSource) sqlDataSource) : new SimpleDataSource(sqlDataSource);
    }

    public static Connection createConnection(final String url, final String user, final String password) throws UncheckedSQLException {
        return createConnection(getDriverClasssByUrl(url), url, user, password);
    }

    private static Class getDriverClasssByUrl(final String url) {
        Class driverClass = null;
        // jdbc:mysql://localhost:3306/abacustest
        if (url.indexOf("mysql") > 0 || StringUtil.indexOfIgnoreCase(url, "mysql") > 0) {
            driverClass = ClassUtil.forClass("com.mysql.jdbc.Driver");
            // jdbc:postgresql://localhost:5432/abacustest
        } else if (url.indexOf("postgresql") > 0 || StringUtil.indexOfIgnoreCase(url, "postgresql") > 0) {
            driverClass = ClassUtil.forClass("org.postgresql.Driver");
            // jdbc:h2:hsql://:/
        } else if (url.indexOf("h2") > 0 || StringUtil.indexOfIgnoreCase(url, "h2") > 0) {
            driverClass = ClassUtil.forClass("org.h2.Driver");
            // jdbc:hsqldb:hsql://localhost/abacustest
        } else if (url.indexOf("hsqldb") > 0 || StringUtil.indexOfIgnoreCase(url, "hsqldb") > 0) {
            driverClass = ClassUtil.forClass("org.hsqldb.jdbc.JDBCDriver");
            // jdbc.url=jdbc:oracle:thin:@localhost:1521:abacustest
        } else if (url.indexOf("oracle") > 0 || StringUtil.indexOfIgnoreCase(url, "oracle") > 0) {
            driverClass = ClassUtil.forClass("oracle.jdbc.driver.OracleDriver");
            // jdbc.url=jdbc:sqlserver://localhost:1433;Database=abacustest
        } else if (url.indexOf("sqlserver") > 0 || StringUtil.indexOfIgnoreCase(url, "sqlserver") > 0) {
            driverClass = ClassUtil.forClass("com.microsoft.sqlserver.jdbc.SQLServerDriver");
            // jdbc:db2://localhost:50000/abacustest
        } else if (url.indexOf("db2") > 0 || StringUtil.indexOfIgnoreCase(url, "db2") > 0) {
            driverClass = ClassUtil.forClass("com.ibm.db2.jcc.DB2Driver");
        } else {
            throw new IllegalArgumentException(
                    "Can not identity the driver class by url: " + url + ". Only mysql, postgresql, hsqldb, sqlserver, oracle and db2 are supported currently");
        }
        return driverClass;
    }

    public static Connection createConnection(final String driverClass, final String url, final String user, final String password)
            throws UncheckedSQLException {
        Class cls = ClassUtil.forClass(driverClass);
        return createConnection(cls, url, user, password);
    }

    public static Connection createConnection(final Class driverClass, final String url, final String user, final String password)
            throws UncheckedSQLException {
        try {
            DriverManager.registerDriver(N.newInstance(driverClass));

            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            throw new UncheckedSQLException("Failed to close create connection", e);
        }
    }

    private static boolean isInSpring = true;

    static {
        try {
            isInSpring = ClassUtil.forClass("org.springframework.jdbc.datasource.DataSourceUtils") != null;
        } catch (Throwable e) {
            isInSpring = false;
        }
    }

    public static Connection getConnection(final javax.sql.DataSource ds) throws SQLException {
        if (isInSpring) {
            try {
                return org.springframework.jdbc.datasource.DataSourceUtils.getConnection(ds);
            } catch (NoClassDefFoundError e) {
                isInSpring = false;
                return ds.getConnection();
            }
        } else {
            return ds.getConnection();
        }
    }

    public static void releaseConnection(final Connection conn, final javax.sql.DataSource ds) {
        if (conn == null) {
            return;
        }

        if (isInSpring && ds != null) {
            try {
                org.springframework.jdbc.datasource.DataSourceUtils.releaseConnection(conn, ds);
            } catch (NoClassDefFoundError e) {
                isInSpring = false;
                JdbcUtil.closeQuietly(conn);
            }
        } else {
            JdbcUtil.closeQuietly(conn);
        }
    }

    public static Runnable createCloseHandler(final Connection conn, final javax.sql.DataSource ds) {
        return new Runnable() {
            @Override
            public void run() {
                releaseConnection(conn, ds);
            }
        };
    }

    public static void close(final ResultSet rs) throws UncheckedSQLException {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            }
        }
    }

    public static void close(final ResultSet rs, final boolean closeStatement) throws UncheckedSQLException {
        close(rs, closeStatement, false);
    }

    /**
     * 
     * @param rs
     * @param closeStatement 
     * @param closeConnection
     * @throws IllegalArgumentException if {@code closeStatement = false} while {@code closeConnection = true}.
     * @throws UncheckedSQLException
     */
    public static void close(final ResultSet rs, final boolean closeStatement, final boolean closeConnection)
            throws IllegalArgumentException, UncheckedSQLException {
        if (closeConnection && closeStatement == false) {
            throw new IllegalArgumentException("'closeStatement' can't be false while 'closeConnection' is true");
        }

        if (rs == null) {
            return;
        }

        Connection conn = null;
        Statement stmt = null;

        try {
            if (closeStatement || closeConnection) {
                stmt = rs.getStatement();
            }

            if (closeConnection && stmt != null) {
                conn = stmt.getConnection();
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            close(rs, stmt, conn);
        }
    }

    public static void close(final Statement stmt) throws UncheckedSQLException {
        if (stmt != null) {
            try {
                if (stmt instanceof PreparedStatement) {
                    try {
                        ((PreparedStatement) stmt).clearParameters();
                    } catch (SQLException e) {
                        logger.error("Failed to clear parameters", e);
                    }
                }

                stmt.close();
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            }
        }
    }

    public static void close(final Connection conn) throws UncheckedSQLException {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            }
        }
    }

    public static void close(final ResultSet rs, final Statement stmt) throws UncheckedSQLException {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            try {
                if (stmt != null) {
                    if (stmt instanceof PreparedStatement) {
                        try {
                            ((PreparedStatement) stmt).clearParameters();
                        } catch (SQLException e) {
                            logger.error("Failed to clear parameters", e);
                        }
                    }
                    stmt.close();
                }
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            }
        }
    }

    public static void close(final Statement stmt, final Connection conn) throws UncheckedSQLException {
        try {
            if (stmt != null) {
                if (stmt instanceof PreparedStatement) {
                    try {
                        ((PreparedStatement) stmt).clearParameters();
                    } catch (SQLException e) {
                        logger.error("Failed to clear parameters", e);
                    }
                }
                stmt.close();
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            }
        }
    }

    public static void close(final ResultSet rs, final Statement stmt, final Connection conn) throws UncheckedSQLException {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            throw new UncheckedSQLException(e);
        } finally {
            try {
                if (stmt != null) {
                    if (stmt instanceof PreparedStatement) {
                        try {
                            ((PreparedStatement) stmt).clearParameters();
                        } catch (SQLException e) {
                            logger.error("Failed to clear parameters", e);
                        }
                    }
                    stmt.close();
                }
            } catch (SQLException e) {
                throw new UncheckedSQLException(e);
            } finally {
                try {
                    if (conn != null) {
                        conn.close();
                    }
                } catch (SQLException e) {
                    throw new UncheckedSQLException(e);
                }
            }
        }
    }

    /**
     * Unconditionally close an ResultSet.
     * 

* Equivalent to {@link ResultSet#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param rs */ public static void closeQuietly(final ResultSet rs) { closeQuietly(rs, null, null); } public static void closeQuietly(final ResultSet rs, final boolean closeStatement) throws UncheckedSQLException { closeQuietly(rs, closeStatement, false); } /** * * @param rs * @param closeStatement * @param closeConnection * @throws IllegalArgumentException if {@code closeStatement = false} while {@code closeConnection = true}. */ public static void closeQuietly(final ResultSet rs, final boolean closeStatement, final boolean closeConnection) throws IllegalArgumentException { if (closeConnection && closeStatement == false) { throw new IllegalArgumentException("'closeStatement' can't be false while 'closeConnection' is true"); } if (rs == null) { return; } Connection conn = null; Statement stmt = null; try { if (closeStatement || closeConnection) { stmt = rs.getStatement(); } if (closeConnection && stmt != null) { conn = stmt.getConnection(); } } catch (SQLException e) { logger.error("Failed to get Statement or Connection by ResultSet", e); } finally { closeQuietly(rs, stmt, conn); } } /** * Unconditionally close an Statement. *

* Equivalent to {@link Statement#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param stmt */ public static void closeQuietly(final Statement stmt) { closeQuietly(null, stmt, null); } /** * Unconditionally close an Connection. *

* Equivalent to {@link Connection#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param conn */ public static void closeQuietly(final Connection conn) { closeQuietly(null, null, conn); } /** * Unconditionally close the ResultSet, Statement. *

* Equivalent to {@link ResultSet#close()}, {@link Statement#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param rs * @param stmt */ public static void closeQuietly(final ResultSet rs, final Statement stmt) { closeQuietly(rs, stmt, null); } /** * Unconditionally close the Statement, Connection. *

* Equivalent to {@link Statement#close()}, {@link Connection#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param stmt * @param conn */ public static void closeQuietly(final Statement stmt, final Connection conn) { closeQuietly(null, stmt, conn); } /** * Unconditionally close the ResultSet, Statement, Connection. *

* Equivalent to {@link ResultSet#close()}, {@link Statement#close()}, {@link Connection#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param rs * @param stmt * @param conn */ public static void closeQuietly(final ResultSet rs, final Statement stmt, final Connection conn) { if (rs != null) { try { rs.close(); } catch (Exception e) { logger.error("Failed to close ResultSet", e); } } if (stmt != null) { if (stmt instanceof PreparedStatement) { try { ((PreparedStatement) stmt).clearParameters(); } catch (Exception e) { logger.error("Failed to clear parameters", e); } } try { stmt.close(); } catch (Exception e) { logger.error("Failed to close Statement", e); } } if (conn != null) { try { conn.close(); } catch (Exception e) { logger.error("Failed to close Connection", e); } } } /** * * @param rs * @param n the count of row to move ahead. * @return the number skipped. * @throws SQLException */ public static int skip(final ResultSet rs, int n) throws SQLException { return skip(rs, (long) n); } /** * * @param rs * @param n the count of row to move ahead. * @return the number skipped. * @throws SQLException * @see {@link ResultSet#absolute(int)} */ public static int skip(final ResultSet rs, long n) throws SQLException { if (n <= 0) { return 0; } else if (n == 1) { return rs.next() == true ? 1 : 0; } else { final int currentRow = rs.getRow(); if (n <= Integer.MAX_VALUE) { try { if (n > Integer.MAX_VALUE - rs.getRow()) { while (n-- > 0L && rs.next()) { } } else { rs.absolute((int) n + rs.getRow()); } } catch (SQLException e) { while (n-- > 0L && rs.next()) { } } } else { while (n-- > 0L && rs.next()) { } } return rs.getRow() - currentRow; } } public static int getColumnCount(ResultSet rs) throws SQLException { return rs.getMetaData().getColumnCount(); } /** * * @param conn * @param tableName * @return * @throws SQLException */ public static List getColumnNameList(final Connection conn, final String tableName) throws SQLException { final String query = "SELECT * FROM " + tableName + " WHERE 1 > 2"; PreparedStatement stmt = null; ResultSet rs = null; try { stmt = prepareStatement(conn, query); rs = stmt.executeQuery(); final ResultSetMetaData metaData = rs.getMetaData(); final int columnCount = metaData.getColumnCount(); final List columnNameList = new ArrayList<>(columnCount); for (int i = 1, n = columnCount + 1; i < n; i++) { columnNameList.add(metaData.getColumnName(i)); } return columnNameList; } finally { closeQuietly(rs, stmt); } } /** * * @param rs * @return * @throws SQLException */ public static List getColumnLabelList(ResultSet rs) throws SQLException { final ResultSetMetaData metaData = rs.getMetaData(); final int columnCount = metaData.getColumnCount(); final List labelList = new ArrayList<>(columnCount); for (int i = 1, n = columnCount + 1; i < n; i++) { labelList.add(getColumnLabel(metaData, i)); } return labelList; } public static String getColumnLabel(final ResultSetMetaData rsmd, final int columnIndex) throws SQLException { final String result = rsmd.getColumnLabel(columnIndex); return N.isNullOrEmpty(result) ? rsmd.getColumnName(columnIndex) : result; } public static Object getColumnValue(final ResultSet rs, final int columnIndex) throws SQLException { // Copied from JdbcUtils#getResultSetValue(ResultSet, int) in SpringJdbc under Apache License, Version 2.0. // final Object obj = rs.getObject(columnIndex); // // if (obj == null) { // return obj; // } // // final String className = obj.getClass().getName(); // // if (obj instanceof Blob) { // final Blob blob = (Blob) obj; // return blob.getBytes(1, (int) blob.length()); // } else if (obj instanceof Clob) { // final Clob clob = (Clob) obj; // return clob.getSubString(1, (int) clob.length()); // } else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { // return rs.getTimestamp(columnIndex); // } else if (className.startsWith("oracle.sql.DATE")) { // final String columnClassName = rs.getMetaData().getColumnClassName(columnIndex); // // if ("java.sql.Timestamp".equals(columnClassName) || "oracle.sql.TIMESTAMP".equals(columnClassName)) { // return rs.getTimestamp(columnIndex); // } else { // return rs.getDate(columnIndex); // } // } else if (obj instanceof java.sql.Date) { // if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(columnIndex))) { // return rs.getTimestamp(columnIndex); // } // } // // return obj; return rs.getObject(columnIndex); } public static Object getColumnValue(final ResultSet rs, final String columnLabel) throws SQLException { // Copied from JdbcUtils#getResultSetValue(ResultSet, int) in SpringJdbc under Apache License, Version 2.0. // final Object obj = rs.getObject(columnLabel); // // if (obj == null) { // return obj; // } // // final String className = obj.getClass().getName(); // // if (obj instanceof Blob) { // final Blob blob = (Blob) obj; // return blob.getBytes(1, (int) blob.length()); // } else if (obj instanceof Clob) { // final Clob clob = (Clob) obj; // return clob.getSubString(1, (int) clob.length()); // } else if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { // return rs.getTimestamp(columnLabel); // } else if (className.startsWith("oracle.sql.DATE")) { // final int columnIndex = JdbcUtil.getColumnLabelList(rs).indexOf(columnLabel); // // if (columnIndex >= 0) { // final String columnClassName = rs.getMetaData().getColumnClassName(columnIndex + 1); // // if ("java.sql.Timestamp".equals(columnClassName) || "oracle.sql.TIMESTAMP".equals(columnClassName)) { // return rs.getTimestamp(columnLabel); // } else { // return rs.getDate(columnLabel); // } // } // } else if (obj instanceof java.sql.Date) { // final int columnIndex = JdbcUtil.getColumnLabelList(rs).indexOf(columnLabel); // // if (columnIndex >= 0) { // if ("java.sql.Timestamp".equals(rs.getMetaData().getColumnClassName(columnIndex + 1))) { // return rs.getTimestamp(columnLabel); // } // } // } // // return obj; return rs.getObject(columnLabel); } public static T getColumnValue(final Class targetClass, final ResultSet rs, final int columnIndex) throws SQLException { return N. typeOf(targetClass).get(rs, columnIndex); } public static T getColumnValue(final Class targetClass, final ResultSet rs, final String columnLabel) throws SQLException { return N. typeOf(targetClass).get(rs, columnLabel); } /** * The transaction will be shared by {@code JdbcUtil.beginTransaction(javax.sql.DataSource, IsolationLevel)} in the same thread for the same {@code DataSource}, but not auto-shared by {@code JdbcUtil.prepareQuery/prepareCallableQuery(javax.sql.DataSource, ...)}. * To include {@code jdbcUtil.prepareQuery/prepareCallableQuery} in the transaction, need to call {@code JdbcUtil.prepareQuery/prepareCallableQuery(transaction.connection(), ...)}. * *
* Here is the general code pattern to work with {@code SimpleTransaction}. * *

     * 
     * public void doSomethingA() {
     *     ...
     *     final SimpleTransaction tranA = JdbcUtil.beginTransacion(dataSource1, isolation);
     *     final Connection conn = tranA.connection();
     *     try {
     *         // do your work with the conn...
     *         ...
     *         doSomethingB(); // Share the same transaction 'tranA' because they're in the same thread and start transaction with same DataSource 'dataSource1'.
     *         ...
     *         doSomethingC(); // won't share the same transaction 'tranA' although they're in the same thread but start transaction with different DataSources.
     *         ...
     *         tranA.commit();
     *     } finally {
     *         tranA.rollbackIfNotCommitted();
     *     }
     * }
     * 
     * public void doSomethingB() {
     *     ...
     *     final SimpleTransaction tranB = JdbcUtil.beginTransacion(dataSource1, isolation);
     *     final Connection conn = tranB.connection();
     *     try {
     *         // do your work with the conn...
     *         ...
     *         tranB.commit();
     *     } finally {
     *         tranB.rollbackIfNotCommitted();
     *     }
     * }
     * 
     * public void doSomethingC() {
     *     ...
     *     final SimpleTransaction tranC = JdbcUtil.beginTransacion(dataSource2, isolation);
     *     final Connection conn = tranC.connection();
     *     try {
     *         // do your work with the conn...
     *         ...
     *         tranC.commit();
     *     } finally {
     *         tranC.rollbackIfNotCommitted();
     *     }
     * }
     * 
* * * It's incorrect to use flag to identity the transaction should be committed or rolled back. * Don't write below code: *
     * 
     * public void doSomethingA() {
     *     ...
     *     final SimpleTransaction tranA = JdbcUtil.beginTransacion(dataSource1, isolation);
     *     final Connection conn = tranA.connection();
     *     boolean flagToCommit = false;
     *     try {
     *         // do your work with the conn...
     *         ...
     *         flagToCommit = true;
     *     } finally {
     *         if (flagToCommit) {
     *             tranA.commit();
     *         } else {
     *             tranA.rollbackIfNotCommitted();
     *         }
     *     }
     * }
     * 
     * 
* * @param dataSource * @param isolationLevel * @return * @throws SQLException */ public static SimpleTransaction beginTransaction(final javax.sql.DataSource dataSource, final IsolationLevel isolationLevel) throws UncheckedSQLException { return beginTransaction(dataSource, isolationLevel, false); } /** * * @param conn * @param isolationLevel * @return * @throws UncheckedSQLException * @see {@link #beginTransaction(javax.sql.DataSource, IsolationLevel)} */ public static SimpleTransaction beginTransaction(final Connection conn, final IsolationLevel isolationLevel) throws UncheckedSQLException { return beginTransaction(conn, isolationLevel, false); } /** * * @param dataSource * @param isolationLevel * @param autoSharedByPrepareQuery If it's true, the {@code Connection} used to start/create the transaction will be used by coming {@code JdbcUtil.prepareQuery/prepareCallableQuery(javax.sql.DataSource dataSource, ...)} called with the same {@code DataSource} in the same thread, without explicit required. * @return * @throws UncheckedSQLException */ static SimpleTransaction beginTransaction(final javax.sql.DataSource dataSource, final IsolationLevel isolationLevel, final boolean autoSharedByPrepareQuery) throws UncheckedSQLException { N.checkArgNotNull(dataSource); N.checkArgNotNull(isolationLevel); SimpleTransaction tran = SimpleTransaction.getTransaction(dataSource); if (tran == null) { Connection conn = null; boolean noException = false; try { conn = getConnection(dataSource); tran = new SimpleTransaction(dataSource, conn, isolationLevel, true, autoSharedByPrepareQuery); tran.incrementAndGetRef(isolationLevel); noException = true; } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { if (noException == false) { releaseConnection(conn, dataSource); } } logger.info("Create a new SimpleTransaction(id={})", tran.id()); SimpleTransaction.putTransaction(dataSource, tran); } else { logger.info("Reusing the existing SimpleTransaction(id={})", tran.id()); tran.incrementAndGetRef(isolationLevel); } return tran; } /** * * @param conn * @param isolationLevel * @param autoSharedByPrepareQuery If it's true, the {@code Connection} used to start/create the transaction will be used by coming {@code JdbcUtil.prepareQuery/prepareCallableQuery(javax.sql.DataSource dataSource, ...)} called with the same {@code DataSource} in the same thread, without explicit required. * @return * @return * @throws UncheckedSQLException */ static SimpleTransaction beginTransaction(final Connection conn, final IsolationLevel isolationLevel, final boolean autoSharedByPrepareQuery) throws UncheckedSQLException { N.checkArgNotNull(conn); N.checkArgNotNull(isolationLevel); SimpleTransaction tran = SimpleTransaction.getTransaction(conn); if (tran == null) { try { tran = new SimpleTransaction(null, conn, isolationLevel, false, autoSharedByPrepareQuery); tran.incrementAndGetRef(isolationLevel); } catch (SQLException e) { throw new UncheckedSQLException(e); } logger.info("Create a new transaction(id={})", tran.id()); SimpleTransaction.putTransaction(conn, tran); } else { logger.info("Reusing the existing SimpleTransaction(id={})", tran.id()); tran.incrementAndGetRef(isolationLevel); } return tran; } public static PreparedQuery prepareQuery(final javax.sql.DataSource ds, final String sql) throws SQLException { PreparedQuery result = null; Connection conn = null; try { conn = getConnection(ds); result = prepareQuery(conn, sql).onClose(createCloseHandler(conn, ds)); } finally { if (result == null) { releaseConnection(conn, ds); } } return result; } public static PreparedQuery prepareQuery(final javax.sql.DataSource ds, final String sql, final boolean autoGeneratedKeys) throws SQLException { PreparedQuery result = null; Connection conn = null; try { conn = getConnection(ds); result = prepareQuery(conn, sql, autoGeneratedKeys).onClose(createCloseHandler(conn, ds)); } finally { if (result == null) { releaseConnection(conn, ds); } } return result; } /** * Never write below code because it will definitely cause {@code Connection} leak: *
     * 
     * JdbcUtil.prepareQuery(dataSource.getConnection(), sql);
     * 
     * 
* * @param conn the specified {@code conn} won't be close after this query is executed. * @param sql * @return * @throws SQLException */ public static PreparedQuery prepareQuery(final Connection conn, final String sql) throws SQLException { return new PreparedQuery(conn.prepareStatement(sql)); } /** * Never write below code because it will definitely cause {@code Connection} leak: *
     * 
     * JdbcUtil.prepareQuery(dataSource.getConnection(), sql, autoGeneratedKeys);
     * 
     * 
* * @param conn the specified {@code conn} won't be close after this query is executed. * @param sql * @param autoGeneratedKeys * @return * @throws SQLException */ public static PreparedQuery prepareQuery(final Connection conn, final String sql, final boolean autoGeneratedKeys) throws SQLException { return new PreparedQuery(conn.prepareStatement(sql, autoGeneratedKeys ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS)); } /** * * @param ds * @param stmtCreator the created {@code PreparedStatement} will be closed after any execution methods in {@code PreparedQuery/PreparedCallableQuery} is called. * An execution method is a method which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/.... * @return * @throws SQLException * @see {@link JdbcUtil#prepareStatement(Connection, String, Object...)} */ @SuppressWarnings("resource") public static PreparedQuery prepareQuery(final javax.sql.DataSource ds, final Try.Function stmtCreator) throws SQLException { PreparedQuery result = null; Connection conn = null; try { conn = getConnection(ds); result = new PreparedQuery(stmtCreator.apply(conn)).onClose(createCloseHandler(conn, ds)); } finally { if (result == null) { releaseConnection(conn, ds); } } return result; } /** * Never write below code because it will definitely cause {@code Connection} leak: *
     * 
     * JdbcUtil.prepareQuery(dataSource.getConnection(), stmtCreator);
     * 
     * 
* * @param conn the specified {@code conn} won't be close after this query is executed. * @param stmtCreator the created {@code PreparedStatement} will be closed after any execution methods in {@code PreparedQuery/PreparedCallableQuery} is called. * An execution method is a method which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/.... * @return * @throws SQLException * @see {@link JdbcUtil#prepareStatement(Connection, String, Object...)} */ public static PreparedQuery prepareQuery(final Connection conn, final Try.Function stmtCreator) throws SQLException { return new PreparedQuery(stmtCreator.apply(conn)); } public static PreparedCallableQuery prepareCallableQuery(final javax.sql.DataSource ds, final String sql) throws SQLException { PreparedCallableQuery result = null; Connection conn = null; try { conn = getConnection(ds); result = prepareCallableQuery(conn, sql).onClose(createCloseHandler(conn, ds)); } finally { if (result == null) { releaseConnection(conn, ds); } } return result; } /** * Never write below code because it will definitely cause {@code Connection} leak: *
     * 
     * JdbcUtil.prepareCallableQuery(dataSource.getConnection(), sql);
     * 
     * 
* * @param conn the specified {@code conn} won't be close after this query is executed. * @param sql * @return * @throws SQLException */ public static PreparedCallableQuery prepareCallableQuery(final Connection conn, final String sql) throws SQLException { return new PreparedCallableQuery(conn.prepareCall(sql)); } /** * @param ds * @param stmtCreator the created {@code CallableStatement} will be closed after any execution methods in {@code PreparedQuery/PreparedCallableQuery} is called. * An execution method is a method which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/.... * @return * @throws SQLException * @see {@link JdbcUtil#prepareCall(Connection, String, Object...)} */ @SuppressWarnings("resource") public static PreparedCallableQuery prepareCallableQuery(final javax.sql.DataSource ds, final Try.Function stmtCreator) throws SQLException { PreparedCallableQuery result = null; Connection conn = null; try { conn = getConnection(ds); result = new PreparedCallableQuery(stmtCreator.apply(conn)).onClose(createCloseHandler(conn, ds)); } finally { if (result == null) { releaseConnection(conn, ds); } } return result; } /** * Never write below code because it will definitely cause {@code Connection} leak: *
     * 
     * JdbcUtil.prepareCallableQuery(dataSource.getConnection(), stmtCreator);
     * 
     * 
* * @param conn the specified {@code conn} won't be close after this query is executed. * @param stmtCreator the created {@code CallableStatement} will be closed after any execution methods in {@code PreparedQuery/PreparedCallableQuery} is called. * An execution method is a method which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/.... * @return * @throws SQLException * @see {@link JdbcUtil#prepareCall(Connection, String, Object...)} */ public static PreparedCallableQuery prepareCallableQuery(final Connection conn, final Try.Function stmtCreator) throws SQLException { return new PreparedCallableQuery(stmtCreator.apply(conn)); } @SafeVarargs public static PreparedStatement prepareStatement(final Connection conn, final String sql, final Object... parameters) throws SQLException { final NamedSQL namedSQL = NamedSQL.parse(sql); final PreparedStatement stmt = conn.prepareStatement(namedSQL.getPureSQL()); if (N.notNullOrEmpty(parameters)) { StatementSetter.DEFAULT.setParameters(namedSQL, stmt, parameters); } return stmt; } @SafeVarargs public static CallableStatement prepareCall(final Connection conn, final String sql, final Object... parameters) throws SQLException { final NamedSQL namedSQL = NamedSQL.parse(sql); final CallableStatement stmt = conn.prepareCall(namedSQL.getPureSQL()); if (N.notNullOrEmpty(parameters)) { StatementSetter.DEFAULT.setParameters(namedSQL, stmt, parameters); } return stmt; } public static PreparedStatement batchPrepareStatement(final Connection conn, final String sql, final List parametersList) throws SQLException { final NamedSQL namedSQL = NamedSQL.parse(sql); final PreparedStatement stmt = conn.prepareStatement(namedSQL.getPureSQL()); for (Object parameters : parametersList) { StatementSetter.DEFAULT.setParameters(namedSQL, stmt, N.asArray(parameters)); stmt.addBatch(); } return stmt; } public static CallableStatement batchPrepareCall(final Connection conn, final String sql, final List parametersList) throws SQLException { final NamedSQL namedSQL = NamedSQL.parse(sql); final CallableStatement stmt = conn.prepareCall(namedSQL.getPureSQL()); for (Object parameters : parametersList) { StatementSetter.DEFAULT.setParameters(namedSQL, stmt, N.asArray(parameters)); stmt.addBatch(); } return stmt; } @SafeVarargs public static DataSet executeQuery(final Connection conn, final String sql, final Object... parameters) throws SQLException { PreparedStatement stmt = null; ResultSet rs = null; try { stmt = prepareStatement(conn, sql, parameters); stmt.setFetchDirection(ResultSet.FETCH_FORWARD); rs = stmt.executeQuery(); return extractData(rs); } finally { closeQuietly(rs, stmt); } } public static DataSet executeQuery(final PreparedStatement stmt) throws SQLException { ResultSet rs = null; try { rs = stmt.executeQuery(); return extractData(rs); } finally { closeQuietly(rs); } } @SafeVarargs public static int executeUpdate(final Connection conn, final String sql, final Object... parameters) throws SQLException { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, sql, parameters); return stmt.executeUpdate(); } finally { closeQuietly(stmt); } } /** * * @param conn * @param sql * @param listOfParameters * @return * @throws SQLException */ public static int executeBatchUpdate(final Connection conn, final String sql, final List listOfParameters) throws SQLException { return executeBatchUpdate(conn, sql, listOfParameters, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * * @param conn * @param sql * @param listOfParameters * @param batchSize. * @return * @throws SQLException */ public static int executeBatchUpdate(final Connection conn, final String sql, final List listOfParameters, final int batchSize) throws SQLException { N.checkArgNotNull(conn); N.checkArgNotNull(sql); N.checkArgPositive(batchSize, "batchSize"); if (N.isNullOrEmpty(listOfParameters)) { return 0; } final NamedSQL namedSQL = NamedSQL.parse(sql); PreparedStatement stmt = null; try { stmt = conn.prepareStatement(namedSQL.getPureSQL()); int res = 0; int idx = 0; for (Object parameters : listOfParameters) { StatementSetter.DEFAULT.setParameters(namedSQL, stmt, N.asArray(parameters)); stmt.addBatch(); if (++idx % batchSize == 0) { res += stmt.executeUpdate(); stmt.clearBatch(); } } if (idx % batchSize != 0) { res += stmt.executeUpdate(); stmt.clearBatch(); } return res; } finally { JdbcUtil.closeQuietly(stmt); } } @SafeVarargs public static boolean execute(final Connection conn, final String sql, final Object... parameters) throws SQLException { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, sql, parameters); return stmt.execute(); } finally { closeQuietly(stmt); } } /** * * @param rs * @return * @throws SQLException */ public static DataSet extractData(final ResultSet rs) throws SQLException { return extractData(rs, false); } public static DataSet extractData(final ResultSet rs, final boolean closeResultSet) throws SQLException { return extractData(rs, 0, Integer.MAX_VALUE, closeResultSet); } public static DataSet extractData(final ResultSet rs, final int offset, final int count) throws SQLException { return extractData(rs, offset, count, false); } public static DataSet extractData(final ResultSet rs, final int offset, final int count, final boolean closeResultSet) throws SQLException { return extractData(rs, offset, count, RowFilter.ALWAYS_TRUE, closeResultSet); } public static DataSet extractData(final ResultSet rs, int offset, int count, final RowFilter filter, final boolean closeResultSet) throws SQLException { N.checkArgNotNull(rs, "ResultSet"); N.checkArgNotNegative(offset, "offset"); N.checkArgNotNegative(count, "count"); N.checkArgNotNull(filter, "filter"); try { // TODO [performance improvement]. it will improve performance a lot if MetaData is cached. final ResultSetMetaData rsmd = rs.getMetaData(); final int columnCount = rsmd.getColumnCount(); final List columnNameList = new ArrayList<>(columnCount); final List> columnList = new ArrayList<>(columnCount); for (int i = 0; i < columnCount;) { columnNameList.add(JdbcUtil.getColumnLabel(rsmd, ++i)); columnList.add(new ArrayList<>()); } JdbcUtil.skip(rs, offset); while (count > 0 && rs.next()) { if (filter == null || filter.test(rs)) { for (int i = 0; i < columnCount;) { columnList.get(i).add(JdbcUtil.getColumnValue(rs, ++i)); } count--; } } // return new RowDataSet(null, entityClass, columnNameList, columnList); return new RowDataSet(columnNameList, columnList); } finally { if (closeResultSet) { closeQuietly(rs); } } } public static boolean doesTableExist(final Connection conn, final String tableName) { try { executeQuery(conn, "SELECT 1 FROM " + tableName + " WHERE 1 > 2"); return true; } catch (SQLException e) { if (isTableNotExistsException(e)) { return false; } throw new UncheckedSQLException(e); } } /** * Returns {@code true} if succeed to create table, otherwise {@code false} is returned. * * @param conn * @param tableName * @param schema * @return */ public static boolean createTableIfNotExists(final Connection conn, final String tableName, final String schema) { if (doesTableExist(conn, tableName)) { return false; } try { execute(conn, schema); return true; } catch (SQLException e) { return false; } } /** * Returns {@code true} if succeed to drop table, otherwise {@code false} is returned. * * @param conn * @param tableName * @return */ public static boolean dropTableIfExists(final Connection conn, final String tableName) { try { if (doesTableExist(conn, tableName)) { execute(conn, "DROP TABLE " + tableName); return true; } } catch (SQLException e) { // ignore. } return false; } /** * Don't cache or reuse the returned {@code BiRowMapper} instance. * * @param targetType Array/List/Map or Entity with getter/setter methods. * @return * @deprecated replaced by {@code BiRowMapper#to(Class)} in JDK 1.8 or above. */ @Deprecated public static BiRowMapper createBiRowMapperByTargetClass(final Class targetClass) { if (Object[].class.isAssignableFrom(targetClass)) { return new BiRowMapper() { @Override public T apply(ResultSet rs, List columnLabelList) throws SQLException { final int columnCount = columnLabelList.size(); final Object[] a = Array.newInstance(targetClass.getComponentType(), columnCount); for (int i = 0; i < columnCount; i++) { a[i] = JdbcUtil.getColumnValue(rs, i + 1); } return (T) a; } }; } else if (List.class.isAssignableFrom(targetClass)) { return new BiRowMapper() { private final boolean isListOrArrayList = targetClass.equals(List.class) || targetClass.equals(ArrayList.class); @Override public T apply(ResultSet rs, List columnLabelList) throws SQLException { final int columnCount = columnLabelList.size(); final List c = isListOrArrayList ? new ArrayList(columnCount) : (List) N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { c.add(JdbcUtil.getColumnValue(rs, i + 1)); } return (T) c; } }; } else if (Map.class.isAssignableFrom(targetClass)) { return new BiRowMapper() { private final boolean isMapOrHashMap = targetClass.equals(Map.class) || targetClass.equals(HashMap.class); private final boolean isLinkedHashMap = targetClass.equals(LinkedHashMap.class); @Override public T apply(ResultSet rs, List columnLabelList) throws SQLException { final int columnCount = columnLabelList.size(); final Map m = isMapOrHashMap ? new HashMap(columnCount) : (isLinkedHashMap ? new LinkedHashMap(columnCount) : (Map) N.newInstance(targetClass)); for (int i = 0; i < columnCount; i++) { m.put(columnLabelList.get(i), JdbcUtil.getColumnValue(rs, i + 1)); } return (T) m; } }; } else if (ClassUtil.isEntity(targetClass)) { return new BiRowMapper() { private final boolean isDirtyMarker = ClassUtil.isDirtyMarker(targetClass); private volatile String[] columnLabels = null; private volatile Method[] propSetters; private volatile Type[] columnTypes = null; @Override public T apply(ResultSet rs, List columnLabelList) throws SQLException { final int columnCount = columnLabelList.size(); String[] columnLabels = this.columnLabels; Method[] propSetters = this.propSetters; Type[] columnTypes = this.columnTypes; if (columnLabels == null) { columnLabels = columnLabelList.toArray(new String[columnLabelList.size()]); this.columnLabels = columnLabels; } if (columnTypes == null || propSetters == null) { final EntityInfo entityInfo = ParserUtil.getEntityInfo(targetClass); final Map column2FieldNameMap = getColumn2FieldNameMap(targetClass); propSetters = new Method[columnCount]; columnTypes = new Type[columnCount]; for (int i = 0; i < columnCount; i++) { propSetters[i] = ClassUtil.getPropSetMethod(targetClass, columnLabels[i]); if (propSetters[i] == null) { String fieldName = column2FieldNameMap.get(columnLabels[i]); if (N.isNullOrEmpty(fieldName)) { fieldName = column2FieldNameMap.get(columnLabels[i].toLowerCase()); } if (N.notNullOrEmpty(fieldName)) { propSetters[i] = ClassUtil.getPropSetMethod(targetClass, fieldName); } } if (propSetters[i] == null) { columnLabels[i] = null; throw new IllegalArgumentException( "No property in class: " + ClassUtil.getCanonicalClassName(targetClass) + " mapping to column: " + columnLabels[i]); } else { columnTypes[i] = entityInfo.getPropInfo(columnLabels[i]).type; } } this.propSetters = propSetters; this.columnTypes = columnTypes; } final Object entity = N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { if (columnLabels[i] == null) { continue; } ClassUtil.setPropValue(entity, propSetters[i], columnTypes[i].get(rs, i + 1)); } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } return (T) entity; } }; } else { return new BiRowMapper() { private final Type targetType = N.typeOf(targetClass); private int columnCount = 0; @Override public T apply(ResultSet rs, List columnLabelList) throws SQLException { if (columnCount != 1 && (columnCount = columnLabelList.size()) != 1) { throw new IllegalArgumentException( "It's not supported to retrieve value from multiple columns: " + columnLabelList + " for type: " + targetClass); } return targetType.get(rs, 1); } }; } } public static List getNamedParameters(String sql) { return NamedSQL.parse(sql).getNamedParameters(); } /** * Imports the data from DataSet to database. * * @param dataset * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Connection conn, final String insertSQL) throws UncheckedSQLException { return importData(dataset, dataset.columnNameList(), conn, insertSQL); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final Connection conn, final String insertSQL) throws UncheckedSQLException { return importData(dataset, selectColumnNames, 0, dataset.size(), conn, insertSQL); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final Connection conn, final String insertSQL) throws UncheckedSQLException { return importData(dataset, selectColumnNames, offset, count, conn, insertSQL, 200, 0); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval) throws UncheckedSQLException { return importData(dataset, selectColumnNames, offset, count, Fn.alwaysTrue(), conn, insertSQL, batchSize, batchInterval); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param filter * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final Try.Predicate filter, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(dataset, selectColumnNames, offset, count, filter, stmt, batchSize, batchInterval); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } /** * Imports the data from DataSet to database. * * @param dataset * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final Connection conn, final String insertSQL, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, 0, dataset.size(), conn, insertSQL, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final Connection conn, final String insertSQL, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, offset, count, conn, insertSQL, 200, 0, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, offset, count, Fn.alwaysTrue(), conn, insertSQL, batchSize, batchInterval, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param filter * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final Try.Predicate filter, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Map columnTypeMap) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(dataset, offset, count, filter, stmt, batchSize, batchInterval, columnTypeMap); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } /** * Imports the data from DataSet to database. * * @param dataset * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param stmtSetter * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Connection conn, final String insertSQL, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, 0, dataset.size(), conn, insertSQL, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param stmtSetter * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final Connection conn, final String insertSQL, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, offset, count, conn, insertSQL, 200, 0, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @param stmtSetter * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, offset, count, Fn.alwaysTrue(), conn, insertSQL, batchSize, batchInterval, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param filter * @param conn * @param insertSQL the column order in the sql must be consistent with the column order in the DataSet. Here is sample about how to create the sql: *

        List columnNameList = new ArrayList<>(dataset.columnNameList());
        columnNameList.retainAll(yourSelectColumnNames);        
        String sql = RE.insert(columnNameList).into(tableName).sql();  
     * 
* @param batchSize * @param batchInterval * @param stmtSetter * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final Try.Predicate filter, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(dataset, offset, count, filter, stmt, batchSize, batchInterval, stmtSetter); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } /** * Imports the data from DataSet to database. * * @param dataset * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final PreparedStatement stmt) throws UncheckedSQLException { return importData(dataset, dataset.columnNameList(), stmt); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final PreparedStatement stmt) throws UncheckedSQLException { return importData(dataset, selectColumnNames, 0, dataset.size(), stmt); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final PreparedStatement stmt) throws UncheckedSQLException { return importData(dataset, selectColumnNames, offset, count, stmt, 200, 0); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final PreparedStatement stmt, final int batchSize, final int batchInterval) throws UncheckedSQLException { return importData(dataset, selectColumnNames, offset, count, Fn.alwaysTrue(), stmt, batchSize, batchInterval); } /** * Imports the data from DataSet to database. * * @param dataset * @param selectColumnNames * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param batchSize * @param batchInterval * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final Collection selectColumnNames, final int offset, final int count, final Try.Predicate filter, final PreparedStatement stmt, final int batchSize, final int batchInterval) throws UncheckedSQLException, E { final Type objType = N.typeOf(Object.class); final Map> columnTypeMap = new HashMap<>(); for (String propName : selectColumnNames) { columnTypeMap.put(propName, objType); } return importData(dataset, offset, count, filter, stmt, batchSize, batchInterval, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final PreparedStatement stmt, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, 0, dataset.size(), stmt, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final PreparedStatement stmt, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, offset, count, stmt, 200, 0, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param columnTypeMap * @param filter * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Map columnTypeMap) throws UncheckedSQLException { return importData(dataset, offset, count, Fn.alwaysTrue(), stmt, batchSize, batchInterval, columnTypeMap); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param filter * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param batchSize * @param batchInterval * @param columnTypeMap * @return * @throws UncheckedSQLException */ @SuppressWarnings("rawtypes") public static int importData(final DataSet dataset, final int offset, final int count, final Try.Predicate filter, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Map columnTypeMap) throws UncheckedSQLException, E { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); int result = 0; try { final int columnCount = columnTypeMap.size(); final List columnNameList = dataset.columnNameList(); final int[] columnIndexes = new int[columnCount]; final Type[] columnTypes = new Type[columnCount]; final Set columnNameSet = new HashSet<>(columnCount); int idx = 0; for (String columnName : columnNameList) { if (columnTypeMap.containsKey(columnName)) { columnIndexes[idx] = dataset.getColumnIndex(columnName); columnTypes[idx] = columnTypeMap.get(columnName); columnNameSet.add(columnName); idx++; } } if (columnNameSet.size() != columnTypeMap.size()) { final List keys = new ArrayList<>(columnTypeMap.keySet()); keys.removeAll(columnNameSet); throw new AbacusException(keys + " are not included in titles: " + N.toString(columnNameList)); } final Object[] row = filter == null ? null : new Object[columnCount]; for (int i = offset, size = dataset.size(); result < count && i < size; i++) { dataset.absolute(i); if (filter == null) { for (int j = 0; j < columnCount; j++) { columnTypes[j].set(stmt, j + 1, dataset.get(columnIndexes[j])); } } else { for (int j = 0; j < columnCount; j++) { row[j] = dataset.get(columnIndexes[j]); } if (filter.test(row) == false) { continue; } for (int j = 0; j < columnCount; j++) { columnTypes[j].set(stmt, j + 1, row[j]); } } stmt.addBatch(); if ((++result % batchSize) == 0) { stmt.executeBatch(); stmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } if ((result % batchSize) > 0) { stmt.executeBatch(); stmt.clearBatch(); } } catch (SQLException e) { throw new UncheckedSQLException(e); } return result; } public static int importData(final DataSet dataset, final PreparedStatement stmt, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, 0, dataset.size(), stmt, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final PreparedStatement stmt, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, offset, count, stmt, 200, 0, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param columnTypeMap * @param offset * @param count * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param filter * @param stmtSetter * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return importData(dataset, offset, count, Fn.alwaysTrue(), stmt, batchSize, batchInterval, stmtSetter); } /** * Imports the data from DataSet to database. * * @param dataset * @param offset * @param count * @param filter * @param stmt the column order in the sql must be consistent with the column order in the DataSet. * @param batchSize * @param batchInterval * @param stmtSetter * @param columnTypeMap * @return * @throws UncheckedSQLException */ public static int importData(final DataSet dataset, final int offset, final int count, final Try.Predicate filter, final PreparedStatement stmt, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException, E { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); final int columnCount = dataset.columnNameList().size(); final Object[] row = new Object[columnCount]; int result = 0; try { for (int i = offset, size = dataset.size(); result < count && i < size; i++) { dataset.absolute(i); for (int j = 0; j < columnCount; j++) { row[j] = dataset.get(j); } if (filter != null && filter.test(row) == false) { continue; } stmtSetter.accept(stmt, row); stmt.addBatch(); if ((++result % batchSize) == 0) { stmt.executeBatch(); stmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } if ((result % batchSize) > 0) { stmt.executeBatch(); stmt.clearBatch(); } } catch (SQLException e) { throw new UncheckedSQLException(e); } return result; } public static long importData(final File file, final Connection conn, final String insertSQL, final Try.Function func) throws UncheckedSQLException, E { return importData(file, 0, Long.MAX_VALUE, conn, insertSQL, 200, 0, func); } public static long importData(final File file, final long offset, final long count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(file, offset, count, stmt, batchSize, batchInterval, func); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } public static long importData(final File file, final PreparedStatement stmt, final Try.Function func) throws UncheckedSQLException, E { return importData(file, 0, Long.MAX_VALUE, stmt, 200, 0, func); } /** * Imports the data from file to database. * * @param file * @param offset * @param count * @param stmt * @param batchSize * @param batchInterval * @param func convert line to the parameters for record insert. Returns a null array to skip the line. * @return * @throws UncheckedSQLException */ public static long importData(final File file, final long offset, final long count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { Reader reader = null; try { reader = new FileReader(file); return importData(reader, offset, count, stmt, batchSize, batchInterval, func); } catch (IOException e) { throw new UncheckedIOException(e); } finally { IOUtil.close(reader); } } public static long importData(final InputStream is, final Connection conn, final String insertSQL, final Try.Function func) throws UncheckedSQLException, E { return importData(is, 0, Long.MAX_VALUE, conn, insertSQL, 200, 0, func); } public static long importData(final InputStream is, final long offset, final long count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(is, offset, count, stmt, batchSize, batchInterval, func); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } public static long importData(final InputStream is, final PreparedStatement stmt, final Try.Function func) throws E { return importData(is, 0, Long.MAX_VALUE, stmt, 200, 0, func); } /** * Imports the data from file to database. * * @param is * @param offset * @param count * @param stmt * @param batchSize * @param batchInterval * @param func convert line to the parameters for record insert. Returns a null array to skip the line. * @return * @throws UncheckedSQLException */ public static long importData(final InputStream is, final long offset, final long count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { final Reader reader = new InputStreamReader(is); return importData(reader, offset, count, stmt, batchSize, batchInterval, func); } public static long importData(final Reader reader, final Connection conn, final String insertSQL, final Try.Function func) throws UncheckedSQLException, E { return importData(reader, 0, Long.MAX_VALUE, conn, insertSQL, 200, 0, func); } public static long importData(final Reader reader, final long offset, final long count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(reader, offset, count, stmt, batchSize, batchInterval, func); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } public static long importData(final Reader reader, final PreparedStatement stmt, final Try.Function func) throws E { return importData(reader, 0, Long.MAX_VALUE, stmt, 200, 0, func); } /** * Imports the data from file to database. * * @param reader * @param offset * @param count * @param stmt * @param batchSize * @param batchInterval * @param func convert line to the parameters for record insert. Returns a null array to skip the line. * @return * @throws UncheckedSQLException */ public static long importData(final Reader reader, long offset, final long count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); long result = 0; final BufferedReader br = Objectory.createBufferedReader(reader); try { while (offset-- > 0 && br.readLine() != null) { } String line = null; Object[] row = null; while (result < count && (line = br.readLine()) != null) { row = func.apply(line); if (row == null) { continue; } for (int i = 0, len = row.length; i < len; i++) { stmt.setObject(i + 1, row[i]); } stmt.addBatch(); if ((++result % batchSize) == 0) { stmt.executeBatch(); stmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } if ((result % batchSize) > 0) { stmt.executeBatch(); stmt.clearBatch(); } } catch (SQLException e) { throw new UncheckedSQLException(e); } catch (IOException e) { throw new UncheckedIOException(e); } finally { Objectory.recycle(br); } return result; } public static long importData(final Iterator iter, final Connection conn, final String insertSQL, final Try.Function func) throws UncheckedSQLException, E { return importData(iter, 0, Long.MAX_VALUE, conn, insertSQL, 200, 0, func); } public static long importData(final Iterator iter, final long offset, final long count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(iter, offset, count, stmt, batchSize, batchInterval, func); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } public static long importData(final Iterator iter, final PreparedStatement stmt, final Try.Function func) throws E { return importData(iter, 0, Long.MAX_VALUE, stmt, 200, 0, func); } /** * Imports the data from Iterator to database. * * @param iter * @param offset * @param count * @param stmt * @param batchSize * @param batchInterval * @param func convert element to the parameters for record insert. Returns a null array to skip the line. * @return * @throws UncheckedSQLException */ public static long importData(final Iterator iter, long offset, final long count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final Try.Function func) throws UncheckedSQLException, E { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); long result = 0; try { while (offset-- > 0 && iter.hasNext()) { iter.next(); } Object[] row = null; while (result < count && iter.hasNext()) { row = func.apply(iter.next()); if (row == null) { continue; } for (int i = 0, len = row.length; i < len; i++) { stmt.setObject(i + 1, row[i]); } stmt.addBatch(); if ((++result % batchSize) == 0) { stmt.executeBatch(); stmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } if ((result % batchSize) > 0) { stmt.executeBatch(); stmt.clearBatch(); } } catch (SQLException e) { throw new UncheckedSQLException(e); } return result; } public static long importData(final Iterator iter, final Connection conn, final String insertSQL, final JdbcUtil.BiParametersSetter stmtSetter) { return importData(iter, 0, Long.MAX_VALUE, conn, insertSQL, 200, 0, stmtSetter); } public static long importData(final Iterator iter, final long offset, final long count, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) { return importData(iter, offset, count, Fn.alwaysTrue(), conn, insertSQL, batchSize, batchInterval, stmtSetter); } /** * * @param iter * @param offset * @param count * @param filter * @param conn * @param insertSQL * @param batchSize * @param batchInterval * @param stmtSetter * @return * @throws UncheckedSQLException */ public static long importData(final Iterator iter, final long offset, final long count, final Try.Predicate filter, final Connection conn, final String insertSQL, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException, E { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, insertSQL); return importData(iter, offset, count, filter, stmt, batchSize, batchInterval, stmtSetter); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { JdbcUtil.closeQuietly(stmt); } } public static long importData(final Iterator iter, final PreparedStatement stmt, final JdbcUtil.BiParametersSetter stmtSetter) { return importData(iter, 0, Long.MAX_VALUE, stmt, 200, 0, stmtSetter); } public static long importData(final Iterator iter, long offset, final long count, final PreparedStatement stmt, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) { return importData(iter, offset, count, Fn.alwaysTrue(), stmt, batchSize, batchInterval, stmtSetter); } /** * Imports the data from Iterator to database. * * @param iter * @param offset * @param count * @param filter * @param stmt * @param batchSize * @param batchInterval * @param stmtSetter * @return * @throws UncheckedSQLException */ public static long importData(final Iterator iter, long offset, final long count, final Try.Predicate filter, final PreparedStatement stmt, final int batchSize, final int batchInterval, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException, E { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); long result = 0; try { while (offset-- > 0 && iter.hasNext()) { iter.next(); } T next = null; while (result < count && iter.hasNext()) { next = iter.next(); if (filter != null && filter.test(next) == false) { continue; } stmtSetter.accept(stmt, next); stmt.addBatch(); if ((++result % batchSize) == 0) { stmt.executeBatch(); stmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } if ((result % batchSize) > 0) { stmt.executeBatch(); stmt.clearBatch(); } } catch (SQLException e) { throw new UncheckedSQLException(e); } return result; } public static void parse(final Connection conn, final String sql, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(conn, sql, rowParser, Fn.emptyAction()); } public static void parse(final Connection conn, final String sql, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(conn, sql, 0, Long.MAX_VALUE, rowParser, onComplete); } public static void parse(final Connection conn, final String sql, final long offset, final long count, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(conn, sql, offset, count, rowParser, Fn.emptyAction()); } public static void parse(final Connection conn, final String sql, final long offset, final long count, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(conn, sql, offset, count, 0, 0, rowParser, onComplete); } public static void parse(final Connection conn, final String sql, final long offset, final long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(conn, sql, offset, count, processThreadNum, queueSize, rowParser, Fn.emptyAction()); } /** * Parse the ResultSet obtained by executing query with the specified Connection and sql. * * @param conn * @param sql * @param offset * @param count * @param processThreadNum new threads started to parse/process the lines/records * @param queueSize size of queue to save the processing records/lines loaded from source data. Default size is 1024. * @param rowParser * @param onComplete * @throws UncheckedSQLException */ public static void parse(final Connection conn, final String sql, final long offset, final long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { PreparedStatement stmt = null; try { stmt = prepareStatement(conn, sql); stmt.setFetchDirection(ResultSet.FETCH_FORWARD); stmt.setFetchSize(200); parse(stmt, offset, count, processThreadNum, queueSize, rowParser, onComplete); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { closeQuietly(stmt); } } public static void parse(final PreparedStatement stmt, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(stmt, rowParser, Fn.emptyAction()); } public static void parse(final PreparedStatement stmt, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(stmt, 0, Long.MAX_VALUE, rowParser, onComplete); } public static void parse(final PreparedStatement stmt, final long offset, final long count, final Try.Consumer rowParser) throws E { parse(stmt, offset, count, rowParser, Fn.emptyAction()); } public static void parse(final PreparedStatement stmt, final long offset, final long count, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(stmt, offset, count, 0, 0, rowParser, onComplete); } public static void parse(final PreparedStatement stmt, final long offset, final long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(stmt, offset, count, processThreadNum, queueSize, rowParser, Fn.emptyAction()); } /** * Parse the ResultSet obtained by executing query with the specified PreparedStatement. * * @param stmt * @param offset * @param count * @param processThreadNum new threads started to parse/process the lines/records * @param queueSize size of queue to save the processing records/lines loaded from source data. Default size is 1024. * @param rowParser * @param onComplete * @throws UncheckedSQLException */ public static void parse(final PreparedStatement stmt, final long offset, final long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { ResultSet rs = null; try { rs = stmt.executeQuery(); parse(rs, offset, count, processThreadNum, queueSize, rowParser, onComplete); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { closeQuietly(rs); } } public static void parse(final ResultSet rs, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(rs, rowParser, Fn.emptyAction()); } public static void parse(final ResultSet rs, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(rs, 0, Long.MAX_VALUE, rowParser, onComplete); } public static void parse(final ResultSet rs, long offset, long count, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(rs, offset, count, rowParser, Fn.emptyAction()); } public static void parse(final ResultSet rs, long offset, long count, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { parse(rs, offset, count, 0, 0, rowParser, onComplete); } public static void parse(final ResultSet rs, long offset, long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser) throws UncheckedSQLException, E { parse(rs, offset, count, processThreadNum, queueSize, rowParser, Fn.emptyAction()); } /** * Parse the ResultSet. * * @param stmt * @param offset * @param count * @param processThreadNum new threads started to parse/process the lines/records * @param queueSize size of queue to save the processing records/lines loaded from source data. Default size is 1024. * @param rowParser * @param onComplete * @throws UncheckedSQLException */ public static void parse(final ResultSet rs, long offset, long count, final int processThreadNum, final int queueSize, final Try.Consumer rowParser, final Try.Runnable onComplete) throws UncheckedSQLException, E, E2 { final Iterator iter = new ObjIterator() { private final JdbcUtil.BiRowMapper biFunc = BiRowMapper.TO_ARRAY; private List columnLabels = null; private boolean hasNext; @Override public boolean hasNext() { if (hasNext == false) { try { hasNext = rs.next(); } catch (SQLException e) { throw new UncheckedSQLException(e); } } return hasNext; } @Override public Object[] next() { if (hasNext() == false) { throw new NoSuchElementException(); } hasNext = false; try { if (columnLabels == null) { columnLabels = JdbcUtil.getColumnLabelList(rs); } return biFunc.apply(rs, columnLabels); } catch (SQLException e) { throw new UncheckedSQLException(e); } } }; Iterables.parse(iter, offset, count, processThreadNum, queueSize, rowParser, onComplete); } public static long copy(final Connection sourceConn, final String selectSql, final Connection targetConn, final String insertSql) throws UncheckedSQLException { return copy(sourceConn, selectSql, 200, 0, Integer.MAX_VALUE, targetConn, insertSql, DEFAULT_STMT_SETTER, 200, 0, false); } /** * * @param sourceConn * @param selectSql * @param fetchSize * @param offset * @param count * @param targetConn * @param insertSql * @param stmtSetter * @param batchSize * @param batchInterval * @param inParallel do the read and write in separated threads. * @return * @throws UncheckedSQLException */ public static long copy(final Connection sourceConn, final String selectSql, final int fetchSize, final long offset, final long count, final Connection targetConn, final String insertSql, final JdbcUtil.BiParametersSetter stmtSetter, final int batchSize, final int batchInterval, final boolean inParallel) throws UncheckedSQLException { PreparedStatement selectStmt = null; PreparedStatement insertStmt = null; int result = 0; try { insertStmt = targetConn.prepareStatement(insertSql); selectStmt = sourceConn.prepareStatement(selectSql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); selectStmt.setFetchSize(fetchSize); copy(selectStmt, offset, count, insertStmt, stmtSetter, batchSize, batchInterval, inParallel); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { closeQuietly(selectStmt); closeQuietly(insertStmt); } return result; } public static long copy(final PreparedStatement selectStmt, final PreparedStatement insertStmt, final JdbcUtil.BiParametersSetter stmtSetter) throws UncheckedSQLException { return copy(selectStmt, 0, Integer.MAX_VALUE, insertStmt, stmtSetter, 200, 0, false); } /** * * @param selectStmt * @param offset * @param count * @param insertStmt * @param stmtSetter * @param batchSize * @param batchInterval * @param inParallel do the read and write in separated threads. * @return * @throws UncheckedSQLException */ public static long copy(final PreparedStatement selectStmt, final long offset, final long count, final PreparedStatement insertStmt, final JdbcUtil.BiParametersSetter stmtSetter, final int batchSize, final int batchInterval, final boolean inParallel) throws UncheckedSQLException { N.checkArgument(offset >= 0 && count >= 0, "'offset'=%s and 'count'=%s can't be negative", offset, count); N.checkArgument(batchSize > 0 && batchInterval >= 0, "'batchSize'=%s must be greater than 0 and 'batchInterval'=%s can't be negative", batchSize, batchInterval); @SuppressWarnings("rawtypes") final JdbcUtil.BiParametersSetter setter = (JdbcUtil.BiParametersSetter) (stmtSetter == null ? DEFAULT_STMT_SETTER : stmtSetter); final AtomicLong result = new AtomicLong(); final Try.Consumer rowParser = new Try.Consumer() { @Override public void accept(Object[] row) { try { setter.accept(insertStmt, row); insertStmt.addBatch(); result.incrementAndGet(); if ((result.longValue() % batchSize) == 0) { insertStmt.executeBatch(); insertStmt.clearBatch(); if (batchInterval > 0) { N.sleep(batchInterval); } } } catch (SQLException e) { throw new UncheckedSQLException(e); } } }; final Try.Runnable onComplete = new Try.Runnable() { @Override public void run() { if ((result.longValue() % batchSize) > 0) { try { insertStmt.executeBatch(); insertStmt.clearBatch(); } catch (SQLException e) { throw new UncheckedSQLException(e); } } } }; parse(selectStmt, offset, count, 0, inParallel ? DEFAULT_QUEUE_SIZE_FOR_ROW_PARSER : 0, rowParser, onComplete); return result.longValue(); } static boolean isTableNotExistsException(final Throwable e) { if (e instanceof SQLException) { SQLException sqlException = (SQLException) e; if (sqlException.getSQLState() != null && sqlStateForTableNotExists.contains(sqlException.getSQLState())) { return true; } final String msg = N.defaultIfNull(e.getMessage(), "").toLowerCase(); return N.notNullOrEmpty(msg) && (msg.contains("not exist") || msg.contains("doesn't exist") || msg.contains("not found")); } else if (e instanceof UncheckedSQLException) { UncheckedSQLException sqlException = (UncheckedSQLException) e; if (sqlException.getSQLState() != null && sqlStateForTableNotExists.contains(sqlException.getSQLState())) { return true; } final String msg = N.defaultIfNull(e.getMessage(), "").toLowerCase(); return N.notNullOrEmpty(msg) && (msg.contains("not exist") || msg.contains("doesn't exist") || msg.contains("not found")); } return false; } private static final Map, Map> column2FieldNameMapPool = new ConcurrentHashMap<>(); static Map getColumn2FieldNameMap(Class entityClass) { Map result = column2FieldNameMapPool.get(entityClass); if (result == null) { result = N.newBiMap(LinkedHashMap.class, LinkedHashMap.class); final Set allFields = new HashSet<>(); for (Class superClass : ClassUtil.getAllSuperclasses(entityClass)) { allFields.addAll(Array.asList(superClass.getDeclaredFields())); } allFields.addAll(Array.asList(entityClass.getDeclaredFields())); for (Field field : allFields) { if (ClassUtil.getPropGetMethod(entityClass, field.getName()) != null) { String columnName = null; if (field.isAnnotationPresent(Column.class)) { columnName = field.getAnnotation(Column.class).value(); } else { try { if (field.isAnnotationPresent(javax.persistence.Column.class)) { columnName = field.getAnnotation(javax.persistence.Column.class).name(); } } catch (Throwable e) { logger.warn("To support javax.persistence.Table/Column, please add dependence javax.persistence:persistence-api"); } } if (N.notNullOrEmpty(columnName)) { result.put(columnName, field.getName()); result.put(columnName.toLowerCase(), field.getName()); result.put(columnName.toUpperCase(), field.getName()); } } } result = ImmutableMap.of(result); column2FieldNameMapPool.put(entityClass, result); } return result; } /** * The backed {@code PreparedStatement/CallableStatement} will be closed by default * after any execution methods(which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/...). * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* Generally, don't cache or reuse the instance of this class, * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* Remember: parameter/column index in {@code PreparedStatement/ResultSet} starts from 1, not 0. * * @author haiyangl * * @param * @param */ static abstract class AbstractPreparedQuery> implements AutoCloseable { final AsyncExecutor asyncExecutor; final S stmt; Connection conn; boolean isFetchDirectionSet = false; boolean isBatch = false; boolean closeAfterExecution = true; boolean isClosed = false; Runnable closeHandler; AbstractPreparedQuery(S stmt) { this(stmt, null); } AbstractPreparedQuery(S stmt, AsyncExecutor asyncExecutor) { this.stmt = stmt; this.asyncExecutor = asyncExecutor; } // /** // * It's designed to void try-catch. // * This method should be called immediately after {@code JdbcUtil#prepareCallableQuery/SQLExecutor#prepareQuery}. // * // * @return // */ // public Try tried() { // assertNotClosed(); // // return Try.of((Q) this); // } public Q closeAfterExecution(boolean closeAfterExecution) { assertNotClosed(); this.closeAfterExecution = closeAfterExecution; return (Q) this; } public boolean closeAfterExecution() { return closeAfterExecution; } /** * * @param closeHandler A task to execute after this {@code Query} is closed * @return */ public Q onClose(final Runnable closeHandler) { checkArgNotNull(closeHandler, "closeHandler"); assertNotClosed(); if (this.closeHandler == null) { this.closeHandler = closeHandler; } else { final Runnable tmp = this.closeHandler; this.closeHandler = new Runnable() { @Override public void run() { try { tmp.run(); } finally { closeHandler.run(); } } }; } return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @return * @throws SQLException */ public Q setNull(int parameterIndex, int sqlType) throws SQLException { stmt.setNull(parameterIndex, sqlType); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @param typeName * @return * @throws SQLException */ public Q setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { stmt.setNull(parameterIndex, sqlType, typeName); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setBoolean(int parameterIndex, boolean x) throws SQLException { stmt.setBoolean(parameterIndex, x); return (Q) this; } public Q setBoolean(int parameterIndex, Boolean x) throws SQLException { stmt.setBoolean(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setByte(int parameterIndex, byte x) throws SQLException { stmt.setByte(parameterIndex, x); return (Q) this; } public Q setByte(int parameterIndex, Byte x) throws SQLException { stmt.setByte(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setShort(int parameterIndex, short x) throws SQLException { stmt.setShort(parameterIndex, x); return (Q) this; } public Q setShort(int parameterIndex, Short x) throws SQLException { stmt.setShort(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setInt(int parameterIndex, int x) throws SQLException { stmt.setInt(parameterIndex, x); return (Q) this; } public Q setInt(int parameterIndex, Integer x) throws SQLException { stmt.setInt(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setLong(int parameterIndex, long x) throws SQLException { stmt.setLong(parameterIndex, x); return (Q) this; } public Q setLong(int parameterIndex, Long x) throws SQLException { stmt.setLong(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setFloat(int parameterIndex, float x) throws SQLException { stmt.setFloat(parameterIndex, x); return (Q) this; } public Q setFloat(int parameterIndex, Float x) throws SQLException { stmt.setFloat(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setDouble(int parameterIndex, double x) throws SQLException { stmt.setDouble(parameterIndex, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setDouble(int parameterIndex, Double x) throws SQLException { stmt.setDouble(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { stmt.setBigDecimal(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setString(int parameterIndex, String x) throws SQLException { stmt.setString(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setDate(int parameterIndex, java.sql.Date x) throws SQLException { stmt.setDate(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setDate(int parameterIndex, java.util.Date x) throws SQLException { stmt.setDate(parameterIndex, x == null ? null : x instanceof java.sql.Date ? (java.sql.Date) x : new java.sql.Date(x.getTime())); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setTime(int parameterIndex, java.sql.Time x) throws SQLException { stmt.setTime(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setTime(int parameterIndex, java.util.Date x) throws SQLException { stmt.setTime(parameterIndex, x == null ? null : x instanceof java.sql.Time ? (java.sql.Time) x : new java.sql.Time(x.getTime())); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setTimestamp(int parameterIndex, java.sql.Timestamp x) throws SQLException { stmt.setTimestamp(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setTimestamp(int parameterIndex, java.util.Date x) throws SQLException { stmt.setTimestamp(parameterIndex, x == null ? null : x instanceof java.sql.Timestamp ? (java.sql.Timestamp) x : new java.sql.Timestamp(x.getTime())); return (Q) this; } public Q setBytes(int parameterIndex, byte[] x) throws SQLException { stmt.setBytes(parameterIndex, x); return (Q) this; } public Q setAsciiStream(int parameterIndex, InputStream inputStream) throws SQLException { stmt.setAsciiStream(parameterIndex, inputStream); return (Q) this; } public Q setAsciiStream(int parameterIndex, InputStream inputStream, long length) throws SQLException { stmt.setAsciiStream(parameterIndex, inputStream, length); return (Q) this; } public Q setBinaryStream(int parameterIndex, InputStream inputStream) throws SQLException { stmt.setBinaryStream(parameterIndex, inputStream); return (Q) this; } public Q setBinaryStream(int parameterIndex, InputStream inputStream, long length) throws SQLException { stmt.setBinaryStream(parameterIndex, inputStream, length); return (Q) this; } public Q setCharacterStream(int parameterIndex, Reader reader) throws SQLException { stmt.setCharacterStream(parameterIndex, reader); return (Q) this; } public Q setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { stmt.setCharacterStream(parameterIndex, reader, length); return (Q) this; } public Q setNCharacterStream(int parameterIndex, Reader reader) throws SQLException { stmt.setNCharacterStream(parameterIndex, reader); return (Q) this; } public Q setNCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { stmt.setNCharacterStream(parameterIndex, reader, length); return (Q) this; } public Q setBlob(int parameterIndex, java.sql.Blob x) throws SQLException { stmt.setBlob(parameterIndex, x); return (Q) this; } public Q setBlob(int parameterIndex, InputStream inputStream) throws SQLException { stmt.setBlob(parameterIndex, inputStream); return (Q) this; } public Q setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { stmt.setBlob(parameterIndex, inputStream, length); return (Q) this; } public Q setClob(int parameterIndex, java.sql.Clob x) throws SQLException { stmt.setClob(parameterIndex, x); return (Q) this; } public Q setClob(int parameterIndex, Reader reader) throws SQLException { stmt.setClob(parameterIndex, reader); return (Q) this; } public Q setClob(int parameterIndex, Reader reader, long length) throws SQLException { stmt.setClob(parameterIndex, reader, length); return (Q) this; } public Q setNClob(int parameterIndex, java.sql.NClob x) throws SQLException { stmt.setNClob(parameterIndex, x); return (Q) this; } public Q setNClob(int parameterIndex, Reader reader) throws SQLException { stmt.setNClob(parameterIndex, reader); return (Q) this; } public Q setNClob(int parameterIndex, Reader reader, long length) throws SQLException { stmt.setNClob(parameterIndex, reader, length); return (Q) this; } /** * @param parameterIndex starts from 1, not 0. * * @param parameterIndex * @param x * @return * @throws SQLException */ public Q setURL(int parameterIndex, URL x) throws SQLException { stmt.setURL(parameterIndex, x); return (Q) this; } /** * @param parameterIndex starts from 1, not 0. * * @param parameterIndex * @param x * @return * @throws SQLException */ public Q setArray(int parameterIndex, java.sql.Array x) throws SQLException { stmt.setArray(parameterIndex, x); return (Q) this; } /** * @param parameterIndex starts from 1, not 0. * * @param parameterIndex * @param x * @return * @throws SQLException */ public Q setSQLXML(int parameterIndex, java.sql.SQLXML x) throws SQLException { stmt.setSQLXML(parameterIndex, x); return (Q) this; } /** * @param parameterIndex starts from 1, not 0. * * @param parameterIndex * @param x * @return * @throws SQLException */ public Q setRef(int parameterIndex, java.sql.Ref x) throws SQLException { stmt.setRef(parameterIndex, x); return (Q) this; } /** * @param parameterIndex starts from 1, not 0. * * @param parameterIndex * @param x * @return * @throws SQLException */ public Q setRowId(int parameterIndex, java.sql.RowId x) throws SQLException { stmt.setRowId(parameterIndex, x); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @return * @throws SQLException */ public Q setObject(int parameterIndex, Object x) throws SQLException { if (x == null) { stmt.setObject(parameterIndex, x); } else { N.typeOf(x.getClass()).set(stmt, parameterIndex, x); } return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @param sqlType * @return * @throws SQLException */ public Q setObject(int parameterIndex, Object x, int sqlType) throws SQLException { stmt.setObject(parameterIndex, x, sqlType); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param x * @param sqlType * @param scaleOrLength * @return * @throws SQLException */ public Q setObject(int parameterIndex, Object x, int sqlType, int scaleOrLength) throws SQLException { stmt.setObject(parameterIndex, x, sqlType, scaleOrLength); return (Q) this; } /** * * @param startParameterIndex * @param x * @param sqlType * @return * @throws SQLException */ public Q setObject(int parameterIndex, Object x, SQLType sqlType) throws SQLException { stmt.setObject(parameterIndex, x, sqlType); return (Q) this; } public Q setObject(int parameterIndex, Object x, SQLType sqlType, int scaleOrLength) throws SQLException { stmt.setObject(parameterIndex, x, sqlType, scaleOrLength); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, String param1, String param2) throws SQLException { stmt.setString(startParameterIndex++, param1); stmt.setString(startParameterIndex++, param2); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, String param1, String param2, String param3) throws SQLException { stmt.setString(startParameterIndex++, param1); stmt.setString(startParameterIndex++, param2); stmt.setString(startParameterIndex++, param3); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, String param1, String param2, String param3, String param4) throws SQLException { stmt.setString(startParameterIndex++, param1); stmt.setString(startParameterIndex++, param2); stmt.setString(startParameterIndex++, param3); stmt.setString(startParameterIndex++, param4); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, String param1, String param2, String param3, String param4, String param5) throws SQLException { stmt.setString(startParameterIndex++, param1); stmt.setString(startParameterIndex++, param2); stmt.setString(startParameterIndex++, param3); stmt.setString(startParameterIndex++, param4); stmt.setString(startParameterIndex++, param5); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4, Object param5) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); setObject(startParameterIndex++, param5); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @param param6 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4, Object param5, Object param6) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); setObject(startParameterIndex++, param5); setObject(startParameterIndex++, param6); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @param param6 * @param param7 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4, Object param5, Object param6, Object param7) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); setObject(startParameterIndex++, param5); setObject(startParameterIndex++, param6); setObject(startParameterIndex++, param7); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @param param6 * @param param7 * @param param8 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4, Object param5, Object param6, Object param7, Object param8) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); setObject(startParameterIndex++, param5); setObject(startParameterIndex++, param6); setObject(startParameterIndex++, param7); setObject(startParameterIndex++, param8); return (Q) this; } /** * * @param startParameterIndex * @param param1 * @param param2 * @param param3 * @param param4 * @param param5 * @param param6 * @param param7 * @param param8 * @param param9 * @return * @throws SQLException */ public Q setParameters(int startParameterIndex, Object param1, Object param2, Object param3, Object param4, Object param5, Object param6, Object param7, Object param8, Object param9) throws SQLException { setObject(startParameterIndex++, param1); setObject(startParameterIndex++, param2); setObject(startParameterIndex++, param3); setObject(startParameterIndex++, param4); setObject(startParameterIndex++, param5); setObject(startParameterIndex++, param6); setObject(startParameterIndex++, param7); setObject(startParameterIndex++, param8); setObject(startParameterIndex++, param9); return (Q) this; } /** * * @param startParameterIndex * @param parameters * @return * @throws IllegalArgumentException if specified {@code parameters} or {@code type} is null. * @throws SQLException */ public Q setParameters(int startParameterIndex, Collection parameters) throws IllegalArgumentException, SQLException { checkArgNotNull(parameters, "parameters"); for (Object param : parameters) { setObject(startParameterIndex++, param); } return (Q) this; } /** * * @param startParameterIndex * @param parameters * @param type * @return * @throws IllegalArgumentException if specified {@code parameters} or {@code type} is null. * @throws SQLException */ public Q setParameters(int startParameterIndex, Collection parameters, Class type) throws IllegalArgumentException, SQLException { checkArgNotNull(parameters, "parameters"); checkArgNotNull(type, "type"); final Type setter = N.typeOf(type); for (T param : parameters) { setter.set(stmt, startParameterIndex++, param); } return (Q) this; } /** * * @param paramSetter * @return * @throws SQLException */ public Q setParameters(ParametersSetter paramSetter) throws SQLException { checkArgNotNull(paramSetter, "paramSetter"); boolean noException = false; try { paramSetter.accept(stmt); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } /** * * @param paramSetter * @return * @throws SQLException */ public Q setParameters(final T parameter, final BiParametersSetter paramSetter) throws SQLException { checkArgNotNull(paramSetter, "paramSetter"); boolean noException = false; try { paramSetter.accept(stmt, parameter); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } /** * * @param paramSetter * @return * @throws SQLException */ public Q settParameters(ParametersSetter paramSetter) throws SQLException { checkArgNotNull(paramSetter, "paramSetter"); boolean noException = false; try { paramSetter.accept((Q) this); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } /** * * @param paramSetter * @return * @throws SQLException */ public Q settParameters(final T parameter, BiParametersSetter paramSetter) throws SQLException { checkArgNotNull(paramSetter, "paramSetter"); boolean noException = false; try { paramSetter.accept((Q) this, parameter); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } public Q addBatch() throws SQLException { stmt.addBatch(); isBatch = true; return (Q) this; } /** * * @param direction one of ResultSet.FETCH_FORWARD, * ResultSet.FETCH_REVERSE, or ResultSet.FETCH_UNKNOWN * @return * @throws SQLException * @see {@link java.sql.Statement#setFetchDirection(int)} */ public Q setFetchDirection(FetchDirection direction) throws SQLException { isFetchDirectionSet = true; stmt.setFetchDirection(direction.intValue); return (Q) this; } public Q setFetchSize(int rows) throws SQLException { stmt.setFetchSize(rows); return (Q) this; } public Q setMaxRows(int max) throws SQLException { stmt.setMaxRows(max); return (Q) this; } public Q setLargeMaxRows(long max) throws SQLException { stmt.setLargeMaxRows(max); return (Q) this; } public Q setMaxFieldSize(int max) throws SQLException { stmt.setMaxFieldSize(max); return (Q) this; } public Q setQueryTimeout(int seconds) throws SQLException { stmt.setQueryTimeout(seconds); return (Q) this; } public OptionalBoolean queryForBoolean() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalBoolean.of(rs.getBoolean(1)) : OptionalBoolean.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalChar queryForChar() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { final String str = rs.getString(1); return OptionalChar.of(str == null || str.length() == 0 ? N.CHAR_0 : str.charAt(0)); } else { return OptionalChar.empty(); } } finally { closeAfterExecutionIfAllowed(); } } public OptionalByte queryForByte() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalByte.of(rs.getByte(1)) : OptionalByte.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalShort queryForShort() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalShort.of(rs.getShort(1)) : OptionalShort.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalInt queryForInt() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalInt.of(rs.getInt(1)) : OptionalInt.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalLong queryForLong() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalLong.of(rs.getLong(1)) : OptionalLong.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalFloat queryForFloat() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalFloat.of(rs.getFloat(1)) : OptionalFloat.empty(); } finally { closeAfterExecutionIfAllowed(); } } public OptionalDouble queryForDouble() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? OptionalDouble.of(rs.getDouble(1)) : OptionalDouble.empty(); } finally { closeAfterExecutionIfAllowed(); } } public Nullable queryForString() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(rs.getString(1)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Nullable queryBigDecimal() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(rs.getBigDecimal(1)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Nullable queryForDate() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(rs.getDate(1)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Nullable queryForTime() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(rs.getTime(1)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Nullable queryForTimestamp() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(rs.getTimestamp(1)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } /** * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}. * * @param targetClass * @return * @throws SQLException */ public Nullable queryForSingleResult(Class targetClass) throws SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Nullable.of(N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass)) : Nullable. empty(); } finally { closeAfterExecutionIfAllowed(); } } /** * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}. * * @param targetClass * @return * @throws SQLException */ public Optional queryForSingleNonNull(Class targetClass) throws SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Optional.of(N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass)) : Optional. empty(); } finally { closeAfterExecutionIfAllowed(); } } /** * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}. * And throws {@code DuplicatedResultException} if more than one record found. * * @param targetClass * @return * @throws DuplicatedResultException if more than one record found. * @throws SQLException */ public Nullable queryForUniqueResult(Class targetClass) throws DuplicatedResultException, SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final Nullable result = rs.next() ? Nullable.of(N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass)) : Nullable. empty(); if (result.isPresent() && rs.next()) { throw new DuplicatedResultException( "At least two results found: " + Strings.concat(result.get(), ", ", N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass))); } return result; } finally { closeAfterExecutionIfAllowed(); } } /** * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}. * And throws {@code DuplicatedResultException} if more than one record found. * * @param targetClass * @return * @throws DuplicatedResultException if more than one record found. * @throws SQLException */ public Optional queryForUniqueNonNull(Class targetClass) throws DuplicatedResultException, SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final Optional result = rs.next() ? Optional.of(N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass)) : Optional. empty(); if (result.isPresent() && rs.next()) { throw new DuplicatedResultException( "At least two results found: " + Strings.concat(result.get(), ", ", N.convert(JdbcUtil.getColumnValue(rs, 1), targetClass))); } return result; } finally { closeAfterExecutionIfAllowed(); } } private T get(Class targetClass, ResultSet rs) throws SQLException { final List columnLabels = JdbcUtil.getColumnLabelList(rs); return BiRowMapper.to(targetClass).apply(rs, columnLabels); } public DataSet query() throws SQLException { return query(ResultExtractor.TO_DATA_SET); } public R query(final ResultExtractor resultExtrator) throws SQLException { checkArgNotNull(resultExtrator, "resultExtrator"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return resultExtrator.apply(rs); } finally { closeAfterExecutionIfAllowed(); } } public R query(final BiResultExtractor resultExtrator) throws SQLException { checkArgNotNull(resultExtrator, "resultExtrator"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return resultExtrator.apply(rs, getColumnLabelList(rs)); } finally { closeAfterExecutionIfAllowed(); } } /** * * @param targetClass * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public Optional get(final Class targetClass) throws DuplicatedResultException, SQLException { return Optional.ofNullable(gett(targetClass)); } /** * * @param rowMapper * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public Optional get(RowMapper rowMapper) throws DuplicatedResultException, SQLException { return Optional.ofNullable(gett(rowMapper)); } /** * * @param rowMapper * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public Optional get(BiRowMapper rowMapper) throws DuplicatedResultException, SQLException { return Optional.ofNullable(gett(rowMapper)); } /** * * @param targetClass * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public T gett(final Class targetClass) throws DuplicatedResultException, SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { final T result = Objects.requireNonNull(get(targetClass, rs)); if (rs.next()) { throw new DuplicatedResultException("There are more than one record found by the query"); } return result; } else { return null; } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param rowMapper * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public T gett(RowMapper rowMapper) throws DuplicatedResultException, SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { final T result = Objects.requireNonNull(rowMapper.apply(rs)); if (rs.next()) { throw new DuplicatedResultException("There are more than one record found by the query"); } return result; } else { return null; } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param rowMapper * @return * @throws DuplicatedResultException If there are more than one record found by the query * @throws SQLException */ public T gett(BiRowMapper rowMapper) throws DuplicatedResultException, SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { final T result = Objects.requireNonNull(rowMapper.apply(rs, JdbcUtil.getColumnLabelList(rs))); if (rs.next()) { throw new DuplicatedResultException("There are more than one record found by the query"); } return result; } else { return null; } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param targetClass * @return * @throws SQLException */ public Optional findFirst(final Class targetClass) throws SQLException { checkArgNotNull(targetClass, "targetClass"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { return Optional.of(get(targetClass, rs)); } else { return Optional.empty(); } } finally { closeAfterExecutionIfAllowed(); } } public Optional findFirst(RowMapper rowMapper) throws SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Optional.of(rowMapper.apply(rs)) : Optional. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Optional findFirst(final RowFilter recordFilter, RowMapper rowMapper) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { while (rs.next()) { if (recordFilter.test(rs)) { return Optional.of(rowMapper.apply(rs)); } } return Optional.empty(); } finally { closeAfterExecutionIfAllowed(); } } public Optional findFirst(BiRowMapper rowMapper) throws SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next() ? Optional.of(rowMapper.apply(rs, JdbcUtil.getColumnLabelList(rs))) : Optional. empty(); } finally { closeAfterExecutionIfAllowed(); } } public Optional findFirst(final BiRowFilter recordFilter, BiRowMapper rowMapper) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { if (recordFilter.test(rs, columnLabels)) { return Optional.of(rowMapper.apply(rs, columnLabels)); } } return Optional.empty(); } finally { closeAfterExecutionIfAllowed(); } } public List list(final Class targetClass) throws SQLException { return list(BiRowMapper.to(targetClass)); } public List list(final Class targetClass, int maxResult) throws SQLException { return list(BiRowMapper.to(targetClass), maxResult); } public List list(RowMapper rowMapper) throws SQLException { return list(rowMapper, Integer.MAX_VALUE); } public List list(RowMapper rowMapper, int maxResult) throws SQLException { return list(RowFilter.ALWAYS_TRUE, rowMapper, maxResult); } public List list(final RowFilter recordFilter, RowMapper rowMapper) throws SQLException { return list(recordFilter, rowMapper, Integer.MAX_VALUE); } public List list(final RowFilter recordFilter, RowMapper rowMapper, int maxResult) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowMapper, "rowMapper"); checkArg(maxResult >= 0, "'maxResult' can' be negative: " + maxResult); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List result = new ArrayList<>(); while (maxResult > 0 && rs.next()) { if (recordFilter.test(rs)) { result.add(rowMapper.apply(rs)); maxResult--; } } return result; } finally { closeAfterExecutionIfAllowed(); } } public List list(BiRowMapper rowMapper) throws SQLException { return list(rowMapper, Integer.MAX_VALUE); } public List list(BiRowMapper rowMapper, int maxResult) throws SQLException { return list(BiRowFilter.ALWAYS_TRUE, rowMapper, maxResult); } public List list(final BiRowFilter recordFilter, BiRowMapper rowMapper) throws SQLException { return list(recordFilter, rowMapper, Integer.MAX_VALUE); } public List list(final BiRowFilter recordFilter, BiRowMapper rowMapper, int maxResult) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowMapper, "rowMapper"); checkArg(maxResult >= 0, "'maxResult' can' be negative: " + maxResult); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); final List result = new ArrayList<>(); while (maxResult > 0 && rs.next()) { if (recordFilter.test(rs, columnLabels)) { result.add(rowMapper.apply(rs, columnLabels)); maxResult--; } } return result; } finally { closeAfterExecutionIfAllowed(); } } public ExceptionalStream stream(final Class targetClass) throws SQLException { return stream(BiRowMapper.to(targetClass)); } public ExceptionalStream stream(final RowMapper rowMapper) throws SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); final ExceptionalIterator lazyIter = ExceptionalIterator .of(new Try.Supplier, SQLException>() { private ExceptionalIterator internalIter; @Override public ExceptionalIterator get() throws SQLException { if (internalIter == null) { ResultSet rs = null; try { rs = executeQuery(); final ResultSet resultSet = rs; internalIter = new ExceptionalIterator() { private boolean hasNext; @Override public boolean hasNext() throws SQLException { if (hasNext == false) { hasNext = resultSet.next(); } return hasNext; } @Override public T next() throws SQLException { if (hasNext() == false) { throw new NoSuchElementException(); } hasNext = false; return rowMapper.apply(resultSet); } @Override public void skip(long n) throws SQLException { N.checkArgNotNegative(n, "n"); final long m = hasNext ? n - 1 : n; hasNext = false; JdbcUtil.skip(resultSet, m); } @Override public long count() throws SQLException { long cnt = hasNext ? 1 : 0; hasNext = false; while (resultSet.next()) { cnt++; } return cnt; } @Override public void close() throws SQLException { try { JdbcUtil.closeQuietly(resultSet); } finally { closeAfterExecutionIfAllowed(); } } }; } finally { if (internalIter == null) { try { JdbcUtil.closeQuietly(rs); } finally { closeAfterExecutionIfAllowed(); } } } } return internalIter; } }); return ExceptionalStream.newStream(lazyIter).onClose(new Try.Runnable() { @Override public void run() throws SQLException { lazyIter.close(); } }); } public ExceptionalStream stream(final BiRowMapper rowMapper) throws SQLException { checkArgNotNull(rowMapper, "rowMapper"); assertNotClosed(); final ExceptionalIterator lazyIter = ExceptionalIterator .of(new Try.Supplier, SQLException>() { private ExceptionalIterator internalIter; @Override public ExceptionalIterator get() throws SQLException { if (internalIter == null) { ResultSet rs = null; try { rs = executeQuery(); final ResultSet resultSet = rs; internalIter = new ExceptionalIterator() { private List columnLabels = null; private boolean hasNext; @Override public boolean hasNext() throws SQLException { if (hasNext == false) { hasNext = resultSet.next(); } return hasNext; } @Override public T next() throws SQLException { if (hasNext() == false) { throw new NoSuchElementException(); } hasNext = false; if (columnLabels == null) { columnLabels = JdbcUtil.getColumnLabelList(resultSet); } return rowMapper.apply(resultSet, columnLabels); } @Override public void skip(long n) throws SQLException { N.checkArgNotNegative(n, "n"); final long m = hasNext ? n - 1 : n; hasNext = false; JdbcUtil.skip(resultSet, m); } @Override public long count() throws SQLException { long cnt = hasNext ? 1 : 0; hasNext = false; while (resultSet.next()) { cnt++; } return cnt; } @Override public void close() throws SQLException { try { JdbcUtil.closeQuietly(resultSet); } finally { closeAfterExecutionIfAllowed(); } } }; } finally { if (internalIter == null) { try { JdbcUtil.closeQuietly(rs); } finally { closeAfterExecutionIfAllowed(); } } } } return internalIter; } }); return ExceptionalStream.newStream(lazyIter).onClose(new Try.Runnable() { @Override public void run() throws SQLException { lazyIter.close(); } }); } /** * Note: using {@code select 1 from ...}, not {@code select count(*) from ...}. * * @return * @throws SQLException */ public boolean exists() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { return rs.next(); } finally { closeAfterExecutionIfAllowed(); } } public void ifExists(final RowConsumer rowConsumer) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { rowConsumer.accept(rs); } } finally { closeAfterExecutionIfAllowed(); } } public void ifExists(final BiRowConsumer rowConsumer) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { rowConsumer.accept(rs, JdbcUtil.getColumnLabelList(rs)); } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param rowConsumer * @param orElseAction * @throws SQLException */ public void ifExistsOrElse(final RowConsumer rowConsumer, Try.Runnable orElseAction) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); checkArgNotNull(orElseAction, "orElseAction"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { rowConsumer.accept(rs); } else { orElseAction.run(); } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param rowConsumer * @param orElseAction * @throws SQLException */ public void ifExistsOrElse(final BiRowConsumer rowConsumer, Try.Runnable orElseAction) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); checkArgNotNull(orElseAction, "orElseAction"); assertNotClosed(); try (ResultSet rs = executeQuery()) { if (rs.next()) { rowConsumer.accept(rs, JdbcUtil.getColumnLabelList(rs)); } else { orElseAction.run(); } } finally { closeAfterExecutionIfAllowed(); } } /** * Note: using {@code select count(*) from ...} * * @return * @throws SQLException * @deprecated may be misused and it's inefficient. */ @Deprecated public int count() throws SQLException { assertNotClosed(); try (ResultSet rs = executeQuery()) { int cnt = 0; while (rs.next()) { cnt++; } return cnt; } finally { closeAfterExecutionIfAllowed(); } } public int count(final RowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { int cnt = 0; while (rs.next()) { if (recordFilter.test(rs)) { cnt++; } } return cnt; } finally { closeAfterExecutionIfAllowed(); } } public int count(final BiRowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); int cnt = 0; while (rs.next()) { if (recordFilter.test(rs, columnLabels)) { cnt++; } } return cnt; } finally { closeAfterExecutionIfAllowed(); } } public boolean anyMatch(final RowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { while (rs.next()) { if (recordFilter.test(rs)) { return true; } } return false; } finally { closeAfterExecutionIfAllowed(); } } public boolean anyMatch(final BiRowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { if (recordFilter.test(rs, columnLabels)) { return true; } } return false; } finally { closeAfterExecutionIfAllowed(); } } public boolean allMatch(final RowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { while (rs.next()) { if (recordFilter.test(rs) == false) { return false; } } return true; } finally { closeAfterExecutionIfAllowed(); } } public boolean allMatch(final BiRowFilter recordFilter) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { if (recordFilter.test(rs, columnLabels) == false) { return false; } } return true; } finally { closeAfterExecutionIfAllowed(); } } public boolean noneMatch(final RowFilter recordFilter) throws SQLException { return anyMatch(recordFilter) == false; } public boolean noneMatch(final BiRowFilter recordFilter) throws SQLException { return anyMatch(recordFilter) == false; } public void forEach(final RowConsumer rowConsumer) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { while (rs.next()) { rowConsumer.accept(rs); } } finally { closeAfterExecutionIfAllowed(); } } public void forEach(final RowFilter recordFilter, final RowConsumer rowConsumer) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { while (rs.next()) { if (recordFilter.test(rs)) { rowConsumer.accept(rs); } } } finally { closeAfterExecutionIfAllowed(); } } public void forEach(final BiRowConsumer rowConsumer) throws SQLException { checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { rowConsumer.accept(rs, columnLabels); } } finally { closeAfterExecutionIfAllowed(); } } public void forEach(final BiRowFilter recordFilter, final BiRowConsumer rowConsumer) throws SQLException { checkArgNotNull(recordFilter, "recordFilter"); checkArgNotNull(rowConsumer, "rowConsumer"); assertNotClosed(); try (ResultSet rs = executeQuery()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { if (recordFilter.test(rs, columnLabels)) { rowConsumer.accept(rs, columnLabels); } } } finally { closeAfterExecutionIfAllowed(); } } private ResultSet executeQuery() throws SQLException { if (!isFetchDirectionSet) { stmt.setFetchDirection(ResultSet.FETCH_FORWARD); } return stmt.executeQuery(); } /** * Returns the generated key if it exists. * * @return */ public Optional insert() throws SQLException { assertNotClosed(); try { stmt.executeUpdate(); try (ResultSet rs = stmt.getGeneratedKeys()) { return rs.next() ? Optional.of((ID) JdbcUtil.getColumnValue(rs, 1)) : Optional. empty(); } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param keyExtractor * @return * @throws SQLException */ public Optional insert(final RowMapper autoGeneratedKeyExtractor) throws SQLException { assertNotClosed(); try { stmt.executeUpdate(); try (ResultSet rs = stmt.getGeneratedKeys()) { return rs.next() ? Optional.of(autoGeneratedKeyExtractor.apply(rs)) : Optional. empty(); } } finally { closeAfterExecutionIfAllowed(); } } public Optional insert(final BiRowMapper autoGeneratedKeyExtractor) throws SQLException { assertNotClosed(); try { stmt.executeUpdate(); try (ResultSet rs = stmt.getGeneratedKeys()) { if (rs.next()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); return Optional.of(autoGeneratedKeyExtractor.apply(rs, columnLabels)); } else { return Optional. empty(); } } } finally { closeAfterExecutionIfAllowed(); } } /** * Returns the generated key if it exists. * * @return */ public List batchInsert() throws SQLException { assertNotClosed(); try { return executeBatchInsert(); } finally { closeAfterExecutionIfAllowed(); } } /** * * @param autoGeneratedKeyExtractor * @return * @throws SQLException */ public List batchInsert(final RowMapper autoGeneratedKeyExtractor) throws SQLException { assertNotClosed(); try { stmt.executeBatch(); final List result = new ArrayList<>(); try (ResultSet rs = stmt.getGeneratedKeys()) { while (rs.next()) { result.add(autoGeneratedKeyExtractor.apply(rs)); } return result; } finally { stmt.clearBatch(); } } finally { closeAfterExecutionIfAllowed(); } } /** * * @param autoGeneratedKeyExtractor * @return * @throws SQLException */ public List batchInsert(final BiRowMapper autoGeneratedKeyExtractor) throws SQLException { assertNotClosed(); try { stmt.executeBatch(); final List result = new ArrayList<>(); try (ResultSet rs = stmt.getGeneratedKeys()) { final List columnLabels = JdbcUtil.getColumnLabelList(rs); while (rs.next()) { result.add(autoGeneratedKeyExtractor.apply(rs, columnLabels)); } return result; } finally { stmt.clearBatch(); } } finally { closeAfterExecutionIfAllowed(); } } /** * Generally, this method should be executed in transaction. * * @param batchSize * @param batchParameters * @param paramSetter * @return * @throws SQLException */ public List batchInsert(final int batchSize, final Collection batchParameters, BiParametersSetter paramSetter) throws SQLException { return batchInsert(batchSize, batchParameters.iterator(), paramSetter); } /** * Generally, this method should be executed in transaction. * * @param batchSize * @param batchParameters * @param paramSetter * @return * @throws SQLException */ public List batchInsert(final int batchSize, final Iterator batchParameters, BiParametersSetter paramSetter) throws SQLException { checkArg(batchSize > 0, "'batchSize' must be bigger than 0"); checkArgNotNull(paramSetter, "paramSetter"); final Iterator iter = batchParameters == null ? N. emptyIterator() : batchParameters; final List result = new ArrayList<>(); try { long cnt = 0; while (iter.hasNext()) { paramSetter.accept((Q) this, iter.next()); addBatch(); if (++cnt % batchSize == 0) { result.addAll(this. executeBatchInsert()); } } if (cnt % batchSize != 0) { result.addAll(this. executeBatchInsert()); } } finally { closeAfterExecutionIfAllowed(); } return result; } private List executeBatchInsert() throws SQLException { stmt.executeBatch(); final List result = new ArrayList<>(); try (ResultSet rs = stmt.getGeneratedKeys()) { while (rs.next()) { result.add((ID) JdbcUtil.getColumnValue(rs, 1)); } return result; } finally { stmt.clearBatch(); } } public int update() throws SQLException { assertNotClosed(); try { return stmt.executeUpdate(); } finally { closeAfterExecutionIfAllowed(); } } public int[] batchUpdate() throws SQLException { assertNotClosed(); try { return executeBatchUpdate(); } finally { closeAfterExecutionIfAllowed(); } } public long largeUpate() throws SQLException { assertNotClosed(); try { return stmt.executeLargeUpdate(); } finally { closeAfterExecutionIfAllowed(); } } public long[] largeBatchUpdate() throws SQLException { assertNotClosed(); try { final long[] result = stmt.executeLargeBatch(); stmt.clearBatch(); return result; } finally { closeAfterExecutionIfAllowed(); } } /** * Generally, this method should be executed in transaction. * * @param batchSize * @param batchParameters * @param paramSetter * @return * @throws SQLException */ public int batchUpdate(final int batchSize, final Collection batchParameters, BiParametersSetter paramSetter) throws SQLException { return batchUpdate(batchSize, batchParameters == null ? N. emptyIterator() : batchParameters.iterator(), paramSetter); } /** * Generally, this method should be executed in transaction. * * @param batchSize * @param batchParameters * @param paramSetter * @return * @throws SQLException */ public int batchUpdate(final int batchSize, final Iterator batchParameters, final BiParametersSetter paramSetter) throws SQLException { checkArg(batchSize > 0, "'batchSize' must be bigger than 0"); checkArgNotNull(paramSetter, "paramSetter"); final Iterator iter = batchParameters == null ? N. emptyIterator() : batchParameters; long result = 0; try { long cnt = 0; while (iter.hasNext()) { paramSetter.accept((Q) this, iter.next()); addBatch(); if (++cnt % batchSize == 0) { result += N.sum(executeBatchUpdate()); } } if (cnt % batchSize != 0) { result += N.sum(executeBatchUpdate()); } } finally { closeAfterExecutionIfAllowed(); } return result >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) result; } private int[] executeBatchUpdate() throws SQLException { try { return stmt.executeBatch(); } finally { stmt.clearBatch(); } } public boolean execute() throws SQLException { assertNotClosed(); try { return stmt.execute(); } finally { closeAfterExecutionIfAllowed(); } } public R executeThenApply(final Try.Function getter) throws SQLException { checkArgNotNull(getter, "getter"); assertNotClosed(); try { stmt.execute(); return getter.apply(stmt); } finally { closeAfterExecutionIfAllowed(); } } public R executeThenApply(final Try.BiFunction getter) throws SQLException { checkArgNotNull(getter, "getter"); assertNotClosed(); try { final boolean isFirstResultSet = stmt.execute(); return getter.apply(isFirstResultSet, stmt); } finally { closeAfterExecutionIfAllowed(); } } public void executeThenAccept(final Try.Consumer consumer) throws SQLException { checkArgNotNull(consumer, "consumer"); assertNotClosed(); try { stmt.execute(); consumer.accept(stmt); } finally { closeAfterExecutionIfAllowed(); } } public void executeThenAccept(final Try.BiConsumer consumer) throws SQLException { checkArgNotNull(consumer, "consumer"); assertNotClosed(); try { final boolean isFirstResultSet = stmt.execute(); consumer.accept(isFirstResultSet, stmt); } finally { closeAfterExecutionIfAllowed(); } } /** * * @param func * @return * @deprecated {@code asyncExecutor.call(() -> JdbcUtil.prepareQuery(query)...query/update/execute(...)} is recommended. */ @Deprecated public ContinuableFuture asyncApply(final Try.Function func) { checkArgNotNull(func, "func"); assertNotClosed(); final Q q = (Q) this; if (asyncExecutor == null) { return ContinuableFuture.call(new Try.Callable() { @Override public R call() throws SQLException { return func.apply(q); } }); } else { return asyncExecutor.execute(new Try.Callable() { @Override public R call() throws SQLException { return func.apply(q); } }); } } /** * * @param func * @param executor * @return * @deprecated {@code asyncExecutor.call(() -> JdbcUtil.prepareQuery(query)...query/update/execute(...)} is recommended. */ @Deprecated public ContinuableFuture asyncApply(final Try.Function func, final Executor executor) { checkArgNotNull(func, "func"); checkArgNotNull(executor, "executor"); assertNotClosed(); final Q q = (Q) this; return ContinuableFuture.call(new Try.Callable() { @Override public R call() throws SQLException { return func.apply(q); } }, executor); } /** * * @param action * @return * @deprecated {@code asyncExecutor.call(() -> JdbcUtil.prepareQuery(query)...query/update/execute(...)} is recommended. */ @Deprecated public ContinuableFuture asyncAccept(final Try.Consumer action) { checkArgNotNull(action, "action"); assertNotClosed(); final Q q = (Q) this; if (asyncExecutor == null) { return ContinuableFuture.run(new Try.Runnable() { @Override public void run() throws SQLException { action.accept(q); } }); } else { return asyncExecutor.execute(new Try.Callable() { @Override public Void call() throws SQLException { action.accept(q); return null; } }); } } /** * * @param action * @param executor * @return * @deprecated {@code asyncExecutor.call(() -> JdbcUtil.prepareQuery(query)...query/update/execute(...)} is recommended. */ @Deprecated public ContinuableFuture asyncAccept(final Try.Consumer action, final Executor executor) { checkArgNotNull(action, "action"); checkArgNotNull(executor, "executor"); assertNotClosed(); final Q q = (Q) this; return ContinuableFuture.run(new Try.Runnable() { @Override public void run() throws SQLException { action.accept(q); } }, executor); } protected void checkArgNotNull(Object arg, String argName) { if (arg == null) { try { close(); } catch (Exception e) { logger.error("Failed to close PreparedQuery", e); } throw new IllegalArgumentException("'" + argName + "' can't be null"); } } protected void checkArg(boolean b, String errorMsg) { if (b == false) { try { close(); } catch (Exception e) { logger.error("Failed to close PreparedQuery", e); } throw new IllegalArgumentException(errorMsg); } } @Override public void close() { if (isClosed) { return; } isClosed = true; try { if (isBatch) { stmt.clearBatch(); } else { stmt.clearParameters(); } } catch (SQLException e) { logger.error("Failed to clear the parameters set in Statements", e); } finally { if (closeHandler == null) { JdbcUtil.closeQuietly(stmt, conn); } else { try { closeHandler.run(); } finally { JdbcUtil.closeQuietly(stmt, conn); } } } } void closeAfterExecutionIfAllowed() throws SQLException { if (closeAfterExecution) { close(); } } void assertNotClosed() { if (isClosed) { throw new IllegalStateException(); } } } static class AbstractPreparedCallableQuery> extends AbstractPreparedQuery { final S stmt; AbstractPreparedCallableQuery(S stmt) { super(stmt); this.stmt = stmt; } AbstractPreparedCallableQuery(S stmt, AsyncExecutor asyncExecutor) { super(stmt, asyncExecutor); this.stmt = stmt; } public Q setNull(String parameterName, int sqlType) throws SQLException { stmt.setNull(parameterName, sqlType); return (Q) this; } public Q setNull(String parameterName, int sqlType, String typeName) throws SQLException { stmt.setNull(parameterName, sqlType, typeName); return (Q) this; } public Q setBoolean(String parameterName, boolean x) throws SQLException { stmt.setBoolean(parameterName, x); return (Q) this; } public Q setBoolean(String parameterName, Boolean x) throws SQLException { stmt.setBoolean(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setByte(String parameterName, byte x) throws SQLException { stmt.setByte(parameterName, x); return (Q) this; } public Q setByte(String parameterName, Byte x) throws SQLException { stmt.setByte(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setShort(String parameterName, short x) throws SQLException { stmt.setShort(parameterName, x); return (Q) this; } public Q setShort(String parameterName, Short x) throws SQLException { stmt.setShort(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setInt(String parameterName, int x) throws SQLException { stmt.setInt(parameterName, x); return (Q) this; } public Q setInt(String parameterName, Integer x) throws SQLException { stmt.setInt(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setLong(String parameterName, long x) throws SQLException { stmt.setLong(parameterName, x); return (Q) this; } public Q setLong(String parameterName, Long x) throws SQLException { stmt.setLong(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setFloat(String parameterName, float x) throws SQLException { stmt.setFloat(parameterName, x); return (Q) this; } public Q setFloat(String parameterName, Float x) throws SQLException { stmt.setFloat(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setDouble(String parameterName, double x) throws SQLException { stmt.setDouble(parameterName, x); return (Q) this; } public Q setDouble(String parameterName, Double x) throws SQLException { stmt.setDouble(parameterName, Primitives.unboxOrDefault(x)); return (Q) this; } public Q setBigDecimal(String parameterName, BigDecimal x) throws SQLException { stmt.setBigDecimal(parameterName, x); return (Q) this; } public Q setString(String parameterName, String x) throws SQLException { stmt.setString(parameterName, x); return (Q) this; } public Q setDate(String parameterName, java.sql.Date x) throws SQLException { stmt.setDate(parameterName, x); return (Q) this; } public Q setDate(String parameterName, java.util.Date x) throws SQLException { stmt.setDate(parameterName, x == null ? null : x instanceof java.sql.Date ? (java.sql.Date) x : new java.sql.Date(x.getTime())); return (Q) this; } public Q setTime(String parameterName, java.sql.Time x) throws SQLException { stmt.setTime(parameterName, x); return (Q) this; } public Q setTime(String parameterName, java.util.Date x) throws SQLException { stmt.setTime(parameterName, x == null ? null : x instanceof java.sql.Time ? (java.sql.Time) x : new java.sql.Time(x.getTime())); return (Q) this; } public Q setTimestamp(String parameterName, java.sql.Timestamp x) throws SQLException { stmt.setTimestamp(parameterName, x); return (Q) this; } public Q setTimestamp(String parameterName, java.util.Date x) throws SQLException { stmt.setTimestamp(parameterName, x == null ? null : x instanceof java.sql.Timestamp ? (java.sql.Timestamp) x : new java.sql.Timestamp(x.getTime())); return (Q) this; } public Q setBytes(String parameterName, byte[] x) throws SQLException { stmt.setBytes(parameterName, x); return (Q) this; } public Q setAsciiStream(String parameterName, InputStream inputStream) throws SQLException { stmt.setAsciiStream(parameterName, inputStream); return (Q) this; } public Q setAsciiStream(String parameterName, InputStream inputStream, long length) throws SQLException { stmt.setAsciiStream(parameterName, inputStream, length); return (Q) this; } public Q setBinaryStream(String parameterName, InputStream inputStream) throws SQLException { stmt.setBinaryStream(parameterName, inputStream); return (Q) this; } public Q setBinaryStream(String parameterName, InputStream inputStream, long length) throws SQLException { stmt.setBinaryStream(parameterName, inputStream, length); return (Q) this; } public Q setCharacterStream(String parameterName, Reader reader) throws SQLException { stmt.setCharacterStream(parameterName, reader); return (Q) this; } public Q setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { stmt.setCharacterStream(parameterName, reader, length); return (Q) this; } public Q setNCharacterStream(String parameterName, Reader reader) throws SQLException { stmt.setNCharacterStream(parameterName, reader); return (Q) this; } public Q setNCharacterStream(String parameterName, Reader reader, long length) throws SQLException { stmt.setNCharacterStream(parameterName, reader, length); return (Q) this; } public Q setBlob(String parameterName, java.sql.Blob x) throws SQLException { stmt.setBlob(parameterName, x); return (Q) this; } public Q setBlob(String parameterName, InputStream inputStream) throws SQLException { stmt.setBlob(parameterName, inputStream); return (Q) this; } public Q setBlob(String parameterName, InputStream inputStream, long length) throws SQLException { stmt.setBlob(parameterName, inputStream, length); return (Q) this; } public Q setClob(String parameterName, java.sql.Clob x) throws SQLException { stmt.setClob(parameterName, x); return (Q) this; } public Q setClob(String parameterName, Reader reader) throws SQLException { stmt.setClob(parameterName, reader); return (Q) this; } public Q setClob(String parameterName, Reader reader, long length) throws SQLException { stmt.setClob(parameterName, reader, length); return (Q) this; } public Q setNClob(String parameterName, java.sql.NClob x) throws SQLException { stmt.setNClob(parameterName, x); return (Q) this; } public Q setNClob(String parameterName, Reader reader) throws SQLException { stmt.setNClob(parameterName, reader); return (Q) this; } public Q setNClob(String parameterName, Reader reader, long length) throws SQLException { stmt.setNClob(parameterName, reader, length); return (Q) this; } /** * * @param parameterName * @param x * @return * @throws SQLException */ public Q setURL(String parameterName, URL x) throws SQLException { stmt.setURL(parameterName, x); return (Q) this; } /** * * @param parameterName * @param x * @return * @throws SQLException */ public Q setSQLXML(String parameterName, java.sql.SQLXML x) throws SQLException { stmt.setSQLXML(parameterName, x); return (Q) this; } /** * * @param parameterName * @param x * @return * @throws SQLException */ public Q setRowId(String parameterName, java.sql.RowId x) throws SQLException { stmt.setRowId(parameterName, x); return (Q) this; } public Q setObject(String parameterName, Object x) throws SQLException { if (x == null) { stmt.setObject(parameterName, x); } else { N.typeOf(x.getClass()).set(stmt, parameterName, x); } return (Q) this; } public Q setObject(String parameterName, Object x, int sqlType) throws SQLException { stmt.setObject(parameterName, x, sqlType); return (Q) this; } public Q setObject(String parameterName, Object x, int sqlType, int scaleOrLength) throws SQLException { stmt.setObject(parameterName, x, sqlType, scaleOrLength); return (Q) this; } public Q setParameters(Map parameters) throws SQLException { checkArgNotNull(parameters, "parameters"); for (Map.Entry entry : parameters.entrySet()) { setObject(entry.getKey(), entry.getValue()); } return (Q) this; } /** * * @param parameterNames * @param entity * @return * @throws SQLException * @see {@link ClassUtil#getPropNameList(Class)} * @see {@link ClassUtil#getPropNameListExclusively(Class, Set)} * @see {@link ClassUtil#getPropNameListExclusively(Class, Collection)} * @see {@link JdbcUtil#getNamedParameters(String)} */ public Q setParameters(List parameterNames, Object entity) throws SQLException { checkArgNotNull(parameterNames, "parameterNames"); checkArgNotNull(entity, "entity"); for (String parameterName : parameterNames) { setObject(parameterName, ClassUtil.getPropValue(entity, parameterName)); } return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, int sqlType) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @param scale * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType, scale); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @param typeName * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, int sqlType, String typeName) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType, typeName); return (Q) this; } public Q registerOutParameter(String parameterName, int sqlType) throws SQLException { stmt.registerOutParameter(parameterName, sqlType); return (Q) this; } public Q registerOutParameter(String parameterName, int sqlType, int scale) throws SQLException { stmt.registerOutParameter(parameterName, sqlType, scale); return (Q) this; } public Q registerOutParameter(String parameterName, int sqlType, String typeName) throws SQLException { stmt.registerOutParameter(parameterName, sqlType, typeName); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, SQLType sqlType) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @param scale * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, SQLType sqlType, int scale) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType, scale); return (Q) this; } /** * * @param parameterIndex starts from 1, not 0. * @param sqlType * @param typeName * @return * @throws SQLException */ public Q registerOutParameter(int parameterIndex, SQLType sqlType, String typeName) throws SQLException { stmt.registerOutParameter(parameterIndex, sqlType, typeName); return (Q) this; } public Q registerOutParameter(String parameterName, SQLType sqlType) throws SQLException { stmt.registerOutParameter(parameterName, sqlType); return (Q) this; } public Q registerOutParameter(String parameterName, SQLType sqlType, int scale) throws SQLException { stmt.registerOutParameter(parameterName, sqlType, scale); return (Q) this; } public Q registerOutParameter(String parameterName, SQLType sqlType, String typeName) throws SQLException { stmt.registerOutParameter(parameterName, sqlType, typeName); return (Q) this; } public Q registerOutParameters(final ParametersSetter register) throws SQLException { checkArgNotNull(register, "register"); boolean noException = false; try { register.accept(stmt); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } public Q registerOutParameters(final T parameter, final BiParametersSetter register) throws SQLException { checkArgNotNull(register, "register"); boolean noException = false; try { register.accept(parameter, stmt); noException = true; } finally { if (noException == false) { close(); } } return (Q) this; } public Optional call(final ResultExtractor resultExtrator1) throws SQLException { checkArgNotNull(resultExtrator1, "resultExtrator1"); assertNotClosed(); try { if (stmt.execute()) { if (stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { return Optional.of(resultExtrator1.apply(rs)); } } } } finally { closeAfterExecutionIfAllowed(); } return Optional.empty(); } public Tuple2, Optional> call(final ResultExtractor resultExtrator1, final ResultExtractor resultExtrator2) throws SQLException { checkArgNotNull(resultExtrator1, "resultExtrator1"); checkArgNotNull(resultExtrator2, "resultExtrator2"); assertNotClosed(); Optional result1 = Optional.empty(); Optional result2 = Optional.empty(); try { if (stmt.execute()) { if (stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result1 = Optional.of(resultExtrator1.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result2 = Optional.of(resultExtrator2.apply(rs)); } } } } finally { closeAfterExecutionIfAllowed(); } return Tuple.of(result1, result2); } public Tuple3, Optional, Optional> call(final ResultExtractor resultExtrator1, final ResultExtractor resultExtrator2, final ResultExtractor resultExtrator3) throws SQLException { checkArgNotNull(resultExtrator1, "resultExtrator1"); checkArgNotNull(resultExtrator2, "resultExtrator2"); checkArgNotNull(resultExtrator3, "resultExtrator3"); assertNotClosed(); Optional result1 = Optional.empty(); Optional result2 = Optional.empty(); Optional result3 = Optional.empty(); try { if (stmt.execute()) { if (stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result1 = Optional.of(resultExtrator1.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result2 = Optional.of(resultExtrator2.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result3 = Optional.of(resultExtrator3.apply(rs)); } } } } finally { closeAfterExecutionIfAllowed(); } return Tuple.of(result1, result2, result3); } public Tuple4, Optional, Optional, Optional> call(final ResultExtractor resultExtrator1, final ResultExtractor resultExtrator2, final ResultExtractor resultExtrator3, final ResultExtractor resultExtrator4) throws SQLException { checkArgNotNull(resultExtrator1, "resultExtrator1"); checkArgNotNull(resultExtrator2, "resultExtrator2"); checkArgNotNull(resultExtrator3, "resultExtrator3"); checkArgNotNull(resultExtrator4, "resultExtrator4"); assertNotClosed(); Optional result1 = Optional.empty(); Optional result2 = Optional.empty(); Optional result3 = Optional.empty(); Optional result4 = Optional.empty(); try { if (stmt.execute()) { if (stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result1 = Optional.of(resultExtrator1.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result2 = Optional.of(resultExtrator2.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result3 = Optional.of(resultExtrator3.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result4 = Optional.of(resultExtrator4.apply(rs)); } } } } finally { closeAfterExecutionIfAllowed(); } return Tuple.of(result1, result2, result3, result4); } public Tuple5, Optional, Optional, Optional, Optional> call(final ResultExtractor resultExtrator1, final ResultExtractor resultExtrator2, final ResultExtractor resultExtrator3, final ResultExtractor resultExtrator4, final ResultExtractor resultExtrator5) throws SQLException { checkArgNotNull(resultExtrator1, "resultExtrator1"); checkArgNotNull(resultExtrator2, "resultExtrator2"); checkArgNotNull(resultExtrator3, "resultExtrator3"); checkArgNotNull(resultExtrator4, "resultExtrator4"); checkArgNotNull(resultExtrator5, "resultExtrator5"); assertNotClosed(); Optional result1 = Optional.empty(); Optional result2 = Optional.empty(); Optional result3 = Optional.empty(); Optional result4 = Optional.empty(); Optional result5 = Optional.empty(); try { if (stmt.execute()) { if (stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result1 = Optional.of(resultExtrator1.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result2 = Optional.of(resultExtrator2.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result3 = Optional.of(resultExtrator3.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result4 = Optional.of(resultExtrator4.apply(rs)); } } if (stmt.getMoreResults() && stmt.getUpdateCount() == -1) { try (ResultSet rs = stmt.getResultSet()) { result5 = Optional.of(resultExtrator5.apply(rs)); } } } } finally { closeAfterExecutionIfAllowed(); } return Tuple.of(result1, result2, result3, result4, result5); } } /** * The backed {@code PreparedStatement/CallableStatement} will be closed by default * after any execution methods(which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/...). * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* Generally, don't cache or reuse the instance of this class, * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* The {@code ResultSet} returned by query will always be closed after execution, even {@code 'closeAfterExecution'} flag is set to {@code false}. * *
* Remember: parameter/column index in {@code PreparedStatement/ResultSet} starts from 1, not 0. * * @author haiyangl * * @see {@link com.landawn.abacus.annotation.ReadOnly} * @see {@link com.landawn.abacus.annotation.ReadOnlyId} * @see {@link com.landawn.abacus.annotation.NonUpdatable} * @see {@link com.landawn.abacus.annotation.Transient} * @see {@link com.landawn.abacus.annotation.Table} * @see {@link com.landawn.abacus.annotation.Column} * * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html */ public static class PreparedQuery extends AbstractPreparedQuery { PreparedQuery(PreparedStatement stmt) { super(stmt); } PreparedQuery(PreparedStatement stmt, AsyncExecutor asyncExecutor) { super(stmt, asyncExecutor); } } /** * The backed {@code PreparedStatement/CallableStatement} will be closed by default * after any execution methods(which will trigger the backed {@code PreparedStatement/CallableStatement} to be executed, for example: get/query/queryForInt/Long/../findFirst/list/execute/...). * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* Generally, don't cache or reuse the instance of this class, * except the {@code 'closeAfterExecution'} flag is set to {@code false} by calling {@code #closeAfterExecution(false)}. * *
* The {@code ResultSet} returned by query will always be closed after execution, even {@code 'closeAfterExecution'} flag is set to {@code false}. * *
* Remember: parameter/column index in {@code PreparedStatement/ResultSet} starts from 1, not 0. * * @author haiyangl * * @see {@link com.landawn.abacus.annotation.ReadOnly} * @see {@link com.landawn.abacus.annotation.ReadOnlyId} * @see {@link com.landawn.abacus.annotation.NonUpdatable} * @see {@link com.landawn.abacus.annotation.Transient} * @see {@link com.landawn.abacus.annotation.Table} * @see {@link com.landawn.abacus.annotation.Column} * * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html */ public static class PreparedCallableQuery extends AbstractPreparedCallableQuery { PreparedCallableQuery(CallableStatement stmt) { super(stmt); } PreparedCallableQuery(CallableStatement stmt, AsyncExecutor asyncExecutor) { super(stmt, asyncExecutor); } } // public static class PreparedCallableQueryII extends AbstractPreparedCallableQuery { // private final NamedSQL namedSQL; // // PreparedCallableQueryII(String sql, CallableStatement stmt) { // super(stmt); // this.namedSQL = NamedSQL.parse(sql); // } // // PreparedCallableQueryII(String sql, CallableStatement stmt, AsyncExecutor asyncExecutor) { // super(stmt, asyncExecutor); // this.namedSQL = NamedSQL.parse(sql); // } // // /** // * // * @param entity // * @return // * @throws SQLException // */ // public PreparedCallableQueryII setParameters(final Object entity) throws SQLException { // checkArgNotNull(entity, "entity"); // // StatementSetter.DEFAULT.setParameters(namedSQL, stmt, N.asArray(entity)); // // return this; // } // } public static class SimpleTransaction { private static final Logger logger = LoggerFactory.getLogger(SimpleTransaction.class); private static final Map threadTransacionMap = new ConcurrentHashMap<>(); // private static final Map attachedThreadTransacionMap = new ConcurrentHashMap<>(); private final String id; private final String timeId; private final javax.sql.DataSource ds; private final Connection conn; private final boolean closeConnection; private final boolean originalAutoCommit; private final int originalIsolationLevel; private final boolean autoSharedByPrepareQuery; private IsolationLevel isolationLevel; private Transaction.Status status = Status.ACTIVE; private final AtomicInteger refCount = new AtomicInteger(); private final Stack isolationLevelStack = new Stack<>(); private boolean isMarkedByCommitPreviously = false; SimpleTransaction(final javax.sql.DataSource ds, final Connection conn, final IsolationLevel isolationLevel, final boolean closeConnection, final boolean autoSharedByPrepareQuery) throws SQLException { N.checkArgNotNull(conn); N.checkArgNotNull(isolationLevel); this.id = getTransactionId(ds); this.timeId = id + "_" + System.currentTimeMillis(); this.ds = ds; this.conn = conn; this.isolationLevel = isolationLevel; this.closeConnection = closeConnection; this.originalAutoCommit = conn.getAutoCommit(); this.originalIsolationLevel = conn.getTransactionIsolation(); this.autoSharedByPrepareQuery = autoSharedByPrepareQuery; conn.setAutoCommit(false); if (isolationLevel == IsolationLevel.DEFAULT) { conn.setTransactionIsolation(isolationLevel.intValue()); } } public String id() { return timeId; } public Connection connection() { return conn; } public IsolationLevel isolationLevel() { return isolationLevel; } public Transaction.Status status() { return status; } public boolean isActive() { return status == Status.ACTIVE; } // /** // * Attaches this transaction to current thread. // * // */ // public void attach() { // final String currentThreadName = Thread.currentThread().getName(); // final String resourceId = ttid.substring(ttid.lastIndexOf('_') + 1); // final String targetTTID = currentThreadName + "_" + resourceId; // // if (attachedThreadTransacionMap.containsKey(targetTTID)) { // throw new IllegalStateException("Transaction(id=" + attachedThreadTransacionMap.get(targetTTID).id() // + ") has already been attached to current thread: " + currentThreadName); // } else if (threadTransacionMap.containsKey(targetTTID)) { // throw new IllegalStateException( // "Transaction(id=" + threadTransacionMap.get(targetTTID).id() + ") has already been created in current thread: " + currentThreadName); // } // // attachedThreadTransacionMap.put(targetTTID, this); // threadTransacionMap.put(targetTTID, this); // } // // public void detach() { // final String currentThreadName = Thread.currentThread().getName(); // final String resourceId = ttid.substring(ttid.lastIndexOf('_') + 1); // final String targetTTID = currentThreadName + "_" + resourceId; // // if (!attachedThreadTransacionMap.containsKey(targetTTID)) { // throw new IllegalStateException( // "Transaction(id=" + attachedThreadTransacionMap.get(targetTTID).id() + ") is not attached to current thread: " + currentThreadName); // } // // threadTransacionMap.remove(targetTTID); // attachedThreadTransacionMap.remove(targetTTID); // } public void commit() throws SQLException { final int refCount = decrementAndGetRef(); isMarkedByCommitPreviously = true; if (refCount > 0) { return; } else if (refCount < 0) { logger.warn("Transaction(id={}) is already: {}. This committing is ignored", timeId, status); return; } if (status == Status.MARKED_ROLLBACK) { logger.warn("Transaction(id={}) will be rolled back because it's marked for roll back only", timeId); executeRollback(); return; } if (status != Status.ACTIVE) { throw new IllegalArgumentException("Transaction(id=" + timeId + ") is already: " + status + ". It can not be committed"); } logger.info("Committing transaction(id={})", timeId); status = Status.FAILED_COMMIT; try { if (originalAutoCommit) { conn.commit(); } status = Status.COMMITTED; } finally { if (status == Status.COMMITTED) { logger.info("Transaction(id={}) has been committed successfully", timeId); resetAndCloseConnection(); } else { logger.warn("Failed to commit transaction(id={}). It will automatically be rolled back ", timeId); executeRollback(); } } } public void rollbackIfNotCommitted() throws UncheckedSQLException { if (isMarkedByCommitPreviously) { // Do nothing. It happened in finally block. isMarkedByCommitPreviously = false; return; } final int refCount = decrementAndGetRef(); if (refCount > 0) { status = Status.MARKED_ROLLBACK; return; } else if (refCount < 0) { if (refCount == -1 && (status == Status.COMMITTED || status == Status.FAILED_COMMIT || status == Status.ROLLED_BACK || status == Status.FAILED_ROLLBACK)) { // Do nothing. It happened in finally block. } else { logger.warn("Transaction(id={}) is already: {}. This rollback is ignored", timeId, status); } return; } if (!(status == Status.ACTIVE || status == Status.MARKED_ROLLBACK || status == Status.FAILED_COMMIT || status == Status.FAILED_ROLLBACK)) { throw new IllegalArgumentException("Transaction(id=" + timeId + ") is already: " + status + ". It can not be rolled back"); } executeRollback(); } private void executeRollback() throws UncheckedSQLException { logger.warn("Rolling back transaction(id={})", timeId); status = Status.FAILED_ROLLBACK; try { if (originalAutoCommit) { conn.rollback(); } status = Status.ROLLED_BACK; } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { if (status == Status.ROLLED_BACK) { logger.warn("Transaction(id={}) has been rolled back successfully", timeId); } else { logger.warn("Failed to roll back transaction(id={})", timeId); } resetAndCloseConnection(); } } private void resetAndCloseConnection() { try { conn.setAutoCommit(originalAutoCommit); conn.setTransactionIsolation(originalIsolationLevel); } catch (SQLException e) { logger.warn("Failed to reset connection", e); } finally { if (closeConnection) { JdbcUtil.releaseConnection(conn, ds); } } } synchronized int incrementAndGetRef(final IsolationLevel isolationLevel) throws UncheckedSQLException { if (!status.equals(Status.ACTIVE)) { throw new IllegalStateException("Transaction(id=" + timeId + ") is already: " + status); } isMarkedByCommitPreviously = false; if (refCount.get() > 0) { try { conn.setTransactionIsolation(isolationLevel == IsolationLevel.DEFAULT ? this.originalIsolationLevel : isolationLevel.intValue()); } catch (SQLException e) { throw new UncheckedSQLException(e); } this.isolationLevelStack.push(this.isolationLevel); this.isolationLevel = isolationLevel; } return refCount.incrementAndGet(); } synchronized int decrementAndGetRef() throws UncheckedSQLException { final int res = refCount.decrementAndGet(); if (res == 0) { threadTransacionMap.remove(id); logger.info("Finishing transaction(id={})", timeId); logger.debug("Remaining active transactions: {}", threadTransacionMap.values()); } else if (res > 0) { this.isolationLevel = isolationLevelStack.pop(); try { conn.setTransactionIsolation(isolationLevel == IsolationLevel.DEFAULT ? this.originalIsolationLevel : isolationLevel.intValue()); } catch (SQLException e) { throw new UncheckedSQLException(e); } } return res; } boolean autoSharedByPrepareQuery() { return autoSharedByPrepareQuery; } static String getTransactionId(final javax.sql.DataSource ds) { return Thread.currentThread().getName() + "_" + System.identityHashCode(ds); } static String getTransactionThreadId(final Connection conn) { return Thread.currentThread().getName() + "_" + System.identityHashCode(conn); } static SimpleTransaction getTransaction(final javax.sql.DataSource ds) { return threadTransacionMap.get(getTransactionId(ds)); } static SimpleTransaction getTransaction(final Connection conn) { return threadTransacionMap.get(getTransactionThreadId(conn)); } static SimpleTransaction putTransaction(final javax.sql.DataSource ds, final SimpleTransaction tran) { return threadTransacionMap.put(getTransactionId(ds), tran); } static SimpleTransaction putTransaction(final Connection conn, final SimpleTransaction tran) { return threadTransacionMap.put(getTransactionThreadId(conn), tran); } @Override public int hashCode() { return timeId.hashCode(); } @Override public boolean equals(Object obj) { return obj instanceof SimpleTransaction && timeId.equals(((SimpleTransaction) obj).timeId); } @Override public String toString() { return "SimpleTransaction={id=" + timeId + "}"; } } public static enum FetchDirection { FORWARD(ResultSet.FETCH_FORWARD), REVERSE(ResultSet.FETCH_REVERSE), UNKNOWN(ResultSet.FETCH_UNKNOWN); private final int intValue; FetchDirection(int intValue) { this.intValue = intValue; } public static FetchDirection valueOf(int intValue) { switch (intValue) { case ResultSet.FETCH_FORWARD: return FORWARD; case ResultSet.FETCH_REVERSE: return REVERSE; case ResultSet.FETCH_UNKNOWN: return UNKNOWN; default: throw new IllegalArgumentException("No FetchDirection mapping to int value: " + intValue); } } public int intValue() { return intValue; } } public static interface ParametersSetter extends Try.Consumer { @Override void accept(QS preparedQuery) throws SQLException; } public static interface BiParametersSetter extends Try.BiConsumer { @Override void accept(QS preparedQuery, T t) throws SQLException; } public static interface ResultExtractor extends Try.Function { public static final ResultExtractor TO_DATA_SET = new ResultExtractor() { @Override public DataSet apply(final ResultSet rs) throws SQLException { return JdbcUtil.extractData(rs); } }; @Override T apply(ResultSet rs) throws SQLException; /** * * @param keyExtractor * @param valueExtractor * @return */ public static ResultExtractor> toMap(final RowMapper keyExtractor, final RowMapper valueExtractor) { return toMap(keyExtractor, valueExtractor, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param supplier * @return */ public static > ResultExtractor toMap(final RowMapper keyExtractor, final RowMapper valueExtractor, final Supplier supplier) { return toMap(keyExtractor, valueExtractor, FN.throwingMerger(), supplier); } /** * * @param keyExtractor * @param valueExtractor * @param mergeFunction * @return * @see {@link Fn.EE#throwingMerger()} * @see {@link Fn.EE#replacingMerger()} * @see {@link Fn.EE#ignoringMerger()} */ public static ResultExtractor> toMap(final RowMapper keyExtractor, final RowMapper valueExtractor, final Try.BinaryOperator mergeFunction) { return toMap(keyExtractor, valueExtractor, mergeFunction, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param mergeFunction * @param supplier * @return * @see {@link Fn.EE#throwingMerger()} * @see {@link Fn.EE#replacingMerger()} * @see {@link Fn.EE#ignoringMerger()} */ public static > ResultExtractor toMap(final RowMapper keyExtractor, final RowMapper valueExtractor, final Try.BinaryOperator mergeFunction, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(mergeFunction, "mergeFunction"); N.checkArgNotNull(supplier, "supplier"); return new ResultExtractor() { @Override public M apply(final ResultSet rs) throws SQLException { final M result = supplier.get(); while (rs.next()) { Maps.merge(result, keyExtractor.apply(rs), valueExtractor.apply(rs), mergeFunction); } return result; } }; } /** * * @param keyExtractor * @param valueExtractor * @param downstream * @return */ public static ResultExtractor> toMap(final RowMapper keyExtractor, final RowMapper valueExtractor, final Collector downstream) { return toMap(keyExtractor, valueExtractor, downstream, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param downstream * @param supplier * @return */ public static > ResultExtractor toMap(final RowMapper keyExtractor, final RowMapper valueExtractor, final Collector downstream, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(downstream, "downstream"); N.checkArgNotNull(supplier, "supplier"); return new ResultExtractor() { @Override public M apply(final ResultSet rs) throws SQLException { final Supplier downstreamSupplier = downstream.supplier(); final BiConsumer downstreamAccumulator = downstream.accumulator(); final Function downstreamFinisher = downstream.finisher(); final M result = supplier.get(); final Map tmp = (Map) result; K key = null; A container = null; while (rs.next()) { key = keyExtractor.apply(rs); container = tmp.get(key); if (container == null) { container = downstreamSupplier.get(); tmp.put(key, container); } downstreamAccumulator.accept(container, valueExtractor.apply(rs)); } for (Map.Entry entry : result.entrySet()) { entry.setValue(downstreamFinisher.apply((A) entry.getValue())); } return result; } }; } public static ResultExtractor>> groupTo(final RowMapper keyExtractor, final RowMapper valueExtractor) throws SQLException { return groupTo(keyExtractor, valueExtractor, Suppliers.> ofMap()); } public static >> ResultExtractor groupTo(final RowMapper keyExtractor, final RowMapper valueExtractor, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(supplier, "supplier"); return new ResultExtractor() { @Override public M apply(final ResultSet rs) throws SQLException { final M result = supplier.get(); K key = null; List value = null; while (rs.next()) { key = keyExtractor.apply(rs); value = result.get(key); if (value == null) { value = new ArrayList<>(); result.put(key, value); } value.add(valueExtractor.apply(rs)); } return result; } }; } } public static interface BiResultExtractor extends Try.BiFunction, T, SQLException> { @Override T apply(ResultSet rs, List columnLabels) throws SQLException; /** * * @param keyExtractor * @param valueExtractor * @return */ public static BiResultExtractor> toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor) { return toMap(keyExtractor, valueExtractor, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param supplier * @return */ public static > BiResultExtractor toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Supplier supplier) { return toMap(keyExtractor, valueExtractor, FN.throwingMerger(), supplier); } /** * * @param keyExtractor * @param valueExtractor * @param mergeFunction * @return * @see {@link Fn.EE#throwingMerger()} * @see {@link Fn.EE#replacingMerger()} * @see {@link Fn.EE#ignoringMerger()} */ public static BiResultExtractor> toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Try.BinaryOperator mergeFunction) { return toMap(keyExtractor, valueExtractor, mergeFunction, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param mergeFunction * @param supplier * @return * @see {@link Fn.EE#throwingMerger()} * @see {@link Fn.EE#replacingMerger()} * @see {@link Fn.EE#ignoringMerger()} */ public static > BiResultExtractor toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Try.BinaryOperator mergeFunction, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(mergeFunction, "mergeFunction"); N.checkArgNotNull(supplier, "supplier"); return new BiResultExtractor() { @Override public M apply(final ResultSet rs, final List columnLabels) throws SQLException { final M result = supplier.get(); while (rs.next()) { Maps.merge(result, keyExtractor.apply(rs, columnLabels), valueExtractor.apply(rs, columnLabels), mergeFunction); } return result; } }; } /** * * @param keyExtractor * @param valueExtractor * @param downstream * @return */ public static BiResultExtractor> toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Collector downstream) { return toMap(keyExtractor, valueExtractor, downstream, Suppliers. ofMap()); } /** * * @param keyExtractor * @param valueExtractor * @param downstream * @param supplier * @return */ public static > BiResultExtractor toMap(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Collector downstream, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(downstream, "downstream"); N.checkArgNotNull(supplier, "supplier"); return new BiResultExtractor() { @Override public M apply(final ResultSet rs, final List columnLabels) throws SQLException { final Supplier downstreamSupplier = downstream.supplier(); final BiConsumer downstreamAccumulator = downstream.accumulator(); final Function downstreamFinisher = downstream.finisher(); final M result = supplier.get(); final Map tmp = (Map) result; K key = null; A container = null; while (rs.next()) { key = keyExtractor.apply(rs, columnLabels); container = tmp.get(key); if (container == null) { container = downstreamSupplier.get(); tmp.put(key, container); } downstreamAccumulator.accept(container, valueExtractor.apply(rs, columnLabels)); } for (Map.Entry entry : result.entrySet()) { entry.setValue(downstreamFinisher.apply((A) entry.getValue())); } return result; } }; } public static BiResultExtractor>> groupTo(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor) throws SQLException { return groupTo(keyExtractor, valueExtractor, Suppliers.> ofMap()); } public static >> BiResultExtractor groupTo(final BiRowMapper keyExtractor, final BiRowMapper valueExtractor, final Supplier supplier) { N.checkArgNotNull(keyExtractor, "keyExtractor"); N.checkArgNotNull(valueExtractor, "valueExtractor"); N.checkArgNotNull(supplier, "supplier"); return new BiResultExtractor() { @Override public M apply(final ResultSet rs, List columnLabels) throws SQLException { final M result = supplier.get(); K key = null; List value = null; while (rs.next()) { key = keyExtractor.apply(rs, columnLabels); value = result.get(key); if (value == null) { value = new ArrayList<>(); result.put(key, value); } value.add(valueExtractor.apply(rs, columnLabels)); } return result; } }; } } /** * Don't use {@code RowMapper} in {@link PreparedQuery#list(RowMapper)} or any place where multiple records will be retrieved by it, if column labels/count are used in {@link RowMapper#apply(ResultSet)}. * Consider using {@code BiRowMapper} instead because it's more efficient to retrieve multiple records when column labels/count are used. * * @param */ public static interface RowMapper extends Try.Function { public static final RowMapper GET_BOOLEAN = new RowMapper() { @Override public Boolean apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getBoolean(1); } }; public static final RowMapper GET_BYTE = new RowMapper() { @Override public Byte apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getByte(1); } }; public static final RowMapper GET_SHORT = new RowMapper() { @Override public Short apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getShort(1); } }; public static final RowMapper GET_INT = new RowMapper() { @Override public Integer apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getInt(1); } }; public static final RowMapper GET_LONG = new RowMapper() { @Override public Long apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getLong(1); } }; public static final RowMapper GET_FLOAT = new RowMapper() { @Override public Float apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getFloat(1); } }; public static final RowMapper GET_DOUBLE = new RowMapper() { @Override public Double apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getDouble(1); } }; public static final RowMapper GET_BIG_DECIMAL = new RowMapper() { @Override public BigDecimal apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getBigDecimal(1); } }; public static final RowMapper GET_STRING = new RowMapper() { @Override public String apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getString(1); } }; public static final RowMapper GET_DATE = new RowMapper() { @Override public Date apply(final ResultSet rs) throws SQLException, RuntimeException { return rs.getDate(1); } }; public static final RowMapper