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

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

There is a newer version: 1.8.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 java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.io.Closeable;
import java.io.IOException;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

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.annotation.Beta;
import com.landawn.abacus.condition.And;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.condition.ConditionFactory.L;
import com.landawn.abacus.condition.Equal;
import com.landawn.abacus.core.RowDataSet;
import com.landawn.abacus.core.sql.dataSource.SQLDataSource;
import com.landawn.abacus.dataChannel.StatementDataChannel;
import com.landawn.abacus.exception.AbacusException;
import com.landawn.abacus.exception.NonUniqueResultException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.SQLBuilder.NE;
import com.landawn.abacus.util.SQLBuilder.NE2;
import com.landawn.abacus.util.SQLBuilder.NE3;
import com.landawn.abacus.util.SQLBuilder.SP;
import com.landawn.abacus.util.function.Consumer;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.stream.Stream;

/**
 * SQLExecutor is a simple sql/jdbc utility class. SQL is supported with different format: 
* *
 * 
 * 
  • INSERT INTO account (first_name, last_name, gui, last_update_time, create_time) VALUES (?, ?, ?, ?, ?)
  • *
  • INSERT INTO account (first_name, last_name, gui, last_update_time, create_time) VALUES (#{firstName}, #{lastName}, #{gui}, #{lastUpdateTime}, #{createTime})
  • *
  • INSERT INTO account (first_name, last_name, gui, last_update_time, create_time) VALUES (:firstName, :lastName, :gui, :lastUpdateTime, :createTime)
  • * * All these kinds of SQLs can be generated by SQLBuilder conveniently. Parameters with format of Array/List parameters are supported for parameterized sql with '?'. * Parameters with format of Array/List/Map/Entity are supported for parameterized SQL with named parameters. *
    * * Here is sample of CRUD(create/read/update/delete): *
    ======================================================================== *
     * 
            static final DataSource dataSource = JdbcUtil.createDataSource(...);
            static final SQLExecutor sqlExecutor = new SQLExecutor(dataSource);
            ...
            Account account = createAccount();
    
            // create
            String sql_insert = NE.insert(GUI, FIRST_NAME, LAST_NAME, LAST_UPDATE_TIME, CREATE_TIME).into(Account.class).sql();
            N.println(sql_insert);
            sqlExecutor.insert(sql_insert, account);
    
            // read
            String sql_selectByGUI = NE.selectFrom(Account.class, N.asSet(DEVICES)).where(L.eq(GUI, L.QME)).sql();
            N.println(sql_selectByGUI);
            Account dbAccount = sqlExecutor.queryForEntity(Account.class, sql_selectByGUI, account);
            assertEquals(account.getFirstName(), dbAccount.getFirstName());
    
            // update
            String sql_updateByLastName = NE.update(Account.class).set(FIRST_NAME).where(L.eq(LAST_NAME, L.QME)).sql();
            N.println(sql_updateByLastName);
            dbAccount.setFirstName("newFirstName");
            sqlExecutor.update(sql_updateByLastName, dbAccount);
    
            // delete
            String sql_deleteByFirstName = NE.deleteFrom(Account.class).where(L.eq(FIRST_NAME, L.QME)).sql();
            N.println(sql_deleteByFirstName);
            sqlExecutor.update(sql_deleteByFirstName, dbAccount);
    
            dbAccount = sqlExecutor.queryForEntity(Account.class, sql_selectByGUI, account);
            assertNull(dbAccount);
     * 
     * 
    * ======================================================================== *
    *
    * If {@code conn} argument is null or not specified, {@code SQLExecutor} is responsible to get the connection from the * internal {@code DataSource}, start and commit/roll back transaction for batch operations if needed, and close the * connection finally. otherwise it's user's responsibility to do such jobs if {@code conn} is specified and not null.
    *
    * * The general programming way with SQLExeucte is to execute sql scripts(generated by SQLBuilder) with array/list/map/entity by calling (batch)insert/update/delete/query/... methods. * if Transaction is required. it can be started: *
     * 
     *      final SQLTransaction tran = sqlExecutor.beginTransaction(IsolationLevel.READ_COMMITTED);
            boolean noException = false;
            try {
                // sqlExecutor.insert(tran.getConnection(), ...);
                // sqlExecutor.update(tran.getConnection(), ...);
                // sqlExecutor.query(tran.getConnection(), ...);
    
                noException = true;
            } finally {
                // The connection will be automatically closed after the transaction is committed or rolled back.            
                if (noException) {
                    tran.commit();
                } else {
                    tran.rollback();
                }
            }
     * 
     * 
    * * SQLExecutor is tread-safe.

    * * @since 0.8 * * @author Haiyang Li * * @see JdbcUtil * @see http://docs.oracle.com/javase/7/docs/api/java/sql/Connection.html * @see http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html * @see http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html * @see http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html */ public final class SQLExecutor implements Closeable { private static final Logger logger = LoggerFactory.getLogger(SQLExecutor.class); static final String ID = "id"; static final String QUERY_WITH_DATA_SOURCE = "queryWithDataSource"; static final StatementSetter DEFAULT_STATEMENT_SETTER = new DefaultStatementSetter(); static final ResultSetExtractor DEFAULT_RESULT_SET_EXTRACTOR = new DefaultResultSetExtractor(); static final ResultSetExtractor ROW_ITERATOR_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor() { @Override public RowIterator extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { return new RowIterator(rs, jdbcSettings.getOffset(), jdbcSettings.getCount(), true, true); } }; static final ResultSetExtractor EXISTS_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor() { @Override public Boolean extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { long offset = jdbcSettings.getOffset(); while ((offset-- > 0) && rs.next()) { } return offset <= 0 && rs.next(); } }; static final ResultSetExtractor> SINGLE_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor>() { @Override public Nullable extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { long offset = jdbcSettings.getOffset(); while ((offset-- > 0) && rs.next()) { } if (offset <= 0 && rs.next()) { return Nullable.of(rs.getObject(1)); } return Nullable.empty(); } }; static final ResultSetExtractor> LIST_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor>() { @Override public List extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { N.checkArgument(rs.getMetaData().getColumnCount() == 1, "Multiple columns selected in sql %s", namedSQL.getPureSQL()); long offset = jdbcSettings.getOffset(); long count = jdbcSettings.getCount(); while ((offset-- > 0) && rs.next()) { } final List result = new ArrayList<>((int) N.min(count, 16)); while (count-- > 0 && rs.next()) { result.add(rs.getObject(1)); } return result; } }; @SuppressWarnings("rawtypes") private static final ResultSetExtractor ENTITY_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor() { @SuppressWarnings("deprecation") @Override public Object extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { long offset = jdbcSettings.getOffset(); while ((offset-- > 0) && rs.next()) { } if (offset <= 0 && rs.next()) { final List columnLabelList = getColumnLabelList(namedSQL, rs); final int columnCount = columnLabelList.size(); final Object entity = N.newInstance(cls); if (Map.class.isAssignableFrom(cls)) { final Map m = (Map) entity; for (int i = 0; i < columnCount; i++) { m.put(columnLabelList.get(i), rs.getObject(i + 1)); } } else { // Method method = null; // // for (int i = 0; i < columnCount; i++) { // method = N.getPropSetMethod(cls, columnLabelList.get(i)); // // if (method != null) { // N.setPropValue(entity, method, rs.getObject(i + 1)); // } // } for (int i = 0; i < columnCount; i++) { ClassUtil.setPropValue(entity, columnLabelList.get(i), rs.getObject(i + 1), true); } if (N.isDirtyMarker(cls)) { ((DirtyMarker) entity).markDirty(false); } } return entity; } return null; } }; @SuppressWarnings("rawtypes") private static final ResultSetExtractor ENTITY_LIST_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor>() { @SuppressWarnings("deprecation") @Override public List extractData(final Class cls, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { final List resultList = new ArrayList<>(); long offset = jdbcSettings.getOffset(); long count = jdbcSettings.getCount(); while ((offset-- > 0) && rs.next()) { } if (offset <= 0 && count > 0) { final String[] columnLabels = getColumnLabelList(namedSQL, rs).toArray(N.EMPTY_STRING_ARRAY); final int columnCount = columnLabels.length; final boolean isMap = Map.class.isAssignableFrom(cls); final boolean isDirtyMarker = N.isDirtyMarker(cls); while ((count-- > 0) && rs.next()) { final Object entity = N.newInstance(cls); if (isMap) { final Map m = (Map) entity; for (int i = 0; i < columnCount; i++) { m.put(columnLabels[i], rs.getObject(i + 1)); } } else { // Method method = null; // // for (int i = 0; i < columnCount; i++) { // method = N.getPropSetMethod(cls, columnLabelList.get(i)); // // if (method != null) { // N.setPropValue(entity, method, rs.getObject(i + 1)); // } // } for (int i = 0; i < columnCount; i++) { ClassUtil.setPropValue(entity, columnLabels[i], rs.getObject(i + 1), true); } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } } resultList.add(entity); } } return resultList; } }; private static final int factor = Math.min(Math.max(1, IOUtil.MAX_MEMORY_IN_MB / 1024), 8); private static final int CACHED_SQL_LENGTH = 1024 * factor; private static final int SQL_CACHE_SIZE = 1000 * factor; private static final Map> _sqlColumnLabelPool = new ConcurrentHashMap<>(); private final Map> _tableColumnNamePool = new ConcurrentHashMap<>(); private final DataSource _ds; private final DataSourceManager _dsm; private final DataSourceSelector _dss; private final JdbcSettings _jdbcSettings; private final SQLMapper _sqlMapper; private final NamingPolicy _namingPolicy; private final AsyncExecutor _asyncExecutor; private final boolean _isReadOnly; private final String _dbProudctName; private final String _dbProudctVersion; private final DBVersion _dbVersion; private final IsolationLevel _defaultIsolationLevel; private final AsyncSQLExecutor _asyncSQLExecutor; private final Map, Mapper> mapperPool = new ConcurrentHashMap<>(); /** * * @param dataSource * @see JdbcUtil#createDataSource(String) * @see JdbcUtil#createDataSource(java.io.InputStream) */ public SQLExecutor(final javax.sql.DataSource dataSource) { this(dataSource, null); } /** * * @param dataSource * @param jdbcSettings * @see JdbcUtil#createDataSource(String) * @see JdbcUtil#createDataSource(java.io.InputStream) */ public SQLExecutor(final javax.sql.DataSource dataSource, final JdbcSettings jdbcSettings) { this(dataSource, jdbcSettings, null); } /** * * @param dataSource * @param jdbcSettings * @param sqlMapper * @see JdbcUtil#createDataSource(String) * @see JdbcUtil#createDataSource(java.io.InputStream) */ public SQLExecutor(final javax.sql.DataSource dataSource, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper) { this(dataSource, jdbcSettings, sqlMapper, null); } /** * * @param dataSource * @param jdbcSettings * @param sqlMapper * @param namingPolicy * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final javax.sql.DataSource dataSource, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper, final NamingPolicy namingPolicy) { this(dataSource, jdbcSettings, sqlMapper, namingPolicy, null); } /** * * @param dataSource * @param jdbcSettings * @param sqlMapper * @param asyncExecutor * @see JdbcUtil#createDataSource(String) * @see JdbcUtil#createDataSource(java.io.InputStream) */ public SQLExecutor(final javax.sql.DataSource dataSource, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper, final NamingPolicy namingPolicy, final AsyncExecutor asyncExecutor) { this(null, JdbcUtil.wrap(dataSource), jdbcSettings, sqlMapper, namingPolicy, asyncExecutor, false); } /** * * @param dataSourceManager * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final DataSourceManager dataSourceManager) { this(dataSourceManager, null); } /** * * @param dataSourceManager * @param jdbcSettings * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final DataSourceManager dataSourceManager, final JdbcSettings jdbcSettings) { this(dataSourceManager, jdbcSettings, null); } /** * * @param dataSourceManager * @param jdbcSettings * @param sqlMapper * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final DataSourceManager dataSourceManager, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper) { this(dataSourceManager, jdbcSettings, sqlMapper, null); } /** * * @param dataSourceManager * @param jdbcSettings * @param sqlMapper * @param namingPolicy * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final DataSourceManager dataSourceManager, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper, final NamingPolicy namingPolicy) { this(dataSourceManager, jdbcSettings, sqlMapper, namingPolicy, null); } /** * * @param dataSourceManager * @param jdbcSettings * @param sqlMapper * @param asyncExecutor * @see JdbcUtil#createDataSourceManager(String) * @see JdbcUtil#createDataSourceManager(java.io.InputStream) */ public SQLExecutor(final DataSourceManager dataSourceManager, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper, final NamingPolicy namingPolicy, final AsyncExecutor asyncExecutor) { this(dataSourceManager, null, jdbcSettings, sqlMapper, namingPolicy, asyncExecutor, false); } protected SQLExecutor(final DataSourceManager dataSourceManager, final DataSource dataSource, final JdbcSettings jdbcSettings, final SQLMapper sqlMapper, final NamingPolicy namingPolicy, final AsyncExecutor asyncExecutor, final boolean isReadOnly) { if (dataSourceManager == null) { this._ds = dataSource; this._dsm = null; this._dss = null; } else { this._ds = dataSourceManager.getPrimaryDataSource(); this._dsm = dataSourceManager; this._dss = dataSourceManager.getDataSourceSelector(); } this._jdbcSettings = (jdbcSettings == null) ? JdbcSettings.create() : jdbcSettings.copy(); if (_jdbcSettings.getBatchSize() == 0) { _jdbcSettings.setBatchSize(JdbcSettings.DEFAULT_BATCH_SIZE); } _jdbcSettings.freeze(); this._sqlMapper = sqlMapper; this._namingPolicy = namingPolicy == null ? NamingPolicy.LOWER_CASE_WITH_UNDERSCORE : namingPolicy; this._asyncExecutor = asyncExecutor == null ? new AsyncExecutor(64, 300, TimeUnit.SECONDS) : asyncExecutor; this._isReadOnly = isReadOnly; Connection conn = getConnection(); try { _dbProudctName = conn.getMetaData().getDatabaseProductName(); _dbProudctVersion = conn.getMetaData().getDatabaseProductVersion(); _dbVersion = JdbcUtil.getDBVersion(conn); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { closeQuietly(conn); } _defaultIsolationLevel = this._ds instanceof SQLDataSource ? ((SQLDataSource) this._ds).getDefaultIsolationLevel() : IsolationLevel.DEFAULT; this._asyncSQLExecutor = new AsyncSQLExecutor(this, _asyncExecutor); } // public static SQLExecutor create(final String dataSourceFile) { // return new SQLExecutor(JdbcUtil.createDataSourceManager(dataSourceFile)); // } // // public static SQLExecutor create(final InputStream dataSourceInputStream) { // return new SQLExecutor(JdbcUtil.createDataSourceManager(dataSourceInputStream)); // } // // public static SQLExecutor create(final String url, final String user, final String password) { // return new SQLExecutor(JdbcUtil.createDataSource(url, user, password)); // } // // public static SQLExecutor create(final String driver, final String url, final String user, final String password) { // return new SQLExecutor(JdbcUtil.createDataSource(driver, url, user, password)); // } // // public static SQLExecutor create(final Class driverClass, final String url, final String user, final String password) { // return new SQLExecutor(JdbcUtil.createDataSource(driverClass, url, user, password)); // } // // /** // * // * @param props refer to Connection.xsd for the supported properties. // * @return // */ // public static SQLExecutor create(final Map props) { // return new SQLExecutor(JdbcUtil.createDataSource(props)); // } // // public static SQLExecutor create(final javax.sql.DataSource sqlDataSource) { // return new SQLExecutor(JdbcUtil.wrap(sqlDataSource)); // } // // public SQLMapper sqlMapper() { // return _sqlMapper; // } @Beta public static SQLExecutor w(final String url, final String user, final String password) { return new SQLExecutor(JdbcUtil.createDataSource(url, user, password)); } @Beta public static SQLExecutor w(final String driver, final String url, final String user, final String password) { return new SQLExecutor(JdbcUtil.createDataSource(driver, url, user, password)); } @Beta public static SQLExecutor w(final Class driverClass, final String url, final String user, final String password) { return new SQLExecutor(JdbcUtil.createDataSource(driverClass, url, user, password)); } public Mapper mapper(final Class targetClass) { Mapper mapper = (Mapper) mapperPool.get(targetClass); if (mapper == null) { N.checkArgument(N.isEntity(targetClass), ClassUtil.getCanonicalClassName(targetClass) + " is not an entity class with getter/setter methods"); mapper = new Mapper(targetClass, this, this._namingPolicy); mapperPool.put(targetClass, mapper); } return mapper; } // /** // * Create a Mapper which has the same life cycle as the specified Connection. // * To start transaction for a Mapper: // *
        //     * 
        //     * final Transaction tran = sqlExecutor.beginTransaction(isolationLevel);
        //     * final ExMapper mapper = sqlExecutor.mapper(targetClass, tran.connection());
        //     * boolean isOk = false;
        //     * try {
        //     *     // Do something with sqlExecutor and mapper
        //     *     isOk = true;
        //     * } finally {
        //     *     if (isOk) {
        //     *         tran.commit();
        //     *     } else {
        //     *          tran.rollback();
        //     *     }  
        //     * }
        //     * 
        //     * 
        //     * 
    // * @param targetClass // * @param conn // * @return // */ // public ExMapper mapper(final Class targetClass, final Connection conn) { // if (conn == null) { // return mapper(targetClass); // } // // return new ExMapper(conn, targetClass, this, this._namingPolicy); // } public AsyncSQLExecutor async() { return _asyncSQLExecutor; } public DataSource dataSource() { return _ds; } public JdbcSettings jdbcSettings() { return _jdbcSettings; } public String dbProudctName() { return _dbProudctName; } public String dbProudctVersion() { return _dbProudctVersion; } public DBVersion dbVersion() { return _dbVersion; } @SafeVarargs public final T insert(final String sql, final Object... parameters) { return insert(null, sql, null, null, parameters); } @SafeVarargs public final T insert(final String sql, final StatementSetter statementSetter, final Object... parameters) { return insert(null, sql, statementSetter, null, parameters); } @SafeVarargs public final T insert(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return insert(null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final T insert(final Connection conn, final String sql, final Object... parameters) { return insert(conn, sql, null, null, parameters); } @SafeVarargs public final T insert(final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return insert(conn, sql, statementSetter, null, parameters); } /** * @see #batchInsert(Connection, String, StatementSetter, JdbcSettings, String, Object[]) */ @SuppressWarnings({ "unchecked", "deprecation" }) @SafeVarargs public final T insert(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); String idPropName = checkGeneratedIdPropName(jdbcSettings); DataSource ds = null; Connection localConn = null; Object result = null; PreparedStatement stmt = null; try { ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.RETURN_GENERATED_KEYS, false, parameters); result = executeInsert(namedSQL, stmt); } catch (SQLException e) { String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { closeQuietly(stmt, localConn, conn); } if ((result != null) && isEntityOrMapParameter(namedSQL, parameters)) { Object parameter_0 = (parameters[0] instanceof TypedParameters) ? ((TypedParameters) parameters[0]).parameters[0] : parameters[0]; if (parameter_0 instanceof Map) { // // don't update input map ? // Map m = (Map) parameter_0; // Object idPropValue = m.get(generatedIdPropName); // // if ((idPropValue == null) // || (idPropValue instanceof Number // && (((Number) idPropValue).longValue() == 0))) { // m.put(generatedIdPropName, result); // } } else { final Object entity = parameter_0; try { Method idGetMethod = ClassUtil.getPropGetMethod(entity.getClass(), idPropName); Method idSetMethod = ClassUtil.getPropSetMethod(entity.getClass(), idPropName); if ((idGetMethod != null) && (idSetMethod != null)) { Object idPropValue = ClassUtil.getPropValue(entity, idGetMethod); if ((idPropValue == null) || (idPropValue instanceof Number && (((Number) idPropValue).longValue() == 0))) { ClassUtil.setPropValue(entity, idSetMethod, result); } } else { if (logger.isWarnEnabled()) { logger.warn("Failed to set the returned id property to entity. no get/set method for id property (" + idPropName + ") found. "); } } } catch (Exception e) { logger.error("Failed to set the returned id property to entity", e); } if (entity instanceof DirtyMarker) { ((DirtyMarker) entity).dirtyPropNames().clear(); } } } return (T) result; } protected Object executeInsert(final NamedSQL namedSQL, final PreparedStatement stmt) throws SQLException { if (_isReadOnly) { throw new AbacusException("This SQL Executor is configured for read-only"); } stmt.executeUpdate(); Object id = null; ResultSet rs = null; try { rs = stmt.getGeneratedKeys(); if (rs.next()) { id = rs.getObject(1); } } catch (SQLException e) { logger.error("Failed to retrieve the auto-generated Ids", e); } finally { closeQuietly(rs); } return id; } List batchInsert(final String sql, final Object[] parametersList) { return batchInsert(null, sql, null, null, parametersList); } List batchInsert(final String sql, final StatementSetter statementSetter, final Object[] parametersList) { return batchInsert(null, sql, statementSetter, null, parametersList); } List batchInsert(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] parametersList) { return batchInsert(null, sql, statementSetter, jdbcSettings, parametersList); } List batchInsert(final Connection conn, final String sql, final Object[] parametersList) { return batchInsert(conn, sql, null, null, parametersList); } List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final Object[] parametersList) { return batchInsert(conn, sql, statementSetter, null, parametersList); } /** * Returns the auto-generated key by preparing statement * {@code prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)} {@code null} is returned if no auto-generated key. * * Call {@code update} instead if there is no auto-generated key. * * @param conn * @param sql * @param statementSetter * @param props * @param parametersList * @return */ List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] parametersList) { return batchInsert(conn, sql, statementSetter, jdbcSettings, Arrays.asList(parametersList)); } public List batchInsert(final String sql, final List parametersList) { return batchInsert(null, sql, null, null, parametersList); } public List batchInsert(final String sql, final StatementSetter statementSetter, final List parametersList) { return batchInsert(null, sql, statementSetter, null, parametersList); } public List batchInsert(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final List parametersList) { return batchInsert(null, sql, statementSetter, jdbcSettings, parametersList); } public List batchInsert(final Connection conn, final String sql, final List parametersList) { return batchInsert(conn, sql, null, null, parametersList); } public List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final List parametersList) { return batchInsert(conn, sql, statementSetter, null, parametersList); } /** * * @see #batchInsert(Connection, String, StatementSetter, JdbcSettings, Object[]) */ @SuppressWarnings("deprecation") public List batchInsert(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final List parametersList) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); String idPropName = checkGeneratedIdPropName(jdbcSettings); final int len = parametersList.size(); final int batchSize = getBatchSize(jdbcSettings); List resultIdList = new ArrayList<>(len); DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; int isolationLevel = 0; boolean autoCommit = true; try { ds = getDataSource(namedSQL.getPureSQL(), parametersList, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; try { isolationLevel = localConn.getTransactionIsolation(); autoCommit = localConn.getAutoCommit(); } catch (SQLException e) { closeQuietly(null, localConn, conn); throw new UncheckedSQLException(e, namedSQL.toString()); } if ((conn == null) && (len > batchSize)) { localConn.setAutoCommit(false); if (jdbcSettings.getIsolationLevel() == null || jdbcSettings.getIsolationLevel() == IsolationLevel.DEFAULT) { // ignore. by default } else { localConn.setTransactionIsolation(jdbcSettings.getIsolationLevel().intValue()); } } stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.RETURN_GENERATED_KEYS, true, parametersList); if (len <= batchSize) { for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, parametersList.get(i)); stmt.addBatch(); } executeBatchInsert(resultIdList, namedSQL, stmt); } else { int num = 0; for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, parametersList.get(i)); stmt.addBatch(); num++; if ((num % batchSize) == 0) { executeBatchInsert(resultIdList, namedSQL, stmt); } } if ((num % batchSize) > 0) { executeBatchInsert(resultIdList, namedSQL, stmt); } } if ((conn == null) && (len > batchSize)) { localConn.commit(); } } catch (SQLException e) { if ((conn == null) && (len > batchSize)) { if (logger.isWarnEnabled()) { logger.warn("Trying to roll back ..."); } try { localConn.rollback(); if (logger.isWarnEnabled()) { logger.warn("succeeded to roll back"); } } catch (SQLException e1) { logger.error("Failed to roll back", e1); } } String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { if ((conn == null) && (len > batchSize)) { try { localConn.setAutoCommit(autoCommit); localConn.setTransactionIsolation(isolationLevel); } catch (SQLException e) { logger.error("Failed to reset AutoCommit", e); } } closeQuietly(stmt, localConn, conn); } if (N.notNullOrEmpty(resultIdList)) { if (isEntityOrMapParameter(namedSQL, parametersList.get(0))) { if (resultIdList.size() == len) { boolean isTypedParameter = parametersList.get(0) instanceof TypedParameters; Object parameter_0 = isTypedParameter ? ((TypedParameters) parametersList.get(0)).parameters[0] : parametersList.get(0); if (parameter_0 instanceof Map) { // // don't update input map ? // Map m = null; // Object idPropValue = null; // // for (int i = 0; i < parameters.length; i++) { // m = (Map) (isTypedParameter // ? ((TypedParameters) parameters[i]).parameters[0] : // parameters[i]); // idPropValue = m.get(generatedIdPropName); // // if ((idPropValue == null) // || (idPropValue instanceof Number // && (((Number) idPropValue).longValue() == 0))) { // m.put(generatedIdPropName, resultList.get(i)); // } // } } else { try { Method idGetMethod = ClassUtil.getPropGetMethod(parameter_0.getClass(), idPropName); Method idSetMethod = ClassUtil.getPropSetMethod(parameter_0.getClass(), idPropName); if ((idGetMethod != null) && (idSetMethod != null)) { Object entity = null; Object idPropValue = null; for (int i = 0; i < len; i++) { entity = (isTypedParameter ? ((TypedParameters) parametersList.get(i)).parameters[0] : parametersList.get(i)); idPropValue = ClassUtil.invokeMethod(entity, idGetMethod); if ((idPropValue == null) || (idPropValue instanceof Number && (((Number) idPropValue).longValue() == 0))) { ClassUtil.setPropValue(entity, idSetMethod, resultIdList.get(i)); } if (entity instanceof DirtyMarker) { ((DirtyMarker) entity).dirtyPropNames().clear(); } } } else { if (logger.isWarnEnabled()) { logger.warn( "Failed to set the returned id property to entity. no get/set method for id property (" + idPropName + ") found. "); } } } catch (Exception e) { logger.error("Failed to set the returned id property to entity", e); } } } else { if (logger.isWarnEnabled()) { logger.warn( "Failed to set the returned id property to entity/map. because the size of returned key not equals the lenght of the input arrray"); } } } } return resultIdList; } protected void executeBatchInsert(final List resultIdList, final NamedSQL namedSQL, final PreparedStatement stmt) throws SQLException { if (_isReadOnly) { throw new AbacusException("This SQL Executor is configured for read-only"); } stmt.executeBatch(); ResultSet rs = null; try { rs = stmt.getGeneratedKeys(); while (rs.next()) { resultIdList.add((T) rs.getObject(1)); } } catch (SQLException e) { logger.error("Failed to retrieve the auto-generated Ids", e); } finally { closeQuietly(rs); } stmt.clearBatch(); } @SafeVarargs public final int update(final String sql, final Object... parameters) { return update(null, sql, null, null, parameters); } @SafeVarargs public final int update(final String sql, final StatementSetter statementSetter, final Object... parameters) { return update(null, sql, statementSetter, null, parameters); } @SafeVarargs public final int update(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return update(null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final int update(final Connection conn, final String sql, final Object... parameters) { return update(conn, sql, null, null, parameters); } @SafeVarargs public final int update(final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return update(conn, sql, statementSetter, null, parameters); } /** * @see #batchUpdate(Connection, String, StatementSetter, JdbcSettings, Object[]) */ @SafeVarargs public final int update(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; try { ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, false, parameters); return executeUpdate(namedSQL, stmt); } catch (SQLException e) { String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { closeQuietly(stmt, localConn, conn); } } protected int executeUpdate(final NamedSQL namedSQL, final PreparedStatement stmt) throws SQLException { if (_isReadOnly) { throw new AbacusException("This SQL Executor is configured for read-only"); } return stmt.executeUpdate(); } int batchUpdate(final String sql, final Object[] parametersList) { return batchUpdate(null, sql, null, null, parametersList); } int batchUpdate(final String sql, final StatementSetter statementSetter, final Object[] parametersList) { return batchUpdate(null, sql, statementSetter, null, parametersList); } int batchUpdate(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] parametersList) { return batchUpdate(null, sql, statementSetter, jdbcSettings, parametersList); } int batchUpdate(final Connection conn, final String sql, final Object[] parametersList) { return batchUpdate(conn, sql, null, null, parametersList); } int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final Object[] parametersList) { return batchUpdate(conn, sql, statementSetter, null, parametersList); } /** * batch insert/update/delete sql scripts are supported * * @param conn * @param sql * @param statementSetter * @param props * @param parametersList * @return */ int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] parametersList) { return batchUpdate(conn, sql, statementSetter, jdbcSettings, Arrays.asList(parametersList)); } public int batchUpdate(final String sql, final List parametersList) { return batchUpdate(null, sql, null, null, parametersList); } public int batchUpdate(final String sql, final StatementSetter statementSetter, final List parametersList) { return batchUpdate(null, sql, statementSetter, null, parametersList); } public int batchUpdate(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final List parametersList) { return batchUpdate(null, sql, statementSetter, jdbcSettings, parametersList); } public int batchUpdate(final Connection conn, final String sql, final List parametersList) { return batchUpdate(conn, sql, null, null, parametersList); } public int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final List parametersList) { return batchUpdate(conn, sql, statementSetter, null, parametersList); } /** * @see #batchUpdate(Connection, String, StatementSetter, JdbcSettings, Object[]) */ public int batchUpdate(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final List parametersList) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); final int len = parametersList.size(); final int batchSize = getBatchSize(jdbcSettings); DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; int isolationLevel = 0; boolean autoCommit = true; try { ds = getDataSource(namedSQL.getPureSQL(), parametersList, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; try { isolationLevel = localConn.getTransactionIsolation(); autoCommit = localConn.getAutoCommit(); } catch (SQLException e) { closeQuietly(null, localConn, conn); throw new UncheckedSQLException(e, namedSQL.toString()); } if ((conn == null) && (len > batchSize)) { localConn.setAutoCommit(false); if (jdbcSettings.getIsolationLevel() == null || jdbcSettings.getIsolationLevel() == IsolationLevel.DEFAULT) { // ignore. by default } else { localConn.setTransactionIsolation(jdbcSettings.getIsolationLevel().intValue()); } } stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, true, parametersList); int result = 0; if (len <= batchSize) { for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, parametersList.get(i)); stmt.addBatch(); } result += executeBatchUpdate(namedSQL, stmt); } else { int num = 0; for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, parametersList.get(i)); stmt.addBatch(); num++; if ((num % batchSize) == 0) { result += executeBatchUpdate(namedSQL, stmt); } } if ((num % batchSize) > 0) { result += executeBatchUpdate(namedSQL, stmt); } } if ((conn == null) && (len > batchSize)) { localConn.commit(); } return result; } catch (SQLException e) { if ((conn == null) && (len > batchSize)) { if (logger.isWarnEnabled()) { logger.warn("Trying to roll back ..."); } try { localConn.rollback(); if (logger.isWarnEnabled()) { logger.warn("succeeded to roll back"); } } catch (SQLException e1) { logger.error("Failed to roll back", e1); } } String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { if ((conn == null) && (len > batchSize)) { try { localConn.setAutoCommit(autoCommit); localConn.setTransactionIsolation(isolationLevel); } catch (SQLException e) { logger.error("Failed to reset AutoCommit", e); } } closeQuietly(stmt, localConn, conn); } } protected int executeBatchUpdate(final NamedSQL namedSQL, final PreparedStatement stmt) throws SQLException { if (_isReadOnly) { throw new AbacusException("This SQL Executor is configured for read-only"); } final int[] results = stmt.executeBatch(); stmt.clearBatch(); if ((results == null) || (results.length == 0)) { return 0; } int sum = 0; for (int i = 0; i < results.length; i++) { sum += results[i]; } return sum; } // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // int update(final EntityId entityId, final Map props) { // return update(null, entityId, props); // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // int update(final Connection conn, final EntityId entityId, final Map props) { // final Pair2 pair = generateUpdateSQL(entityId, props); // // return update(conn, pair.sql, pair.parameters); // } // // private Pair2 generateUpdateSQL(final EntityId entityId, final Map props) { // final Condition cond = EntityManagerUtil.entityId2Condition(entityId); // final NamingPolicy namingPolicy = _jdbcSettings.getNamingPolicy(); // // if (namingPolicy == null) { // return NE.update(entityId.entityName()).set(props).where(cond).pair(); // } // // switch (namingPolicy) { // case LOWER_CASE_WITH_UNDERSCORE: { // return NE.update(entityId.entityName()).set(props).where(cond).pair(); // } // // case UPPER_CASE_WITH_UNDERSCORE: { // return NE2.update(entityId.entityName()).set(props).where(cond).pair(); // } // // case CAMEL_CASE: { // return NE3.update(entityId.entityName()).set(props).where(cond).pair(); // } // // default: // throw new AbacusException("Unsupported naming policy"); // } // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // int delete(final EntityId entityId) { // return delete(null, entityId); // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // int delete(final Connection conn, final EntityId entityId) { // final Pair2 pair = generateDeleteSQL(entityId); // // return update(conn, pair.sql, pair.parameters); // } // // private Pair2 generateDeleteSQL(final EntityId entityId) { // final Condition cond = EntityManagerUtil.entityId2Condition(entityId); // final NamingPolicy namingPolicy = _jdbcSettings.getNamingPolicy(); // // if (namingPolicy == null) { // return NE.deleteFrom(entityId.entityName()).where(cond).pair(); // } // // switch (namingPolicy) { // case LOWER_CASE_WITH_UNDERSCORE: { // return NE.deleteFrom(entityId.entityName()).where(cond).pair(); // } // // case UPPER_CASE_WITH_UNDERSCORE: { // return NE2.deleteFrom(entityId.entityName()).where(cond).pair(); // } // // case CAMEL_CASE: { // return NE3.deleteFrom(entityId.entityName()).where(cond).pair(); // } // // default: // throw new AbacusException("Unsupported naming policy"); // } // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // boolean exists(final EntityId entityId) { // return exists(null, entityId); // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // boolean exists(final Connection conn, final EntityId entityId) { // final Pair2 pair = generateQuerySQL(entityId, NE._1_list); // // return query(conn, pair.sql, null, EXISTS_RESULT_SET_EXTRACTOR, null, pair.parameters); // } @SafeVarargs public final boolean exists(final String sql, final Object... parameters) { return exists(null, sql, parameters); } @SafeVarargs public final boolean exists(final Connection conn, final String sql, final Object... parameters) { return query(conn, sql, null, EXISTS_RESULT_SET_EXTRACTOR, null, parameters); } @SafeVarargs public final int count(final String sql, final Object... parameters) { return count(null, sql, parameters); } @SafeVarargs public final int count(final Connection conn, final String sql, final Object... parameters) { return queryForSingleResult(int.class, conn, sql, parameters).orElse(0); } // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // T get(final Class targetClass, final EntityId entityId, final String... selectPropNames) { // return get(targetClass, entityId, N.asList(selectPropNames)); // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // T get(final Class targetClass, final EntityId entityId, final Collection selectPropNames) { // return get(targetClass, null, entityId, selectPropNames); // } // // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // T get(final Class targetClass, final Connection conn, final EntityId entityId, final String... selectPropNames) { // return get(targetClass, conn, entityId, N.asList(selectPropNames)); // } // // /** // * // * @param targetClass // * @param conn // * @param entityId // * @param selectPropNames // * @return NonUniqueResultException if more than one records are found. // */ // // mess up. To uncomment this method, also need to modify getNamingPolicy/setNamingPolicy in JdbcSettings. // T get(final Class targetClass, final Connection conn, final EntityId entityId, final Collection selectPropNames) { // final Pair2 pair = generateQuerySQL(entityId, selectPropNames); // final List entities = find(targetClass, conn, pair.sql, pair.parameters); // // if (entities.size() > 1) { // throw new NonUniqueResultException("More than one records found by EntityId: " + entityId.toString()); // } // // return (entities.size() > 0) ? entities.get(0) : null; // } // // private Pair2 generateQuerySQL(final EntityId entityId, final Collection selectPropNames) { // final Condition cond = EntityManagerUtil.entityId2Condition(entityId); // final NamingPolicy namingPolicy = _jdbcSettings.getNamingPolicy(); // // if (namingPolicy == null) { // return NE.select(selectPropNames).from(entityId.entityName()).where(cond).limit(2).pair(); // } // // switch (namingPolicy) { // case LOWER_CASE_WITH_UNDERSCORE: { // return NE.select(selectPropNames).from(entityId.entityName()).where(cond).limit(2).pair(); // } // // case UPPER_CASE_WITH_UNDERSCORE: { // return NE2.select(selectPropNames).from(entityId.entityName()).where(cond).limit(2).pair(); // } // // case CAMEL_CASE: { // return NE3.select(selectPropNames).from(entityId.entityName()).where(cond).limit(2).pair(); // } // // default: // throw new AbacusException("Unsupported naming policy"); // } // } @SafeVarargs public final T get(final Class targetClass, final String sql, final Object... parameters) { return get(targetClass, sql, null, null, parameters); } @SafeVarargs public final T get(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return get(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final T get(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return get(targetClass, conn, sql, null, null, parameters); } /** * * @param targetClass * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return * @throws NonUniqueResultException if two or more records are found. */ @SuppressWarnings("unchecked") @SafeVarargs public final T get(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { jdbcSettings = jdbcSettings == null ? _jdbcSettings.copy() : jdbcSettings.copy(); jdbcSettings.setCount(2); final List entities = find(targetClass, conn, sql, statementSetter, jdbcSettings, parameters); if (entities.size() > 1) { throw new NonUniqueResultException("More than one records found by sql: " + sql); } return (entities.size() > 0) ? entities.get(0) : null; } @SafeVarargs public final Optional gett(final Class targetClass, final String sql, final Object... parameters) { return Optional.ofNullable(this.get(targetClass, sql, parameters)); } @SafeVarargs public final Optional gett(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return Optional.ofNullable(this.get(targetClass, sql, statementSetter, jdbcSettings, parameters)); } @SafeVarargs public final Optional gett(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return Optional.ofNullable(this.get(targetClass, conn, sql, parameters)); } @SafeVarargs public final Optional gett(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { return Optional.ofNullable(this.get(targetClass, conn, sql, statementSetter, jdbcSettings, parameters)); } @SafeVarargs public final List find(final Class targetClass, final String sql, final Object... parameters) { return find(targetClass, sql, null, null, parameters); } @SafeVarargs public final List find(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return find(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final List find(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return find(targetClass, conn, sql, null, null, parameters); } /** * * @param targetClass * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SuppressWarnings("unchecked") @SafeVarargs public final List find(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return (List) query(targetClass, conn, sql, statementSetter, ENTITY_LIST_RESULT_SET_EXTRACTOR, jdbcSettings, parameters); } @SafeVarargs public final List findAll(final Class targetClass, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, sql, null, jdbcSettings, parameters); } @SafeVarargs public final List findAll(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } List findAll(final Class targetClass, final Connection conn, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, conn, sql, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql in parallel. Mostly it's designed * for partition to query the partitioning table in more than one databases. * * @param targetClass * @param conn * @param sql * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ List findAll(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(conn, sql, statementSetter, jdbcSettings, parameters).toList(targetClass); } @SafeVarargs public final List findAll(final Class targetClass, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, sqls, null, jdbcSettings, parameters); } @SafeVarargs public final List findAll(final Class targetClass, final List sqls, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, null, sqls, statementSetter, jdbcSettings, parameters); } List findAll(final Class targetClass, final Connection conn, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, conn, sqls, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql list in parallel. Mostly it's designed * for partition to query multiple partitioning tables in one or more databases. * * @param targetClass * @param conn * @param sqls * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ List findAll(final Class targetClass, final Connection conn, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(conn, sqls, statementSetter, jdbcSettings, parameters).toList(targetClass); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalBoolean queryForBoolean(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(boolean.class, null, sql, parameters); return result.isPresent() ? OptionalBoolean.of(result.get()) : OptionalBoolean.empty(); } /** * * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalChar queryForChar(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(char.class, null, sql, parameters); return result.isPresent() ? OptionalChar.of(result.get()) : OptionalChar.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalByte queryForByte(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(byte.class, null, sql, parameters); return result.isPresent() ? OptionalByte.of(result.get()) : OptionalByte.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalShort queryForShort(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(short.class, null, sql, parameters); return result.isPresent() ? OptionalShort.of(result.get()) : OptionalShort.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalInt queryForInt(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(int.class, null, sql, parameters); return result.isPresent() ? OptionalInt.of(result.get()) : OptionalInt.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalLong queryForLong(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(long.class, null, sql, parameters); return result.isPresent() ? OptionalLong.of(result.get()) : OptionalLong.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalFloat queryForFloat(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(float.class, null, sql, parameters); return result.isPresent() ? OptionalFloat.of(result.get()) : OptionalFloat.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final OptionalDouble queryForDouble(final String sql, final Object... parameters) { final Nullable result = queryForSingleResult(double.class, null, sql, parameters); return result.isPresent() ? OptionalDouble.of(result.get()) : OptionalDouble.empty(); } /** * @see SQLExecutor#queryForSingleResult(Class, Connection, String, Object...). */ @SafeVarargs public final Nullable queryForString(final String sql, final Object... parameters) { return queryForSingleResult(String.class, null, sql, parameters); } /** * @see SQLExecutor#queryForSingleResult(Class, String, Object...). */ @SafeVarargs public final Nullable queryForDate(final String sql, final Object... parameters) { return queryForSingleResult(Date.class, sql, parameters); } /** * @see SQLExecutor#queryForSingleResult(Class, String, Object...). */ @SafeVarargs public final Nullable queryForDate(final Class targetClass, final String sql, final Object... parameters) { // final Nullable date = this.queryForDate(sql, parameters); // // if (date.isNotNull()) { // if (targetClass.isAssignableFrom(date.get().getClass())) { // return (Nullable) date; // } else if (targetClass.equals(Timestamp.class)) { // return (Nullable) Nullable.of((new Timestamp(date.get().getTime()))); // } else if (targetClass.equals(Time.class)) { // return (Nullable) Nullable.of((new Time(date.get().getTime()))); // } else if (targetClass.equals(java.sql.Date.class)) { // return (Nullable) Nullable.of((new java.sql.Date(date.get().getTime()))); // } else { // return Nullable.of(N.as(targetClass, date.get())); // } // } else { // return (Nullable) date; // } return queryForSingleResult(targetClass, sql, parameters); } @SafeVarargs public final Nullable queryForSingleResult(final Class targetClass, final String sql, final Object... parameters) { return queryForSingleResult(targetClass, sql, null, null, parameters); } @SafeVarargs public final Nullable queryForSingleResult(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return queryForSingleResult(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final Nullable queryForSingleResult(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return queryForSingleResult(targetClass, conn, sql, null, null, parameters); } /** * Just fetch the result in the 1st row and 1st column. {@code null} is returned if no result is found. And this * method will try to convert the result to the specified {@code targetClass} if {@code targetClass} is not null and it's not * assignable from the result. * * Special note for type conversion for {@code boolean} or {@code Boolean} type: {@code true} is returned if the * {@code String} value of the target column is {@code "true"}, case insensitive. or it's an integer with value > 0. * Otherwise, {@code false} is returned. * * Remember to add {@code limit} condition if big result will be returned by the query. * * @param targetClass * set result type to avoid the NullPointerException if result is null and T is primitive type * "int, long. short ... char, boolean..". * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @throws ClassCastException */ @SuppressWarnings("unchecked") @SafeVarargs public final Nullable queryForSingleResult(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { final Nullable result = query(conn, sql, statementSetter, SINGLE_RESULT_SET_EXTRACTOR, jdbcSettings, parameters); return result.isPresent() ? Nullable.of(N.as(targetClass, result.get())) : (Nullable) Nullable.empty(); } // // public Map queryForMap(String sql, Object... parameters) { // return queryForMap(sql, null, parameters); // } // // public Map queryForMap(String sql, StatementSetter statementSetter, Object... parameters) { // return queryForMap(null, sql, statementSetter, parameters); // } // // public Map queryForMap(final Connection conn, final String sql, Object... parameters) { // return queryForMap(conn, sql, null, parameters); // } // // /** // * Just fetch the result in the 1st row. {@code null} is returned if no result is found. // * // * Remember to add {@code limit} condition if big result will be returned by the query. // * // * @param conn // * @param sql // * @param statementSetter // * @param parameters // * @return // */ // public Map queryForMap(final Connection conn, final String sql, StatementSetter statementSetter, Object... parameters) { // return query(conn, sql, statementSetter, MAP_RESULT_SET_EXTRACTOR, null, parameters); // } @SafeVarargs public final Optional queryForEntity(final Class targetClass, final String sql, final Object... parameters) { return queryForEntity(targetClass, sql, null, null, parameters); } @SafeVarargs public final Optional queryForEntity(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return queryForEntity(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final Optional queryForEntity(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return queryForEntity(targetClass, conn, sql, null, null, parameters); } /** * Just fetch the result in the 1st row. {@code null} is returned if no result is found. This method will try to * convert the column value to the type of mapping entity property if the mapping entity property is not assignable * from column value. * * Remember to add {@code limit} condition if big result will be returned by the query. * * @param targetClass * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SuppressWarnings("unchecked") @SafeVarargs public final Optional queryForEntity(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { final T result = (T) query(targetClass, conn, sql, statementSetter, ENTITY_RESULT_SET_EXTRACTOR, jdbcSettings, parameters); return result == null ? (Optional) Optional.empty() : Optional.of(result); } /** * Query single column. * * @param sql * @param parameters * @return */ @SafeVarargs public final List queryForList(final String sql, final Object... parameters) { return queryForList(sql, null, null, parameters); } /** * Query single column. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final List queryForList(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return queryForList(null, sql, statementSetter, jdbcSettings, parameters); } /** * Query single column. * * @param conn * @param sql * @param parameters * @return */ @SafeVarargs public final List queryForList(final Connection conn, final String sql, final Object... parameters) { return queryForList(conn, sql, null, null, parameters); } /** * Query single column. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @throws ClassCastException */ @SuppressWarnings({ "unchecked", "rawtypes" }) @SafeVarargs public final List queryForList(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { final List result = query(conn, sql, statementSetter, LIST_RESULT_SET_EXTRACTOR, jdbcSettings, parameters); return (List) result; } @SafeVarargs public final DataSet query(final String sql, final Object... parameters) { return query(sql, null, parameters); } @SafeVarargs public final DataSet query(final String sql, final StatementSetter statementSetter, final Object... parameters) { return query(sql, statementSetter, null, parameters); } // // public T query(String sql, ResultSetExtractor resultSetExtractor, // Object... parameters) { // return query(null, sql, null, resultSetExtractor, null, parameters); // } // @SafeVarargs public final T query(final String sql, final StatementSetter statementSetter, final ResultSetExtractor resultSetExtractor, final Object... parameters) { return query(sql, statementSetter, resultSetExtractor, null, parameters); } /** * Remember to close the ResultSet, Statement and Connection if the return type T is ResultSet or RowIterator. * * If T is RowIterator, call rowIterator.close() to close ResultSet, Statement and Connection. *

    * If T is ResultSet, call below codes to close ResultSet, Statement and Connection. * *
         * 
         * Connection conn = null;
         * Statement stmt = null;
         * 
         * try {
         *     stmt = rs.getStatement();
         *     conn = stmt.getConnection();
         * } catch (SQLException e) {
         *     // TODO.
         * } finally {
         *     JdbcUtil.closeQuietly(rs, stmt, conn);
         * }
         * 
         * 
    * * @param sql * @param statementSetter * @param resultSetExtractor * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final T query(final String sql, final StatementSetter statementSetter, final ResultSetExtractor resultSetExtractor, final JdbcSettings jdbcSettings, final Object... parameters) { return query(null, sql, statementSetter, resultSetExtractor, jdbcSettings, parameters); } @SafeVarargs public final DataSet query(final Connection conn, final String sql, final Object... parameters) { return query(conn, sql, null, parameters); } @SafeVarargs public final DataSet query(final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return query(conn, sql, statementSetter, null, parameters); } @SafeVarargs public final T query(final Connection conn, final String sql, final StatementSetter statementSetter, final ResultSetExtractor resultSetExtractor, final Object... parameters) { return query(conn, sql, statementSetter, resultSetExtractor, null, parameters); } /** * Remember to close the ResultSet, Statement and Connection if the return type T is ResultSet or RowIterator. * * If T is RowIterator, call rowIterator.close() to close ResultSet, Statement and Connection. *

    * If T is ResultSet, call below codes to close ResultSet, Statement and Connection. * *
         * 
         * Connection conn = null;
         * Statement stmt = null;
         * 
         * try {
         *     stmt = rs.getStatement();
         *     conn = stmt.getConnection();
         * } catch (SQLException e) {
         *     // TODO.
         * } finally {
         *     JdbcUtil.closeQuietly(rs, stmt, conn);
         * }
         * 
         * 
    * * @param conn * @param sql * @param statementSetter * @param resultSetExtractor * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final T query(final Connection conn, final String sql, final StatementSetter statementSetter, final ResultSetExtractor resultSetExtractor, final JdbcSettings jdbcSettings, final Object... parameters) { return query(null, conn, sql, statementSetter, resultSetExtractor, jdbcSettings, parameters); } protected T query(final Class targetClass, final Connection conn, final String sql, StatementSetter statementSetter, ResultSetExtractor resultSetExtractor, JdbcSettings jdbcSettings, final Object... parameters) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); resultSetExtractor = checkResultSetExtractor(namedSQL, resultSetExtractor); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); T result = null; DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; ResultSet rs = null; try { ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, false, parameters); rs = stmt.executeQuery(); result = resultSetExtractor.extractData(targetClass, namedSQL, rs, jdbcSettings); } catch (SQLException e) { String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { if (result instanceof ResultSet || result instanceof RowIterator) { // delay. } else { closeQuietly(rs, stmt, localConn, conn); } } return result; } @SafeVarargs public final DataSet queryAll(final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(sql, null, jdbcSettings, parameters); } @SafeVarargs public final DataSet queryAll(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(null, sql, statementSetter, jdbcSettings, parameters); } DataSet queryAll(final Connection conn, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(conn, sql, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql in parallel. Mostly it's designed * for partition to query the partitioning table in more than one databases. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ DataSet queryAll(final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } if (N.isNullOrEmpty(jdbcSettings.getQueryWithDataSources())) { return query(conn, sql, statementSetter, jdbcSettings, parameters); } else { final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List columnNames = new ArrayList<>(); try (final Stream s = streamAlll(columnNames, sql, statementSetter, newJdbcSettings, parameters)) { return toDataSet(columnNames, skipAndLimit(s, jdbcSettings)); } } } @SafeVarargs public final DataSet queryAll(final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(sqls, null, jdbcSettings, parameters); } @SafeVarargs public final DataSet queryAll(final List sqls, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(null, sqls, statementSetter, jdbcSettings, parameters); } DataSet queryAll(final Connection conn, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return queryAll(conn, sqls, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql list in parallel. Mostly it's designed * for partition to query multiple partitioning tables in one or more databases. * * @param conn * @param sqls * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ DataSet queryAll(final Connection conn, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); N.checkArgument(N.notNullOrEmpty(sqls), "'sqls' can't be null or empty"); if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List columnNames = new ArrayList<>(); try (final Stream s = (newJdbcSettings.isQueryInParallel() ? Stream.of(sqls).parallel(sqls.size()) : Stream.of(sqls)) .flatMap(new Function>() { @Override public Stream apply(String sql) { return streamAlll(columnNames, sql, statementSetter, newJdbcSettings, parameters); } })) { return toDataSet(columnNames, skipAndLimit(s, jdbcSettings)); } } private RowIterator iterate(final String sql, final StatementSetter statementSetter, final JdbcSettings newJdbcSettings, final Object... parameters) { return query(sql, statementSetter, ROW_ITERATOR_RESULT_SET_EXTRACTOR, newJdbcSettings, parameters); } private Stream streamAlll(final List columnNames, final String sql, final StatementSetter statementSetter, final JdbcSettings newJdbcSettings, final Object... parameters) { final Collection dataSources = N.isNullOrEmpty(newJdbcSettings.getQueryWithDataSources()) ? N.asList(newJdbcSettings.getQueryWithDataSource()) : newJdbcSettings.getQueryWithDataSources(); return (newJdbcSettings.isQueryInParallel() ? Stream.of(dataSources).parallel(dataSources.size()) : Stream.of(dataSources)) .flatMap(new Function>() { @Override public Stream apply(String t) { final RowIterator iter = iterate(sql, statementSetter, newJdbcSettings.copy().setQueryWithDataSource(t), parameters); synchronized (columnNames) { if (columnNames.size() == 0) { try { columnNames.addAll(getColumnLabelList(sql, iter.resultSet())); } catch (SQLException e) { IOUtil.closeQuietly(iter); throw new UncheckedSQLException(e); } } } return Stream.of(iter).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iter); } }); } }); } private Stream skipAndLimit(Stream s, JdbcSettings jdbcSettings) { if (jdbcSettings.getOffset() > 0) { s = s.skip(jdbcSettings.getOffset()); } if (jdbcSettings.getCount() < Long.MAX_VALUE) { s = s.limit(jdbcSettings.getCount()); } return s; } private DataSet toDataSet(final List columnNames, final Stream s) { final List columnNameList = new ArrayList<>(); final List> columnList = new ArrayList<>(); final MutableInt columnCount = MutableInt.of(0); s.sequential().forEach(new Consumer() { @Override public void accept(Object[] a) { if (columnCount.value() == 0) { columnCount.setValue(columnNames.size()); columnNameList.addAll(columnNames); for (int i = 0, len = columnCount.value(); i < len; i++) { columnList.add(new ArrayList<>()); } } for (int i = 0, len = columnCount.value(); i < len; i++) { columnList.get(i).add(a[i]); } } }); return new RowDataSet(columnNameList, columnList); } @SafeVarargs public final Try> stream(final String sql, final Object... parameters) { return stream(sql, null, parameters); } @SafeVarargs public final Try> stream(final String sql, final StatementSetter statementSetter, final Object... parameters) { return stream(sql, statementSetter, null, parameters); } /** * Remember to close the returned Stream to close the underlying ResultSet. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> stream(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return stream((Connection) null, sql, statementSetter, jdbcSettings, parameters); } Try> stream(final Connection conn, final String sql, final Object... parameters) { return stream(conn, sql, null, parameters); } Try> stream(final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return stream(conn, sql, statementSetter, null, parameters); } /** * Remember to close the returned Stream to close the underlying ResultSet. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ Try> stream(final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); return stream2(null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final Try> stream(final Class targetClass, final String sql, final Object... parameters) { return stream(targetClass, sql, null, parameters); } @SafeVarargs public final Try> stream(final Class targetClass, final String sql, final StatementSetter statementSetter, final Object... parameters) { return stream(targetClass, sql, statementSetter, null, parameters); } /** * Remember to close the returned Stream to close the underlying ResultSet. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> stream(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return stream(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } Try> stream(final Class targetClass, final Connection conn, final String sql, final Object... parameters) { return stream(targetClass, conn, sql, null, parameters); } Try> stream(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return stream(targetClass, conn, sql, statementSetter, null, parameters); } /** * Remember to close the returned Stream to close the underlying ResultSet. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ Try> stream(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); N.checkArgNotNull(targetClass); return stream2(targetClass, sql, statementSetter, jdbcSettings, parameters); } private Try> stream2(final Class targetClass, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final RowIterator iterator = this.iterate(sql, statementSetter, newJdbcSettings, parameters); final Stream s = skipAndLimit(Stream.of(iterator), jdbcSettings); return (targetClass == null ? (Stream) s : s.map(newMapper(targetClass, sql, iterator))).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterator); } }).tried(); } private Function newMapper(final Class targetClass, final String sql, final RowIterator iterator) { try { final NamedSQL namedSQL = getNamedSQL(sql); final ResultSet rs = iterator.resultSet(); final String[] columnLabels = getColumnLabelList(namedSQL.getPureSQL(), rs).toArray(new String[0]); final int columnCount = columnLabels.length; final boolean isMap = Map.class.isAssignableFrom(targetClass); final boolean isDirtyMarker = N.isDirtyMarker(targetClass); return new Function() { @SuppressWarnings("deprecation") @Override public T apply(Object[] a) { if (isMap) { final Map m = (Map) N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { m.put(columnLabels[i], a[i]); } return (T) m; } else { final Object entity = N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { if (columnLabels[i] == null) { continue; } if (ClassUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } return (T) entity; } } }; } catch (SQLException e) { IOUtil.closeQuietly(iterator); throw new UncheckedSQLException(e); } } @SafeVarargs public final Try> streamAll(final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(sql, null, jdbcSettings, parameters); } /** * Remember to close the returned Stream list to close the underlying ResultSet list. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> streamAll(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll((Connection) null, sql, statementSetter, jdbcSettings, parameters); } Try> streamAll(final Connection conn, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(conn, sql, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql in parallel. Mostly it's designed * for partition to query the partitioning table in more than one databases. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ Try> streamAll(final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); return streamAll2(null, sql, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final Try> streamAll(final Class targetClass, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, sql, null, jdbcSettings, parameters); } /** * Remember to close the returned Stream list to close the underlying ResultSet list. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> streamAll(final Class targetClass, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, null, sql, statementSetter, jdbcSettings, parameters); } Try> streamAll(final Class targetClass, final Connection conn, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, conn, sql, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql in parallel. Mostly it's designed * for partition to query the partitioning table in more than one databases. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ Try> streamAll(final Class targetClass, final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); N.checkArgNotNull(targetClass); return streamAll2(targetClass, sql, statementSetter, jdbcSettings, parameters); } private Try> streamAll2(final Class targetClass, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } if (N.isNullOrEmpty(jdbcSettings.getQueryWithDataSources())) { return stream2(targetClass, sql, statementSetter, jdbcSettings, parameters); } else { final Collection dataSources = jdbcSettings.getQueryWithDataSources(); final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE).setQueryWithDataSources(null); final Stream s = (newJdbcSettings.isQueryInParallel() ? Stream.of(dataSources).parallel(dataSources.size()) : Stream.of(dataSources)) .flatMap(new Function>() { @Override public Stream apply(String t) { return stream2(targetClass, sql, statementSetter, newJdbcSettings.copy().setQueryWithDataSource(t), parameters).val(); } }); return skipAndLimit(s, jdbcSettings).tried(); } } @SafeVarargs public final Try> streamAll(final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(sqls, null, jdbcSettings, parameters); } /** * Remember to close the returned Stream list to close the underlying ResultSet list. * * @param sqls * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> streamAll(final List sqls, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll((Connection) null, sqls, statementSetter, jdbcSettings, parameters); } Try> streamAll(final Connection conn, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(conn, sqls, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql list in parallel. Mostly it's designed * for partition to query multiple partitioning tables in one or more databases. * * @param conn * @param sqls * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ Try> streamAll(final Connection conn, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); return this.streamAll2(null, sqls, statementSetter, jdbcSettings, parameters); } @SafeVarargs public final Try> streamAll(final Class targetClass, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, sqls, null, jdbcSettings, parameters); } /** * Remember to close the returned Stream list to close the underlying ResultSet list. * * @param sqls * @param statementSetter * @param jdbcSettings * @param parameters * @return */ @SafeVarargs public final Try> streamAll(final Class targetClass, final List sqls, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, null, sqls, statementSetter, jdbcSettings, parameters); } Try> streamAll(final Class targetClass, final Connection conn, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return streamAll(targetClass, conn, sqls, null, jdbcSettings, parameters); } /** * Returns the merged ResultSet acquired by querying with the specified sql list in parallel. Mostly it's designed * for partition to query multiple partitioning tables in one or more databases. * * @param conn * @param sqls * @param statementSetter * @param jdbcSettings set multiple data sources by method: setQueryWithDataSources * @param parameters * @return */ Try> streamAll(final Class targetClass, final Connection conn, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if Stream is closed. that means the specified connection can't be held independently. N.checkArgument(conn == null, "Must not specified connection"); N.checkArgNotNull(targetClass); return streamAll2(targetClass, sqls, statementSetter, jdbcSettings, parameters); } private Try> streamAll2(final Class targetClass, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { N.checkArgument(N.notNullOrEmpty(sqls), "'sqls' can't be null or empty"); if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final Stream s = (newJdbcSettings.isQueryInParallel() ? Stream.of(sqls).parallel(sqls.size()) : Stream.of(sqls)) .flatMap(new Function>() { @Override public Stream apply(String sql) { return streamAll2(targetClass, sql, statementSetter, newJdbcSettings, parameters).val(); } }); return skipAndLimit(s, jdbcSettings).tried(); } /** * Execute the sql with the specified parameters. * * @param sql * @param parameters * * @see java.sql.PreparedStatement#execute() */ @SafeVarargs public final void execute(final String sql, final Object... parameters) { final NamedSQL namedSQL = getNamedSQL(sql); final StatementSetter statementSetter = checkStatementSetter(namedSQL, null); final JdbcSettings jdbcSettings = checkJdbcSettings(null, namedSQL); DataSource ds = null; Connection conn = null; PreparedStatement stmt = null; try { ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); conn = ds.getConnection(); stmt = prepareStatement(ds, conn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, false, parameters); stmt.execute(); } catch (SQLException e) { String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); logger.error(msg); throw new UncheckedSQLException(e, msg); } finally { closeQuietly(stmt, conn); } } // /** // * Execute update by the sql with the specified parameters. // * // * @param sql // * @param parameters // * // * @see java.sql.PreparedStatement#executeUpdate() // */ // public int executeUpdate(final String sql, final Object... parameters) { // final NamedSQL namedSQL = getNamedSQL(sql); // final StatementSetter statementSetter = checkStatementSetter(namedSQL, null); // final JdbcSettings jdbcSettings = checkJdbcSettings(null, namedSQL); // // DataSource ds = null; // Connection conn = null; // PreparedStatement stmt = null; // // try { // ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); // conn = ds.getConnection(); // // stmt = prepareStatement(ds, conn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, false, parameters); // // return stmt.executeUpdate(); // } catch (SQLException e) { // String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); // logger.error(msg); // throw new UncheckedSQLException(e, msg); // } finally { // closeQuietly(stmt, conn); // } // } // // /** // * Execute query by the sql with the specified parameters. // * Don't forget to close the returned ResultSet. // * // * @param sql // * @param parameters // * // * @see java.sql.PreparedStatement#executeUpdate() // */ // public ResultSet executeQuery(final String sql, final Object... parameters) { // final NamedSQL namedSQL = getNamedSQL(sql); // final StatementSetter statementSetter = checkStatementSetter(namedSQL, null); // final JdbcSettings jdbcSettings = checkJdbcSettings(null, namedSQL); // // DataSource ds = null; // Connection conn = null; // PreparedStatement stmt = null; // // try { // ds = getDataSource(namedSQL.getPureSQL(), parameters, jdbcSettings); // conn = ds.getConnection(); // // stmt = prepareStatement(ds, conn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, false, parameters); // // final ResultSet rs = stmt.executeQuery(); // long offset = jdbcSettings.getOffset(); // // while ((offset-- > 0) && rs.next()) { // } // // return rs; // } catch (SQLException e) { // String msg = AbacusException.getErrorMsg(e) + ". [SQL] " + namedSQL.getNamedSQL(); // logger.error(msg); // throw new UncheckedSQLException(e, msg); // } finally { // closeQuietly(stmt, conn); // } // } /** * * The connection opened in the transaction will be automatically closed after the transaction is committed or rolled back. * DON'T close it again by calling the close method. * * @param isolationLevel * @return */ public SQLTransaction beginTransaction(IsolationLevel isolationLevel) { if (isolationLevel == null) { throw new IllegalArgumentException("The parameter isolationLevel can't be null"); } isolationLevel = isolationLevel == IsolationLevel.DEFAULT ? _defaultIsolationLevel : isolationLevel; return new SQLTransaction(getConnection(), isolationLevel); } public DBSequence getDBSequence(final String tableName, final String seqName) { return new DBSequence(this, tableName, seqName, 0, 1000); } /** * Supports global sequence by db table. * * @param tableName * @param seqName * @param startVal * @param seqBufferSize the numbers to allocate/reserve from database table when cached numbers are used up. * @return */ public DBSequence getDBSequence(final String tableName, final String seqName, final long startVal, final int seqBufferSize) { return new DBSequence(this, tableName, seqName, startVal, seqBufferSize); } /** * Supports global lock by db table * * @param tableName * @return */ public DBLock getDBLock(final String tableName) { return new DBLock(this, tableName); } public boolean doesTableExist(final String tableName) { Connection conn = getConnection(); try { return JdbcUtil.doesTableExist(conn, tableName); } finally { closeQuietly(conn); } } /** * Returns {@code true} if succeed to create table, otherwise {@code false} is returned. * * @param tableName * @param schema * @return */ public boolean createTableIfNotExists(final String tableName, final String schema) { Connection conn = getConnection(); try { return JdbcUtil.createTableIfNotExists(conn, tableName, schema); } finally { closeQuietly(conn); } } /** * Returns {@code true} if succeed to drop table, otherwise {@code false} is returned. * * @param tableName * @return */ public boolean dropTableIfExists(final String tableName) { Connection conn = getConnection(); try { return JdbcUtil.dropTableIfExists(conn, tableName); } finally { closeQuietly(conn); } } /** * * @param tableName * @return */ public List getColumnNameList(final String tableName) { List columnNameList = _tableColumnNamePool.get(tableName); if (columnNameList == null) { Connection conn = getConnection(); try { columnNameList = ImmutableList.of(JdbcUtil.getColumnNameList(conn, tableName)); _tableColumnNamePool.put(tableName, columnNameList); } finally { closeQuietly(conn); } } return columnNameList; } public Connection getConnection() { return _ds.getConnection(); } /** * Unconditionally close an ResultSet. *

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

    * Equivalent to {@link Statement#close()}, except any exceptions will be ignored. * This is typically used in finally blocks. * * @param stmt */ void closeQuietly(final PreparedStatement 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 void closeQuietly(final Connection conn) { closeQuietly(null, (PreparedStatement) 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 * @param conn */ void closeQuietly(final ResultSet rs, final PreparedStatement 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 rs * @param stmt * @param conn */ void closeQuietly(final PreparedStatement 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 */ void closeQuietly(final ResultSet rs, final PreparedStatement stmt, final Connection conn) { closeQuietly(rs, stmt, conn, null); } protected void closeQuietly(final PreparedStatement stmt, final Connection localConn, final Connection inputConn) { closeQuietly(null, stmt, localConn, inputConn); } protected void closeQuietly(final ResultSet rs, final PreparedStatement stmt, final Connection localConn, final Connection inputConn) { JdbcUtil.closeQuietly(rs, stmt, inputConn == null ? localConn : null); } /** * Close the underline data source */ @Override public void close() throws IOException { try { if (_ds != null && _ds.isClosed() == false) { _ds.close(); } } finally { if (_dsm != null && _dsm.isClosed() == false) { _dsm.close(); } } } protected int getBatchSize(final JdbcSettings jdbcSettings) { return ((jdbcSettings == null) || (jdbcSettings.getBatchSize() < 0)) ? JdbcSettings.DEFAULT_BATCH_SIZE : jdbcSettings.getBatchSize(); } protected StatementSetter checkStatementSetter(final NamedSQL namedSQL, StatementSetter statementSetter) { if (statementSetter == null) { statementSetter = DEFAULT_STATEMENT_SETTER; } return statementSetter; } @SuppressWarnings("unchecked") protected ResultSetExtractor checkResultSetExtractor(final NamedSQL namedSQL, ResultSetExtractor resultSetExtractor) { if (resultSetExtractor == null) { resultSetExtractor = (ResultSetExtractor) DEFAULT_RESULT_SET_EXTRACTOR; } return resultSetExtractor; } protected JdbcSettings checkJdbcSettings(final JdbcSettings jdbcSettings, final NamedSQL namedSQL) { JdbcSettings newJdbcSettings = null; if (jdbcSettings == null) { newJdbcSettings = setJdbcSettingsForNamedSQL(_jdbcSettings, namedSQL); } else { newJdbcSettings = setJdbcSettingsForNamedSQL(jdbcSettings, namedSQL); } if ((newJdbcSettings.getOffset() < 0) || (newJdbcSettings.getCount() < 0)) { throw new IllegalArgumentException("offset or count can't be less than 0: " + newJdbcSettings.getOffset() + ", " + newJdbcSettings.getCount()); } return newJdbcSettings; } protected JdbcSettings setJdbcSettingsForNamedSQL(JdbcSettings jdbcSettings, final NamedSQL namedSQL) { if ((namedSQL == null) || N.isNullOrEmpty(namedSQL.getAttribes())) { return jdbcSettings; } else { jdbcSettings = jdbcSettings.copy(); Map attrs = namedSQL.getAttribes(); String attr = attrs.get(SQLMapper.BATCH_SIZE); if (attr != null) { jdbcSettings.setBatchSize(N.asInt(attr)); } attr = attrs.get(SQLMapper.FETCH_SIZE); if (attr != null) { jdbcSettings.setFetchSize(N.asInt(attr)); } attr = attrs.get(SQLMapper.RESULT_SET_TYPE); if (attr != null) { Integer resultSetType = SQLMapper.RESULT_SET_TYPE_MAP.get(attr); if (resultSetType == null) { throw new IllegalArgumentException("Result set type: '" + attr + "' is not supported"); } jdbcSettings.setResultSetType(resultSetType); } attr = attrs.get(SQLMapper.TIMEOUT); if (attr != null) { jdbcSettings.setQueryTimeout(N.asInt(attr)); } return jdbcSettings; } } protected String checkGeneratedIdPropName(final JdbcSettings jdbcSettings) { return ((jdbcSettings == null) || (jdbcSettings.getGeneratedIdPropName() == null)) ? JdbcSettings.DEFAULT_GENERATED_ID_PROP_NAME : jdbcSettings.getGeneratedIdPropName(); } protected NamedSQL getNamedSQL(final String sql) { NamedSQL namedSQL = null; if (_sqlMapper != null) { namedSQL = _sqlMapper.get(sql); } if (namedSQL == null) { namedSQL = NamedSQL.parse(sql); } return namedSQL; } protected DataSource getDataSource(final String sql, final Object[] parameters, final JdbcSettings jdbcSettings) { if (_dsm == null || _dss == null) { if ((jdbcSettings != null) && (jdbcSettings.getQueryWithDataSource() != null || N.notNullOrEmpty(jdbcSettings.getQueryWithDataSources()))) { throw new UncheckedSQLException("No data source is available with name: " + (jdbcSettings.getQueryWithDataSource() != null ? jdbcSettings.getQueryWithDataSource() : N.toString(jdbcSettings.getQueryWithDataSources()))); } return _ds; } else { if ((jdbcSettings == null) || (jdbcSettings.getQueryWithDataSource() == null)) { return _dss.select(_dsm, null, sql, parameters, null); } else { return _dss.select(_dsm, null, sql, parameters, N.asProps(QUERY_WITH_DATA_SOURCE, jdbcSettings.getQueryWithDataSource())); } } } protected DataSource getDataSource(final String sql, final List parametersList, final JdbcSettings jdbcSettings) { if (_dsm == null || _dss == null) { if ((jdbcSettings != null) && (jdbcSettings.getQueryWithDataSource() != null || N.notNullOrEmpty(jdbcSettings.getQueryWithDataSources()))) { throw new UncheckedSQLException("No data source is available with name: " + (jdbcSettings.getQueryWithDataSource() != null ? jdbcSettings.getQueryWithDataSource() : N.toString(jdbcSettings.getQueryWithDataSources()))); } return _ds; } else { if ((jdbcSettings == null) || (jdbcSettings.getQueryWithDataSource() == null)) { return _dss.select(_dsm, null, sql, parametersList, null); } else { return _dss.select(_dsm, null, sql, parametersList, N.asProps(QUERY_WITH_DATA_SOURCE, jdbcSettings.getQueryWithDataSource())); } } } protected PreparedStatement prepareStatement(final DataSource ds, final Connection localConn, final NamedSQL namedSQL, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final int autoGeneratedKeys, final boolean isBatch, final Object... parameters) throws SQLException { String sql = namedSQL.getPureSQL(); if (isBatch) { sql = ds.getSliceSelector().select(null, sql, (List) parameters[0], null); } else { sql = ds.getSliceSelector().select(null, sql, parameters, null); } PreparedStatement stmt = null; if (jdbcSettings == null) { stmt = localConn.prepareStatement(sql); } else { if (N.notNullOrEmpty(jdbcSettings.getColumnIndexes())) { if (jdbcSettings.getColumnIndexes().length != 1) { throw new IllegalArgumentException("only 1 generated key is supported At present"); } stmt = localConn.prepareStatement(sql, jdbcSettings.getColumnIndexes()); } else if (N.notNullOrEmpty(jdbcSettings.getColumnNames())) { if (jdbcSettings.getColumnNames().length != 1) { throw new IllegalArgumentException("only 1 generated key is supported At present"); } stmt = localConn.prepareStatement(sql, jdbcSettings.getColumnNames()); } else if ((jdbcSettings.getAutoGeneratedKeys() == Statement.RETURN_GENERATED_KEYS) || (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS)) { stmt = localConn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); } else if ((jdbcSettings.getResultSetType() != -1) || (jdbcSettings.getResultSetConcurrency() != -1) || (jdbcSettings.getResultSetHoldability() != -1)) { int resultSetType = (jdbcSettings.getResultSetType() == -1) ? JdbcSettings.DEFAULT_RESULT_SET_TYPE : jdbcSettings.getResultSetType(); int resultSetConcurrency = (jdbcSettings.getResultSetConcurrency() == -1) ? JdbcSettings.DEFAULT_RESULT_SET_CONCURRENCY : jdbcSettings.getResultSetConcurrency(); if (jdbcSettings.getResultSetHoldability() != -1) { stmt = localConn.prepareStatement(sql, resultSetType, resultSetConcurrency, jdbcSettings.getResultSetHoldability()); } else { stmt = localConn.prepareStatement(sql, resultSetType, resultSetConcurrency); } } else { stmt = localConn.prepareStatement(sql); } if (jdbcSettings.getFetchSize() != -1) { stmt.setFetchSize(jdbcSettings.getFetchSize()); } if (jdbcSettings.getMaxRows() != -1) { stmt.setMaxRows(jdbcSettings.getMaxRows()); } if (jdbcSettings.getMaxFieldSize() != -1) { stmt.setMaxFieldSize(jdbcSettings.getMaxFieldSize()); } if (jdbcSettings.getFetchDirection() != -1) { stmt.setFetchDirection(jdbcSettings.getFetchDirection()); } if (jdbcSettings.getQueryTimeout() != -1) { stmt.setQueryTimeout(jdbcSettings.getQueryTimeout()); } } if (isBatch || N.isNullOrEmpty(parameters)) { // ignore } else { statementSetter.setParameters(namedSQL, stmt, parameters); } if ((jdbcSettings != null) && jdbcSettings.isLogSQL() && logger.isInfoEnabled()) { if (N.isNullOrEmpty(parameters)) { logger.info(sql); } else { logger.info(sql + " {" + N.deepToString(parameters) + "}"); } } return stmt; } /** * * @param sql should be prepared sql because it will be cached. * @param rs * @return * @throws SQLException */ protected static List getColumnLabelList(final String sql, final ResultSet rs) throws SQLException { List labelList = N.notNullOrEmpty(sql) ? _sqlColumnLabelPool.get(sql) : null; if (labelList == null) { labelList = ImmutableList.of(JdbcUtil.getColumnLabelList(rs)); if (N.notNullOrEmpty(sql) && sql.length() <= CACHED_SQL_LENGTH) { if (_sqlColumnLabelPool.size() >= SQL_CACHE_SIZE) { final List tmp = new ArrayList<>(_sqlColumnLabelPool.keySet()); Maps.removeKeys(_sqlColumnLabelPool, tmp.subList(0, (int) (tmp.size() * 0.25))); } _sqlColumnLabelPool.put(sql, labelList); } } return labelList; } protected static boolean isEntityOrMapParameter(final NamedSQL namedSQL, final Object... parameters) { Map namedParameters = namedSQL.getNamedParameters(); if (N.isNullOrEmpty(namedParameters)) { return false; } if (N.isNullOrEmpty(parameters) || (parameters.length != 1) || (parameters[0] == null)) { return false; } if (parameters[0] instanceof Map || (N.isEntity(parameters[0].getClass()))) { return true; } if (parameters[0] instanceof TypedParameters) { return isEntityOrMapParameter(namedSQL, ((TypedParameters) parameters[0]).parameters); } return false; } /** * * @author haiyang li * * @param */ public static final class Mapper { @Documented @Target(value = { FIELD, /* METHOD, */ TYPE }) @Retention(RUNTIME) public static @interface Id { String[] value() default ""; } /** * The field will be excluded by add/addAll/batchAdd and update/updateAll/batchUpdate operations if the input is an entity. */ @Documented @Target(value = { FIELD /* METHOD, */ }) @Retention(RUNTIME) public static @interface ReadOnly { } /** * The field is identity and will be excluded by add/addAll/batchAdd and update/updateAll/batchUpdate operations if the input is an entity. */ @Documented @Target(value = { FIELD, /* METHOD, */ TYPE }) @Retention(RUNTIME) public static @interface ReadOnlyId { String[] value() default ""; } /** * The properties will be ignored by update/updateAll/batchUpdate operations if the input is an entity. */ @Documented @Target(value = { FIELD /* METHOD, */ }) @Retention(RUNTIME) public static @interface NonUpdatable { } /** * The properties will be ignored by all db related operations if the input is an entity. */ @Documented @Target(value = { FIELD /* METHOD, */ }) @Retention(RUNTIME) public static @interface Transient { } static final List EXISTS_SELECT_PROP_NAMES = ImmutableList.of(NE._1); static final List COUNT_SELECT_PROP_NAMES = ImmutableList.of(NE.COUNT_ALL); private final Class targetClass; private final Type targetType; private final List propNameList; private final Set propNameSet; private final List defaultSelectPropNameList; private final String idPropName; private final List idPropNameList; private final Set idPropNameSet; private final Set readOnlyPropNameSet; private final Set nonUpdatablePropNameSet; private final SQLExecutor sqlExecutor; private final NamingPolicy namingPolicy; private final Condition idCond; private final String sql_exists_by_id; private final String sql_get_by_id; private final String sql_delete_by_id; private final AsyncMapper asyncMapper; // TODO cache more sqls to improve performance. Mapper(final Class targetClass, final SQLExecutor sqlExecutor, final NamingPolicy namingPolicy) { this.sqlExecutor = sqlExecutor; this.namingPolicy = namingPolicy; final Set readOnlyPropNames = new HashSet<>(); final Set nonUpdatablePropNames = new HashSet<>(); final Set transientPropNames = new HashSet<>(); final Set idPropNames = new LinkedHashSet<>(); final Set allFields = new HashSet<>(); for (Class superClass : ClassUtil.getAllSuperclasses(targetClass)) { allFields.addAll(Array.asList(superClass.getDeclaredFields())); } allFields.addAll(Array.asList(targetClass.getDeclaredFields())); for (Field field : allFields) { if (ClassUtil.getPropGetMethod(targetClass, field.getName()) == null && ClassUtil.getPropGetMethod(targetClass, ClassUtil.formalizePropName(field.getName())) == null) { continue; } if (field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(ReadOnlyId.class)) { idPropNames.add(field.getName()); } if (field.isAnnotationPresent(ReadOnly.class) || field.isAnnotationPresent(ReadOnlyId.class)) { readOnlyPropNames.add(field.getName()); } if (field.isAnnotationPresent(NonUpdatable.class)) { nonUpdatablePropNames.add(field.getName()); } if (field.isAnnotationPresent(Transient.class) || Modifier.isTransient(field.getModifiers())) { readOnlyPropNames.add(field.getName()); transientPropNames.add(field.getName()); transientPropNames.add(ClassUtil.formalizePropName(field.getName())); } } /* final Iterator> iter = Iterators.concat(ClassUtil.getPropGetMethodList(targetClass).entrySet(), ClassUtil.getPropSetMethodList(targetClass).entrySet()); Map.Entry entry = null; while (iter.hasNext()) { entry = iter.next(); if (entry.getValue().isAnnotationPresent(Id.class) || entry.getValue().isAnnotationPresent(ReadOnlyId.class)) { idPropNames.add(entry.getKey()); } if (entry.getValue().isAnnotationPresent(ReadOnly.class) || entry.getValue().isAnnotationPresent(ReadOnlyId.class)) { readOnlyPropNames.add(entry.getKey()); } if (entry.getValue().isAnnotationPresent(NonUpdatable.class)) { nonUpdatablePropNames.add(entry.getKey()); } if (entry.getValue().isAnnotationPresent(Transient.class)) { readOnlyPropNames.add(entry.getKey()); transientPropNames.add(entry.getKey()); transientPropNames.add(ClassUtil.formalizePropName(entry.getKey())); } } */ if (targetClass.isAnnotationPresent(Id.class) || targetClass.isAnnotationPresent(ReadOnlyId.class)) { String[] values = targetClass.getAnnotation(Id.class).value(); N.checkArgNotNullOrEmpty(values, "values for annotation @Id on Type/Class can't be null or empty"); idPropNames.addAll(Arrays.asList(values)); if (targetClass.isAnnotationPresent(ReadOnlyId.class)) { readOnlyPropNames.addAll(Arrays.asList(values)); } } nonUpdatablePropNames.addAll(readOnlyPropNames); N.checkArgNotNullOrEmpty(idPropNames, "Target class: " + ClassUtil.getCanonicalClassName(targetClass) + " must at least has one id property annotated by @Id or @ReadOnlyId on field/method or class"); this.targetClass = targetClass; this.targetType = N.typeOf(targetClass); this.propNameList = ImmutableList.copyOf(ClassUtil.getPropGetMethodList(targetClass).keySet()); this.propNameSet = ImmutableSet.of(N.newLinkedHashSet(ClassUtil.getPropGetMethodList(targetClass).keySet())); this.idPropName = idPropNames.iterator().next(); this.idPropNameList = ImmutableList.copyOf(idPropNames); this.idPropNameSet = ImmutableSet.copyOf(idPropNames); this.defaultSelectPropNameList = ImmutableList.copyOf(SQLBuilder.getPropNamesByClass(targetClass, false, transientPropNames)); this.readOnlyPropNameSet = N.isNullOrEmpty(readOnlyPropNames) ? null : ImmutableSet.of(readOnlyPropNames); this.nonUpdatablePropNameSet = N.isNullOrEmpty(nonUpdatablePropNames) ? null : ImmutableSet.of(nonUpdatablePropNames); Condition cond = null; if (idPropNameList.size() == 1) { cond = L.eq(idPropName); } else { final And and = L.and(); for (String idName : idPropNameList) { and.add(L.eq(idName)); } cond = and; } this.idCond = cond; this.sql_exists_by_id = this.prepareQuery(SQLBuilder._1_list, idCond).sql; this.sql_get_by_id = this.prepareQuery(defaultSelectPropNameList, idCond).sql; this.sql_delete_by_id = this.prepareDelete(idCond).sql; this.asyncMapper = new AsyncMapper(this, sqlExecutor._asyncExecutor); } public Class targetClass() { return targetClass; } public Type targetType() { return targetType; } public List idPropNameList() { return idPropNameList; } public Set idPropNameSet() { return idPropNameSet; } public List propNameList() { return propNameList; } public Set propNameSet() { return propNameSet; } public AsyncMapper async() { return asyncMapper; } private void checkId(final Object id) { N.checkArgument(idPropNameList.size() > 1 || !(id instanceof Map || N.isEntity(id.getClass())), "Input 'id' can not be Map or entity for single id "); } private Condition id2Cond(final Object id, boolean isIdOrEntity) { if (isIdOrEntity == false) { checkId(id); } if (idPropNameList.size() == 1) { if (id instanceof Map) { return L.eq(idPropName, ((Map) id).get(idPropName)); } else if (N.isEntity(id.getClass())) { return L.eq(idPropName, ClassUtil.getPropValue(id, idPropName)); } else { return L.eq(idPropName, id); } } if (id instanceof Map) { return L.eqAnd((Map) id); } else if (N.isEntity(id.getClass())) { return L.eqAnd(id, idPropNameList); } else { throw new IllegalArgumentException("Not supported id type: " + (id == null ? "null" : ClassUtil.getClassName(id.getClass()))); } } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @return */ public boolean exists(final Object id) { checkId(id); return sqlExecutor.queryForInt(sql_exists_by_id, id).orElse(0) > 0; } public boolean exists(final Condition whereCause) { return exists(null, whereCause); } public boolean exists(final Connection conn, final Object id) { checkId(id); return sqlExecutor.queryForSingleResult(int.class, conn, sql_exists_by_id, id).orElse(0) > 0; } public boolean exists(final Connection conn, final Condition whereCause) { final SP pair = prepareQuery(EXISTS_SELECT_PROP_NAMES, whereCause, 1); return sqlExecutor.exists(conn, pair.sql, pair.parameters.toArray()); } public int count(final Condition whereCause) { return count(null, whereCause); } public int count(final Connection conn, final Condition whereCause) { final SP pair = prepareQuery(COUNT_SELECT_PROP_NAMES, whereCause); return sqlExecutor.count(conn, pair.sql, pair.parameters.toArray()); } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @return */ public T get(final Object id) { return get(id, (Collection) null); } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ @SafeVarargs public final T get(final Object id, final String... selectPropNames) { return get(id, Arrays.asList(selectPropNames)); } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ public T get(final Object id, final Collection selectPropNames) { return get(null, id, selectPropNames); } /** * * @param conn * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ public T get(final Connection conn, final Object id, final Collection selectPropNames) { checkId(id); final JdbcSettings jdbcSetting = JdbcSettings.create().setCount(2); List entities = null; if (N.isNullOrEmpty(selectPropNames)) { entities = sqlExecutor.find(targetClass, conn, sql_get_by_id, null, jdbcSetting, id); } else { final SP pair = prepareQuery(selectPropNames, idCond, 2); entities = sqlExecutor.find(targetClass, conn, pair.sql, null, jdbcSetting, id); } if (N.isNullOrEmpty(entities)) { return null; } else if (entities.size() == 1) { return entities.get(0); } else { throw new NonUniqueResultException("More than one records found by id: " + id); } } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @return */ public Optional gett(final Object id) { return Optional.ofNullable(this.get(id)); } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ @SafeVarargs public final Optional gett(final Object id, final String... selectPropNames) { return Optional.ofNullable(this.get(id, selectPropNames)); } /** * * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ public Optional gett(final Object id, final Collection selectPropNames) { return Optional.ofNullable(this.get(id, selectPropNames)); } /** * * @param conn * @param id which could be {@code Number}/{@code String}... or {@code Map} for composed id. * @param selectPropNames * @return */ public Optional gett(final Connection conn, final Object id, final Collection selectPropNames) { return Optional.ofNullable(this.get(conn, id, selectPropNames)); } /** * * @param ids * @return */ public List batchGet(final List ids) { return batchGet(ids, (Collection) null); } /** * * @param ids * @param selectPropNames * @return */ @SafeVarargs public final List batchGet(final List ids, final String... selectPropNames) { return batchGet(ids, Arrays.asList(selectPropNames)); } /** * * @param ids * @param selectPropNames * @return */ public List batchGet(final List ids, final Collection selectPropNames) { return batchGet(ids, selectPropNames, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * * @param ids * @param selectPropNames * @param batchSize * @return */ public List batchGet(final List ids, final Collection selectPropNames, final int batchSize) { return batchGet(null, ids, selectPropNames, batchSize); } /** * * @param conn * @param ids * @param selectPropNames * @param batchSize * @return */ public List batchGet(final Connection conn, final List ids, final Collection selectPropNames, final int batchSize) { N.checkArgument(batchSize > 0, "The specified batch size must be greater than 0"); if (N.isNullOrEmpty(ids)) { return new ArrayList<>(); } N.checkArgument(idPropNameList.size() > 1 || !(ids.get(0) instanceof Map || N.isEntity(ids.get(0).getClass())), "Input 'ids' can not be Maps or entities for single id "); final List entities = new ArrayList<>(ids.size()); if (idPropNameList.size() == 1) { String sql = prepareQuery(selectPropNames, idCond).sql; sql = sql.substring(0, sql.lastIndexOf('=')) + "IN "; if (ids.size() >= batchSize) { final Joiner joiner = Joiner.with(", ", "(", ")").reuseStringBuilder(true); for (int i = 0; i < batchSize; i++) { joiner.append('?'); } String inSQL = sql + joiner.toString(); for (int i = 0, to = ids.size() - batchSize; i <= to; i += batchSize) { entities.addAll(sqlExecutor.find(targetClass, conn, inSQL, null, null, ids.subList(i, i + batchSize).toArray())); } } if (ids.size() % batchSize != 0) { final int remaining = ids.size() % batchSize; final Joiner joiner = Joiner.with(", ", "(", ")").reuseStringBuilder(true); for (int i = 0; i < remaining; i++) { joiner.append('?'); } String inSQL = sql + joiner.toString(); entities.addAll(sqlExecutor.find(targetClass, conn, inSQL, null, null, ids.subList(ids.size() - remaining, ids.size()).toArray())); } } else { final boolean isMap = ids.get(0) instanceof Map; if (ids.size() >= batchSize) { for (int i = 0, to = ids.size() - batchSize; i <= to; i += batchSize) { if (isMap) { entities.addAll(find(L.eqAndOr((List>) ids.subList(i, i + batchSize)))); } else { entities.addAll(find(L.eqAndOr(ids.subList(i, i + batchSize), idPropNameList))); } } } if (ids.size() % batchSize != 0) { final int remaining = ids.size() % batchSize; if (isMap) { entities.addAll(find(L.eqAndOr((List>) ids.subList(ids.size() - remaining, ids.size())))); } else { entities.addAll(find(L.eqAndOr(ids.subList(ids.size() - remaining, ids.size()), idPropNameList))); } } } return entities; } public List find(final Condition whereCause) { return find(null, whereCause); } public List find(final Collection selectPropNames, final Condition whereCause) { return find(selectPropNames, whereCause, null); } public List find(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return find(null, selectPropNames, whereCause, jdbcSettings); } public List find(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return find(conn, selectPropNames, whereCause, null); } public List find(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.find(targetClass, conn, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#findAll(Class, String, StatementSetter, JdbcSettings, Object...) */ public List findAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return findAll(propNameList(), whereCause, jdbcSettings); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param selectPropNames * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#findAll(Class, String, StatementSetter, JdbcSettings, Object...) */ public List findAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.findAll(targetClass, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } public DataSet query(final Condition whereCause) { return query(null, whereCause); } public DataSet query(final Collection selectPropNames, final Condition whereCause) { return query(selectPropNames, whereCause, null); } public DataSet query(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return query(null, selectPropNames, whereCause, jdbcSettings); } public DataSet query(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return query(conn, selectPropNames, whereCause, null); } public DataSet query(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.query(conn, pair.sql, null, null, jdbcSettings, pair.parameters.toArray()); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#queryAll(String, StatementSetter, JdbcSettings, Object...) */ public DataSet queryAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return queryAll(propNameList(), whereCause, jdbcSettings); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param selectPropNames * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#queryAll(String, StatementSetter, JdbcSettings, Object...) */ public DataSet queryAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.queryAll(pair.sql, null, jdbcSettings, pair.parameters.toArray()); } public OptionalBoolean queryForBoolean(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Boolean.class, propName, whereCause); return res.isPresent() ? OptionalBoolean.of(res.orIfNull(false)) : OptionalBoolean.empty(); } public OptionalChar queryForChar(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Character.class, propName, whereCause); return res.isPresent() ? OptionalChar.of(res.orIfNull((char) 0)) : OptionalChar.empty(); } public OptionalByte queryForByte(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Byte.class, propName, whereCause); return res.isPresent() ? OptionalByte.of(res.orIfNull((byte) 0)) : OptionalByte.empty(); } public OptionalShort queryForShort(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Short.class, propName, whereCause); return res.isPresent() ? OptionalShort.of(res.orIfNull((short) 0)) : OptionalShort.empty(); } public OptionalInt queryForInt(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Integer.class, propName, whereCause); return res.isPresent() ? OptionalInt.of(res.orIfNull(0)) : OptionalInt.empty(); } public OptionalLong queryForLong(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Long.class, propName, whereCause); return res.isPresent() ? OptionalLong.of(res.orIfNull(0L)) : OptionalLong.empty(); } public OptionalFloat queryForFloat(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Float.class, propName, whereCause); return res.isPresent() ? OptionalFloat.of(res.orIfNull(0F)) : OptionalFloat.empty(); } public OptionalDouble queryForDouble(final String propName, final Condition whereCause) { final Nullable res = queryForSingleResult(Double.class, propName, whereCause); return res.isPresent() ? OptionalDouble.of(res.orIfNull(0D)) : OptionalDouble.empty(); } public Nullable queryForString(final String propName, final Condition whereCause) { return queryForSingleResult(String.class, propName, whereCause); } public Nullable queryForDate(final String propName, final Condition whereCause) { return queryForSingleResult(Date.class, propName, whereCause); } public Nullable queryForDate(final Class targetClass, final String propName, final Condition whereCause) { // final Nullable date = this.queryForDate(propName, whereCause); // // if (date.isNotNull()) { // if (targetClass.isAssignableFrom(date.get().getClass())) { // return (Nullable) date; // } else if (targetClass.equals(Timestamp.class)) { // return (Nullable) Nullable.of((new Timestamp(date.get().getTime()))); // } else if (targetClass.equals(Time.class)) { // return (Nullable) Nullable.of((new Time(date.get().getTime()))); // } else if (targetClass.equals(java.sql.Date.class)) { // return (Nullable) Nullable.of((new java.sql.Date(date.get().getTime()))); // } else { // return Nullable.of(N.as(targetClass, date.get())); // } // } else { // return (Nullable) date; // } return queryForSingleResult(targetClass, propName, whereCause); } public Nullable queryForSingleResult(final Class targetValueClass, final String propName, final Object id) { return queryForSingleResult(targetValueClass, propName, id2Cond(id, false)); } public Nullable queryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause) { return queryForSingleResult(targetValueClass, propName, whereCause, null); } public Nullable queryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return queryForSingleResult(targetValueClass, null, propName, whereCause, jdbcSettings); } public Nullable queryForSingleResult(final Class targetValueClass, final Connection conn, final String propName, final Condition whereCause) { return queryForSingleResult(targetValueClass, conn, propName, whereCause, null); } public Nullable queryForSingleResult(final Class targetValueClass, final Connection conn, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(Arrays.asList(propName), whereCause, 1); return sqlExecutor.queryForSingleResult(targetValueClass, conn, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } public Optional queryForEntity(final Condition whereCause) { return queryForEntity(null, whereCause); } public Optional queryForEntity(final Collection selectPropNames, final Condition whereCause) { return queryForEntity(selectPropNames, whereCause, null); } public Optional queryForEntity(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return queryForEntity(null, selectPropNames, whereCause, jdbcSettings); } public Optional queryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return queryForEntity(conn, selectPropNames, whereCause, null); } public Optional queryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause, 1); return sqlExecutor.queryForEntity(targetClass, conn, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } public List queryForList(final String propName, final Condition whereCause) { return queryForList(propName, whereCause, null); } public List queryForList(final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return queryForList(null, propName, whereCause, jdbcSettings); } public List queryForList(final Connection conn, final String propName, final Condition whereCause) { return queryForList(conn, propName, whereCause, null); } public List queryForList(final Connection conn, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(Arrays.asList(propName), whereCause); return sqlExecutor.queryForList(conn, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } public Try> stream(final Condition whereCause) { return stream(null, whereCause); } public Try> stream(final Collection selectPropNames, final Condition whereCause) { return stream(selectPropNames, whereCause, null); } public Try> stream(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return stream(null, selectPropNames, whereCause, jdbcSettings); } Try> stream(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return stream(conn, selectPropNames, whereCause, null); } Try> stream(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.stream(targetClass, conn, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#streamAll(Class, String, StatementSetter, JdbcSettings, Object...) */ public Try> streamAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return streamAll(propNameList(), whereCause, jdbcSettings); } /** * Query from multiple data sources specified in {@code JdbcSettings}. * * @param selectPropNames * @param whereCause * @param jdbcSettings * @return * @see SQLExecutor#streamAll(Class, String, StatementSetter, JdbcSettings, Object...) */ public Try> streamAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.streamAll(targetClass, pair.sql, null, jdbcSettings, pair.parameters.toArray()); } private SP prepareQuery(final Collection selectPropNames, final Condition whereCause) { return prepareQuery(selectPropNames, whereCause, 0); } private SP prepareQuery(Collection selectPropNames, final Condition whereCause, final int count) { if (N.isNullOrEmpty(selectPropNames)) { selectPropNames = defaultSelectPropNameList; } SQLBuilder sqlBuilder = null; switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: if (N.isNullOrEmpty(selectPropNames)) { sqlBuilder = NE.selectFrom(targetClass).where(whereCause); } else { sqlBuilder = NE.select(selectPropNames).from(targetClass).where(whereCause); } break; case UPPER_CASE_WITH_UNDERSCORE: if (N.isNullOrEmpty(selectPropNames)) { sqlBuilder = NE2.selectFrom(targetClass).where(whereCause); } else { sqlBuilder = NE2.select(selectPropNames).from(targetClass).where(whereCause); } break; case LOWER_CAMEL_CASE: if (N.isNullOrEmpty(selectPropNames)) { sqlBuilder = NE3.selectFrom(targetClass).where(whereCause); } else { sqlBuilder = NE3.select(selectPropNames).from(targetClass).where(whereCause); } break; default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } if (count > 0 && count < Integer.MAX_VALUE) { switch (sqlExecutor.dbVersion()) { case ORACLE: case SQL_SERVER: // Do nothing because limit is not supported. break; default: sqlBuilder.limit(count); } } return sqlBuilder.pair(); } /** * Insert the specified entity into data store, and set back the auto-generated id to the specified entity if there is the auto-generated id. * * @param entity * @return the auto-generated id or null if there is no auto-generated id. */ public ID add(final Object entity) { return add(null, entity); } public ID add(final Object entity, final Collection insertPropNames) { return add(null, entity, insertPropNames); } /** * * @param props * @return the auto-generated id or null if there is no auto-generated id. */ public ID add(final Map props) { return add(null, props); } public ID add(final Connection conn, final Object entity) { return add(conn, entity, null); } @SuppressWarnings("deprecation") public ID add(final Connection conn, final Object entity, final Collection insertPropNames) { N.checkArgNotNull(entity); if (entity instanceof DirtyMarker && N.isNullOrEmpty(((DirtyMarker) entity).signedPropNames())) { throw new IllegalArgumentException("No property value is signed to the specified entity"); } if (insertPropNames == null) { final SP pair = prepareAdd(entity); final ID id = sqlExecutor.insert(conn, pair.sql, pair.parameters.toArray()); postAdd(entity, id); return id; } else { final SP pair = prepareAdd(insertPropNames); final ID id = sqlExecutor.insert(conn, pair.sql, entity); postAdd(entity, id); return id; } } /** * @param conn * @param props * @return the auto-generated id or null if there is no auto-generated id. */ public ID add(final Connection conn, final Map props) { N.checkArgNotNull(props); final SP pair = prepareAdd(props); return sqlExecutor.insert(conn, pair.sql, pair.parameters.toArray()); } /** * Insert All the records one by one in transaction. And set back auto-generated ids to the specified entities if there are the auto-generated ids. * * @param entities * @return a list with the auto-generated id or null element if there is no auto-generated id. * @deprecated replaced by {@code #batchAdd(Collection)} */ @Deprecated public List addAll(final Collection entities) { return addAll(entities, IsolationLevel.DEFAULT); } /** * Insert All the records one by one in transaction. * * @param entities * @param isolationLevel * @return a list with the auto-generated id or null element if there is no auto-generated id. * @deprecated replaced by {@code #batchAdd(Collection)} */ @Deprecated public List addAll(final Collection entities, final IsolationLevel isolationLevel) { if (N.isNullOrEmpty(entities)) { return new ArrayList<>(); } final SQLTransaction tran = sqlExecutor.beginTransaction(isolationLevel); final Connection conn = tran.connection(); final List result = new ArrayList<>(entities.size()); boolean isOk = false; try { for (Object entity : entities) { result.add((ID) add(conn, entity)); } isOk = true; } finally { if (tran != null) { if (isOk) { tran.commit(); } else { tran.rollback(); } } } return result; } /** * Insert All the records one by one with specified {@code Connection}. * * @param conn * @param entities * @return * @deprecated replaced by {@code #batchAdd(Collection)} */ @Deprecated public List addAll(final Connection conn, final Collection entities) { if (N.isNullOrEmpty(entities)) { return new ArrayList<>(); } if (conn == null) { return addAll(entities); } final List result = new ArrayList<>(entities.size()); for (Object entity : entities) { result.add((ID) add(conn, entity)); } return result; } /** * Insert All the records by batch operation. And set back auto-generated ids to the specified entities if there are the auto-generated ids. * * @param entities which must have the same updated properties set. * @return the auto-generated id list or an empty list if there is no auto-generated id. */ public List batchAdd(final Collection entities) { return batchAdd(entities, JdbcSettings.DEFAULT_BATCH_SIZE); } public List batchAdd(final Collection entities, final int batchSize) { return batchAdd(entities, batchSize, IsolationLevel.DEFAULT); } public List batchAdd(final Collection entities, final int batchSize, final IsolationLevel isolationLevel) { return batchAdd(null, entities, batchSize); } public List batchAdd(final Connection conn, final Collection entities) { return batchAdd(conn, entities, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * Insert All the records by batch operation. And set back auto-generated ids to the specified entities if there are the auto-generated ids. * * @param entities which must have the same updated properties set. * @param batchSize Default value is 200. * @return the auto-generated id list or an empty list if there is no auto-generated id. */ public List batchAdd(final Connection conn, final Collection entities, final int batchSize) { return batchAdd(conn, entities, batchSize, IsolationLevel.DEFAULT); } private List batchAdd(final Connection conn, final Collection entities, final int batchSize, final IsolationLevel isolationLevel) { N.checkArgument(batchSize > 0, "The specified batch size must be greater than 0"); if (N.isNullOrEmpty(entities)) { return new ArrayList<>(); } final SP pair = prepareAdd(entities.iterator().next()); final JdbcSettings jdbcSettings = JdbcSettings.create().setBatchSize(batchSize).setIsolationLevel(isolationLevel); final List parametersList = entities instanceof List ? (List) entities : new ArrayList<>(entities); final List ids = sqlExecutor.batchInsert(conn, pair.sql, null, jdbcSettings, parametersList); if (N.notNullOrEmpty(ids) && ids.size() == batchSize) { int idx = 0; for (Object entity : entities) { postAdd(entity, ids.get(idx++)); } } return ids; } private SP prepareAdd(final Collection insertPropNames) { SP pair = null; switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: pair = NE.insert(insertPropNames).into(targetClass).pair(); break; case UPPER_CASE_WITH_UNDERSCORE: pair = NE2.insert(insertPropNames).into(targetClass).pair(); break; case LOWER_CAMEL_CASE: pair = NE3.insert(insertPropNames).into(targetClass).pair(); break; default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } return pair; } private SP prepareAdd(final Object entity) { checkEntity(entity); if (entity instanceof Map) { return prepareAdd((Map) entity); } else if (entity instanceof DirtyMarker) { @SuppressWarnings("deprecation") final Set signedPropNames = ((DirtyMarker) entity).signedPropNames(); final Map props = new HashMap<>(N.initHashCapacity(signedPropNames.size())); for (String propName : signedPropNames) { props.put(propName, ClassUtil.getPropValue(entity, propName)); } return prepareAdd(props); } else { switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: return NE.insert(entity, readOnlyPropNameSet).into(targetClass).pair(); case UPPER_CASE_WITH_UNDERSCORE: return NE2.insert(entity, readOnlyPropNameSet).into(targetClass).pair(); case LOWER_CAMEL_CASE: return NE3.insert(entity, readOnlyPropNameSet).into(targetClass).pair(); default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } } } private SP prepareAdd(final Map props) { N.checkArgument(N.notNullOrEmpty(props), "The specified 'props' can't be null or empty"); if (N.notNullOrEmpty(readOnlyPropNameSet) && !N.disjoint(readOnlyPropNameSet, props.keySet())) { throw new IllegalArgumentException("Can't write read-only properties: " + N.intersection(readOnlyPropNameSet, props.keySet())); } switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: return NE.insert(props).into(targetClass).pair(); case UPPER_CASE_WITH_UNDERSCORE: return NE2.insert(props).into(targetClass).pair(); case LOWER_CAMEL_CASE: return NE3.insert(props).into(targetClass).pair(); default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } } @SuppressWarnings("deprecation") private void postAdd(final Object entity, final E idVal) { if (idVal != null && N.isEntity(entity.getClass())) { ClassUtil.setPropValue(entity, idPropName, idVal); } if (entity instanceof DirtyMarker) { ((DirtyMarker) entity).markDirty(false); } } /** * Execute {@code add} and return the added entity if the record doesn't, otherwise, {@code update} is executed and updated db record is returned. * * @param entity * @return */ public T addOrUpdate(final T entity) { final T dbEntity = idPropNameList.size() == 1 ? get(ClassUtil.getPropValue(entity, idPropName)) : get(entity); if (dbEntity == null) { add(entity); return entity; } else { N.merge(entity, dbEntity); update(dbEntity); return dbEntity; } } /** * Execute {@code add} and return the added entity if the record doesn't, otherwise, {@code update} is executed and updated db record is returned. * * @param entity * @param queryPropNames the properties names used to verify the if the record exists or not. * @return */ public T addOrUpdate(final T entity, final String... queryPropNames) { return addOrUpdate(entity, Array.asList(queryPropNames)); } /** * Execute {@code add} and return the added entity if the record doesn't, otherwise, {@code update} is executed and updated db record is returned. * * @param entity * @param queryPropNames the properties names used to verify the if the record exists or not. * @return */ public T addOrUpdate(final T entity, final Collection queryPropNames) { N.checkArgNotNullOrEmpty(queryPropNames, "queryPropNames"); final And and = new And(); for (String propName : queryPropNames) { and.add(L.eq(propName, ClassUtil.getPropValue(entity, propName))); } final T dbEntity = queryForEntity(and).orNull(); if (dbEntity == null) { add(entity); return entity; } else { N.merge(entity, dbEntity, false, idPropNameSet); update(dbEntity); return dbEntity; } } public int update(final Object entity) { return update(entity, (Collection) null); } public int update(final Object entity, final Collection updatePropNames) { return update((Connection) null, entity, updatePropNames); } public int update(final Object id, final Map props) { return update((Connection) null, id, props); } public int update(final Map props, final Condition whereCause) { return update((Connection) null, props, whereCause); } public int update(final Connection conn, final Object entity) { return update(conn, entity, (Collection) null); } @SuppressWarnings("deprecation") public int update(final Connection conn, final Object entity, final Collection updatePropNames) { N.checkArgNotNull(entity); if (updatePropNames == null && entity instanceof DirtyMarker && !((DirtyMarker) entity).isDirty()) { return 0; } final SP pair = prepareUpdate(entity, updatePropNames); final int updateCount = sqlExecutor.update(conn, pair.sql, pair.parameters.toArray()); postUpdate(entity, updatePropNames); return updateCount; } public int update(final Connection conn, final Object id, final Map props) { N.checkArgNotNull(id); return update(conn, props, id2Cond(id, false)); } public int update(final Connection conn, final Map props, final Condition whereCause) { N.checkArgNotNull(props); N.checkArgNotNull(whereCause); if (N.isNullOrEmpty(props)) { return 0; } final SP pair = prepareUpdate(whereCause, props); return sqlExecutor.update(conn, pair.sql, pair.parameters.toArray()); } /** * Update All the records one by one in transaction. * * @param entities * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Collection entities) { return updateAll(entities, (Collection) null); } /** * * @param entities * @param updatePropNames * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Collection entities, final Collection updatePropNames) { return updateAll(entities, updatePropNames, IsolationLevel.DEFAULT); } /** * * @param entities * @param isolationLevel * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Collection entities, final IsolationLevel isolationLevel) { return updateAll(entities, (Collection) null, isolationLevel); } /** * Update All the records one by one in transaction. * * @param entities * @param updatePropNames * @param isolationLevel * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Collection entities, final Collection updatePropNames, final IsolationLevel isolationLevel) { if (N.isNullOrEmpty(entities)) { return 0; } final SQLTransaction tran = sqlExecutor.beginTransaction(isolationLevel); final Connection conn = tran.connection(); int result = 0; boolean isOk = false; try { for (Object entity : entities) { result += update(conn, entity, updatePropNames); } isOk = true; } finally { if (tran != null) { if (isOk) { tran.commit(); } else { tran.rollback(); } } } return result; } /** * * @param conn * @param entities * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Connection conn, final Collection entities) { return updateAll(conn, entities, (Collection) null); } /** * Update All the records one by one with specified {@code Connection}. * * @param conn * @param entities * @param updatePropNames * @return * @deprecated replaced by {@code #batchUpdate(Collection)} */ @Deprecated public int updateAll(final Connection conn, final Collection entities, final Collection updatePropNames) { if (N.isNullOrEmpty(entities)) { return 0; } if (conn == null) { return updateAll(entities, updatePropNames); } int result = 0; for (Object entity : entities) { result += update(conn, entity, updatePropNames); } return result; } /** * Update All the records by batch operation. * * @param entities which must have the same updated properties set. * @return */ public int batchUpdate(final Collection entities) { return batchUpdate(entities, (Collection) null); } /** * * @param entities * @param updatePropNames * @return */ public int batchUpdate(final Collection entities, final Collection updatePropNames) { return batchUpdate(entities, updatePropNames, JdbcSettings.DEFAULT_BATCH_SIZE); } public int batchUpdate(final Collection entities, final int batchSize) { return batchUpdate(entities, (Collection) null, batchSize); } /** * Update All the records by batch operation. * * @param entities which must have the same updated properties set. * @param updatePropNames * @param batchSize Default value is 200. * @return */ public int batchUpdate(final Collection entities, final Collection updatePropNames, final int batchSize) { return batchUpdate(entities, updatePropNames, batchSize, IsolationLevel.DEFAULT); } public int batchUpdate(final Collection entities, final int batchSize, final IsolationLevel isolationLevel) { return batchUpdate(entities, (Collection) null, batchSize, isolationLevel); } public int batchUpdate(final Collection entities, final Collection updatePropNames, final int batchSize, final IsolationLevel isolationLevel) { return batchUpdate((Connection) null, entities, updatePropNames, batchSize, isolationLevel); } public int batchUpdate(final Connection conn, final Collection entities) { return batchUpdate(conn, entities, (Collection) null); } public int batchUpdate(final Connection conn, final Collection entities, final Collection updatePropNames) { return batchUpdate(conn, entities, updatePropNames, JdbcSettings.DEFAULT_BATCH_SIZE); } public int batchUpdate(final Connection conn, final Collection entities, final int batchSize) { return batchUpdate(conn, entities, (Collection) null, batchSize); } public int batchUpdate(final Connection conn, final Collection entities, final Collection updatePropNames, final int batchSize) { return batchUpdate(conn, entities, updatePropNames, batchSize, IsolationLevel.DEFAULT); } private int batchUpdate(final Connection conn, final Collection entities, final Collection updatePropNames, final int batchSize, final IsolationLevel isolationLevel) { N.checkArgument(batchSize > 0, "The specified batch size must be greater than 0"); if (N.isNullOrEmpty(entities)) { return 0; } final SP pair = prepareUpdate(entities.iterator().next(), updatePropNames); final JdbcSettings jdbcSettings = JdbcSettings.create().setBatchSize(batchSize).setIsolationLevel(isolationLevel); final List parametersList = entities instanceof List ? (List) entities : new ArrayList<>(entities); final int updateCount = sqlExecutor.batchUpdate(conn, pair.sql, null, jdbcSettings, parametersList); if (N.firstNonNull(entities).orNull() instanceof DirtyMarker) { for (Object entity : entities) { postUpdate(entity, updatePropNames); } } return updateCount; } @SuppressWarnings("deprecation") private SP prepareUpdate(final Object entity, final Collection updatePropNames) { checkEntity(entity); final boolean isDirtyMarkerEntity = entity instanceof DirtyMarker; Collection updatingpropNames = updatePropNames; if (updatingpropNames == null) { if (isDirtyMarkerEntity) { updatingpropNames = ((DirtyMarker) entity).dirtyPropNames(); } else { updatingpropNames = SQLBuilder.getPropNamesByClass(entity.getClass(), false, nonUpdatablePropNameSet); } } final Map props = N.newHashMap(N.initHashCapacity(updatingpropNames.size())); for (String propName : updatingpropNames) { props.put(propName, ClassUtil.getPropValue(entity, propName)); } if (idPropNameList.size() == 1) { props.remove(idPropName); } else { Maps.removeKeys(props, idPropNameList); } return prepareUpdate(id2Cond(entity, true), props); } private SP prepareUpdate(final Condition whereCause, final Map props) { if (N.notNullOrEmpty(nonUpdatablePropNameSet) && !N.disjoint(nonUpdatablePropNameSet, props.keySet())) { throw new IllegalArgumentException("Can't write non-updatable properties: " + N.intersection(nonUpdatablePropNameSet, props.keySet())); } switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: return NE.update(targetClass).set(props).where(whereCause).pair(); case UPPER_CASE_WITH_UNDERSCORE: return NE2.update(targetClass).set(props).where(whereCause).pair(); case LOWER_CAMEL_CASE: return NE3.update(targetClass).set(props).where(whereCause).pair(); default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } } @SuppressWarnings("deprecation") private void postUpdate(final Object entity, final Collection updatePropNames) { if (entity instanceof DirtyMarker) { if (updatePropNames == null) { ((DirtyMarker) entity).markDirty(false); } else { ((DirtyMarker) entity).markDirty(updatePropNames, false); } } } public int delete(final Object idOrEntity) { return delete(null, idOrEntity); } public int delete(final Condition whereCause) { return delete(null, whereCause); } public int delete(final Connection conn, final Object idOrEntity) { N.checkArgNotNull(idOrEntity); checkEntity(idOrEntity); return sqlExecutor.update(conn, sql_delete_by_id, idOrEntity); } public int delete(final Connection conn, final Condition whereCause) { N.checkArgNotNull(whereCause); if (idPropNameList.size() == 1 && whereCause instanceof Equal && ((Equal) whereCause).getPropName().equals(idPropName)) { final Object id = ((Equal) whereCause).getPropValue(); return sqlExecutor.update(conn, sql_delete_by_id, id); } final SP pair = prepareDelete(whereCause); return sqlExecutor.update(conn, pair.sql, pair.parameters.toArray()); } /** * Delete All records by ids or entities in one transaction. * * @param idsOrEntities * @return * @deprecated replaced by {@code #deleteAll(Collection)} */ @Deprecated public int deleteAll(final Collection idsOrEntities) { return deleteAll(idsOrEntities, IsolationLevel.DEFAULT); } /** * Delete All records by ids or entities in one transaction. * * @param idsOrEntities * @param isolationLevel * @return * @deprecated replaced by {@code #deleteAll(Collection)} */ @Deprecated public int deleteAll(final Collection idsOrEntities, final IsolationLevel isolationLevel) { if (N.isNullOrEmpty(idsOrEntities)) { return 0; } final SQLTransaction tran = sqlExecutor.beginTransaction(isolationLevel); final Connection conn = tran.connection(); int result = 0; boolean isOk = false; try { for (Object idOrEntity : idsOrEntities) { result += delete(conn, idOrEntity); } isOk = true; } finally { if (tran != null) { if (isOk) { tran.commit(); } else { tran.rollback(); } } } return result; } /** * Delete All records by ids or entities with specified {@code Connection}. * * @param conn * @param idsOrEntities * @return * @deprecated replaced by {@code #deleteAll(Collection)} */ @Deprecated public int deleteAll(final Connection conn, final Collection idsOrEntities) { if (N.isNullOrEmpty(idsOrEntities)) { return 0; } if (conn == null) { return deleteAll(idsOrEntities); } int result = 0; for (Object idOrEntity : idsOrEntities) { result += delete(conn, idOrEntity); } return result; } /** * Delete all the records by batch operation. * * @param idsOrEntities * @return */ public int batchDelete(final Collection idsOrEntities) { return batchDelete(idsOrEntities, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * Delete all the records by batch operation. * * @param idsOrEntities * @param batchSize Default value is 200. * @return */ public int batchDelete(final Collection idsOrEntities, final int batchSize) { return batchDelete(idsOrEntities, batchSize, IsolationLevel.DEFAULT); } /** * Delete all the records by batch operation. * * @param idsOrEntities * @param batchSize Default value is 200. * @param isolationLevel * @return */ public int batchDelete(final Collection idsOrEntities, final int batchSize, final IsolationLevel isolationLevel) { return batchDelete(null, idsOrEntities, batchSize, isolationLevel); } /** * Delete all the records by batch operation. * * @param conn * @param idsOrEntities * @return */ public int batchDelete(final Connection conn, final Collection idsOrEntities) { return batchDelete(conn, idsOrEntities, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * Delete all the records by batch operation. * * @param conn * @param idsOrEntities * @param batchSize * @return */ public int batchDelete(final Connection conn, final Collection idsOrEntities, final int batchSize) { return batchDelete(conn, idsOrEntities, batchSize, IsolationLevel.DEFAULT); } private int batchDelete(final Connection conn, final Collection idsOrEntities, final int batchSize, final IsolationLevel isolationLevel) { N.checkArgument(batchSize > 0, "The specified batch size must be greater than 0"); if (N.isNullOrEmpty(idsOrEntities)) { return 0; } final List ids = idsOrEntities instanceof List ? ((List) idsOrEntities) : N.newArrayList(idsOrEntities); return sqlExecutor.batchUpdate(conn, sql_delete_by_id, null, ids); } private SP prepareDelete(final Condition whereCause) { SP pair = null; switch (namingPolicy) { case LOWER_CASE_WITH_UNDERSCORE: pair = NE.deleteFrom(targetClass).where(whereCause).pair(); break; case UPPER_CASE_WITH_UNDERSCORE: pair = NE2.deleteFrom(targetClass).where(whereCause).pair(); break; case LOWER_CAMEL_CASE: pair = NE3.deleteFrom(targetClass).where(whereCause).pair(); break; default: throw new RuntimeException("Unsupported naming policy: " + namingPolicy); } return pair; } private void checkEntity(final Object entity) { final Class cls = entity.getClass(); if (N.isEntity(cls)) { N.checkArgument(targetClass.isAssignableFrom(cls), "Delete wrong type: " + ClassUtil.getCanonicalClassName(cls) + " in " + toString()); } } public String toStirng() { return "Mapper[" + ClassUtil.getCanonicalClassName(targetClass) + "]"; } } public static final class AsyncMapper { private final Mapper mapper; private final AsyncExecutor asyncExecutor; AsyncMapper(Mapper mapper, AsyncExecutor asyncExecutor) { this.mapper = mapper; this.asyncExecutor = asyncExecutor; } public CompletableFuture exists(final Object id) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return mapper.exists(id); } }); } public CompletableFuture exists(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return mapper.exists(whereCause); } }); } public CompletableFuture exists(final Connection conn, final Object id) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return mapper.exists(conn, id); } }); } public CompletableFuture exists(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return mapper.exists(conn, whereCause); } }); } public CompletableFuture count(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.count(whereCause); } }); } public CompletableFuture count(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.count(conn, whereCause); } }); } public CompletableFuture get(final Object id) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.get(id); } }); } @SafeVarargs public final CompletableFuture get(final Object id, final String... selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.get(id, selectPropNames); } }); } public CompletableFuture get(final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.get(id, selectPropNames); } }); } public CompletableFuture get(final Connection conn, final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.get(conn, id, selectPropNames); } }); } public CompletableFuture> gett(final Object id) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.gett(id); } }); } @SafeVarargs public final CompletableFuture> gett(final Object id, final String... selectPropNames) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.gett(id, selectPropNames); } }); } public CompletableFuture> gett(final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.gett(id, selectPropNames); } }); } public CompletableFuture> gett(final Connection conn, final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.gett(conn, id, selectPropNames); } }); } public CompletableFuture> batchGet(final List ids) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchGet(ids); } }); } public CompletableFuture> batchGet(final List ids, final String... selectPropNames) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchGet(ids, selectPropNames); } }); } public CompletableFuture> batchGet(final List ids, final Collection selectPropNames) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchGet(ids, selectPropNames); } }); } public CompletableFuture> batchGet(final List ids, final Collection selectPropNames, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchGet(ids, selectPropNames, batchSize); } }); } public CompletableFuture> batchGet(final Connection conn, final List ids, final Collection selectPropNames, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchGet(conn, ids, selectPropNames, batchSize); } }); } public CompletableFuture> find(final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.find(whereCause); } }); } public CompletableFuture> find(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.find(selectPropNames, whereCause); } }); } public CompletableFuture> find(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.find(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> find(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.find(conn, selectPropNames, whereCause); } }); } public CompletableFuture> find(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.find(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> findAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.findAll(whereCause, jdbcSettings); } }); } public CompletableFuture> findAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.findAll(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture query(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.query(whereCause); } }); } public CompletableFuture query(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.query(selectPropNames, whereCause); } }); } public CompletableFuture query(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.query(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture query(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.query(conn, selectPropNames, whereCause); } }); } public CompletableFuture query(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.query(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture queryAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.queryAll(whereCause, jdbcSettings); } }); } public CompletableFuture queryAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return mapper.queryAll(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture queryForBoolean(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalBoolean call() throws Exception { return mapper.queryForBoolean(propName, whereCause); } }); } public CompletableFuture queryForByte(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalByte call() throws Exception { return mapper.queryForByte(propName, whereCause); } }); } public CompletableFuture queryForShort(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalShort call() throws Exception { return mapper.queryForShort(propName, whereCause); } }); } public CompletableFuture queryForInt(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalInt call() throws Exception { return mapper.queryForInt(propName, whereCause); } }); } public CompletableFuture queryForLong(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalLong call() throws Exception { return mapper.queryForLong(propName, whereCause); } }); } public CompletableFuture queryForFloat(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalFloat call() throws Exception { return mapper.queryForFloat(propName, whereCause); } }); } public CompletableFuture queryForDouble(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalDouble call() throws Exception { return mapper.queryForDouble(propName, whereCause); } }); } public CompletableFuture> queryForString(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForString(propName, whereCause); } }); } public CompletableFuture> queryForDate(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForDate(propName, whereCause); } }); } @SuppressWarnings("hiding") public CompletableFuture> queryForDate(final Class targetClass, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForDate(targetClass, propName, whereCause); } }); } public CompletableFuture> queryForSingleResult(final Class targetValueClass, final String propName, final Object id) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForSingleResult(targetValueClass, propName, id); } }); } public CompletableFuture> queryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForSingleResult(targetValueClass, propName, whereCause); } }); } public CompletableFuture> queryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForSingleResult(targetValueClass, propName, whereCause, jdbcSettings); } }); } public CompletableFuture> queryForSingleResult(final Class targetValueClass, final Connection conn, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForSingleResult(targetValueClass, conn, propName, whereCause); } }); } public CompletableFuture> queryForSingleResult(final Class targetValueClass, final Connection conn, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Nullable call() throws Exception { return mapper.queryForSingleResult(targetValueClass, conn, propName, whereCause, jdbcSettings); } }); } public CompletableFuture> queryForEntity(final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.queryForEntity(whereCause); } }); } public CompletableFuture> queryForEntity(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.queryForEntity(selectPropNames, whereCause); } }); } public CompletableFuture> queryForEntity(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.queryForEntity(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> queryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.queryForEntity(conn, selectPropNames, whereCause); } }); } public CompletableFuture> queryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return mapper.queryForEntity(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> queryForList(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.queryForList(propName, whereCause); } }); } public CompletableFuture> queryForList(final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.queryForList(propName, whereCause, jdbcSettings); } }); } public CompletableFuture> queryForList(final Connection conn, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.queryForList(conn, propName, whereCause); } }); } public CompletableFuture> queryForList(final Connection conn, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.queryForList(conn, propName, whereCause, jdbcSettings); } }); } public CompletableFuture>> stream(final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.stream(whereCause); } }); } public CompletableFuture>> stream(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.stream(selectPropNames, whereCause); } }); } public CompletableFuture>> stream(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.stream(selectPropNames, whereCause, jdbcSettings); } }); } CompletableFuture>> stream(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.stream(conn, selectPropNames, whereCause); } }); } CompletableFuture>> stream(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.stream(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture>> streamAll(final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.streamAll(whereCause, jdbcSettings); } }); } public CompletableFuture>> streamAll(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return mapper.streamAll(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture add(final Object entity) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(entity); } }); } public CompletableFuture add(final Object entity, final Collection insertPropNames) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(entity, insertPropNames); } }); } public CompletableFuture add(final Map props) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(props); } }); } public CompletableFuture add(final Connection conn, final Object entity) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(conn, entity); } }); } public CompletableFuture add(final Connection conn, final Object entity, final Collection insertPropNames) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(conn, entity, insertPropNames); } }); } public CompletableFuture add(final Connection conn, final Map props) { return asyncExecutor.execute(new Callable() { @Override public ID call() throws Exception { return mapper.add(conn, props); } }); } @Deprecated public CompletableFuture> addAll(final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.addAll(entities); } }); } @Deprecated public CompletableFuture> addAll(final Collection entities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.addAll(entities, isolationLevel); } }); } @Deprecated public CompletableFuture> addAll(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.addAll(conn, entities); } }); } public CompletableFuture> batchAdd(final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchAdd(entities); } }); } public CompletableFuture> batchAdd(final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchAdd(entities, batchSize); } }); } public CompletableFuture> batchAdd(final Collection entities, final int batchSize, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchAdd(entities, batchSize, isolationLevel); } }); } public CompletableFuture> batchAdd(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchAdd(conn, entities); } }); } public CompletableFuture> batchAdd(final Connection conn, final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return mapper.batchAdd(conn, entities, batchSize); } }); } public CompletableFuture addOrUpdate(final T entity) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.addOrUpdate(entity); } }); } public CompletableFuture addOrUpdate(final T entity, final String... queryPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.addOrUpdate(entity, queryPropNames); } }); } public CompletableFuture addOrUpdate(final T entity, final Collection queryPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return mapper.addOrUpdate(entity, queryPropNames); } }); } public CompletableFuture update(final Object entity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(entity); } }); } public CompletableFuture update(final Object entity, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(entity, updatePropNames); } }); } public CompletableFuture update(final Object id, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(id, props); } }); } public CompletableFuture update(final Map props, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(props, whereCause); } }); } public CompletableFuture update(final Connection conn, final Object entity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(conn, entity); } }); } public CompletableFuture update(final Connection conn, final Object entity, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(conn, entity, updatePropNames); } }); } public CompletableFuture update(final Connection conn, final Object id, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(conn, id, props); } }); } public CompletableFuture update(final Connection conn, final Map props, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.update(conn, props, whereCause); } }); } @Deprecated public CompletableFuture updateAll(final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(entities); } }); } @Deprecated public CompletableFuture updateAll(final Collection entities, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(entities, updatePropNames); } }); } @Deprecated public CompletableFuture updateAll(final Collection entities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(entities, isolationLevel); } }); } @Deprecated public CompletableFuture updateAll(final Collection entities, final Collection updatePropNames, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(entities, updatePropNames, isolationLevel); } }); } @Deprecated public CompletableFuture updateAll(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(conn, entities); } }); } @Deprecated public CompletableFuture updateAll(final Connection conn, final Collection entities, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.updateAll(conn, entities, updatePropNames); } }); } public CompletableFuture batchUpdate(final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities); } }); } public CompletableFuture batchUpdate(final Collection entities, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities, updatePropNames); } }); } public CompletableFuture batchUpdate(final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities, batchSize); } }); } public CompletableFuture batchUpdate(final Collection entities, final Collection updatePropNames, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities, updatePropNames, batchSize); } }); } public CompletableFuture batchUpdate(final Collection entities, final int batchSize, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities, batchSize, isolationLevel); } }); } public CompletableFuture batchUpdate(final Collection entities, final Collection updatePropNames, final int batchSize, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(entities, updatePropNames, batchSize, isolationLevel); } }); } public CompletableFuture batchUpdate(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(conn, entities); } }); } public CompletableFuture batchUpdate(final Connection conn, final Collection entities, final Collection updatePropNames) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(conn, entities, updatePropNames); } }); } public CompletableFuture batchUpdate(final Connection conn, final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(conn, entities, batchSize); } }); } public CompletableFuture batchUpdate(final Connection conn, final Collection entities, final Collection updatePropNames, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchUpdate(conn, entities, updatePropNames, batchSize); } }); } public CompletableFuture delete(final Object idOrEntity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.delete(idOrEntity); } }); } public CompletableFuture delete(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.delete(whereCause); } }); } public CompletableFuture delete(final Connection conn, final Object idOrEntity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.delete(conn, idOrEntity); } }); } public CompletableFuture delete(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.delete(conn, whereCause); } }); } @Deprecated public CompletableFuture deleteAll(final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.deleteAll(idsOrEntities); } }); } @Deprecated public CompletableFuture deleteAll(final Collection idsOrEntities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.deleteAll(idsOrEntities, isolationLevel); } }); } @Deprecated public CompletableFuture deleteAll(final Connection conn, final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.deleteAll(conn, idsOrEntities); } }); } public CompletableFuture batchDelete(final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchDelete(idsOrEntities); } }); } public CompletableFuture batchDelete(final Collection idsOrEntities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchDelete(idsOrEntities, batchSize); } }); } public CompletableFuture batchDelete(final Collection idsOrEntities, final int batchSize, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchDelete(idsOrEntities, batchSize, isolationLevel); } }); } public CompletableFuture batchDelete(final Connection conn, final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchDelete(conn, idsOrEntities); } }); } public CompletableFuture batchDelete(final Connection conn, final Collection idsOrEntities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return mapper.batchDelete(conn, idsOrEntities, batchSize); } }); } } // public static interface RowMapper { // /** // * Implementations must implement this method to map each row of data // * in the ResultSet. This method should not call {@code next()} on // * the ResultSet; it is only supposed to map values of the current row. // * @param rs the ResultSet to map (pre-initialized for the current row) // * @param rowNum the number of the current row // * @return the result object for the current row // * @throws SQLException if a SQLException is encountered getting // * column values (that is, there's no need to catch SQLException) // */ // T mapRow(ResultSet rs, int rowNum) throws SQLException; // } /** * Refer to http://landawn.com/introduction-to-jdbc.html about how to set parameters in java.sql.PreparedStatement * * @author Haiyang Li * */ public static interface StatementSetter { public void setParameters(final NamedSQL namedSQL, final PreparedStatement stmt, final Object... parameters) throws SQLException; } /** * Refer to http://landawn.com/introduction-to-jdbc.html about how to read columns/rows from java.sql.ResultSet * * @author Haiyang Li * */ public static interface ResultSetExtractor { public T extractData(final Class targetClass, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException; } public static abstract class AbstractStatementSetter implements StatementSetter { @Override @SafeVarargs public final void setParameters(final NamedSQL namedSQL, final PreparedStatement stmt, final Object... parameters) throws SQLException { final int parameterCount = namedSQL.getParameterCount(); if (parameterCount == 0) { return; } else if (N.isNullOrEmpty(parameters) || ((parameters[0] != null) && parameters[0] instanceof TypedParameters && N.isNullOrEmpty(((TypedParameters) parameters[0]).parameters))) { throw new IllegalArgumentException( "The count of parameter in sql is: " + namedSQL.getParameterCount() + ". But the specified parameters is null or empty"); } final StatementDataChannel sdc = new StatementDataChannel(stmt); Object[] a = null; int[] sqlTypes = null; if (isEntityOrMapParameter(namedSQL, parameters)) { Map namedParameters = namedSQL.getNamedParameters(); Object parameter_0 = parameters[0]; if (parameter_0 instanceof TypedParameters) { TypedParameters typedParameters = (TypedParameters) parameter_0; sqlTypes = typedParameters.types; parameter_0 = typedParameters.parameters[0]; } a = new Object[parameterCount]; if (parameter_0 instanceof Map) { @SuppressWarnings("unchecked") Map m = (Map) parameter_0; for (int i = 0; i < parameterCount; i++) { a[i] = m.get(namedParameters.get(i)); if ((a[i] == null) && !m.containsKey(namedParameters.get(i))) { throw new IllegalArgumentException("Parameter for property '" + namedParameters.get(i) + "' is missed"); } } } else { Object entity = parameter_0; Class clazz = entity.getClass(); Method propGetMethod = null; for (int i = 0; i < parameterCount; i++) { propGetMethod = ClassUtil.getPropGetMethod(clazz, namedParameters.get(i)); if (propGetMethod == null) { throw new IllegalArgumentException("Parameter for property '" + namedParameters.get(i) + "' is missed"); } a[i] = ClassUtil.invokeMethod(entity, propGetMethod); } } } else { if ((parameters.length == 1) && (parameters[0] != null) && parameters[0] instanceof TypedParameters) { TypedParameters typedParameters = (TypedParameters) parameters[0]; a = getArrayParameters(namedSQL, typedParameters.parameters); sqlTypes = typedParameters.types; } else { a = getArrayParameters(namedSQL, parameters); } } setParameters(sdc, parameterCount, a, sqlTypes); } protected abstract void setParameters(StatementDataChannel sdc, int parameterCount, Object[] a, int[] sqlTypes) throws SQLException; protected Object[] getArrayParameters(final NamedSQL namedSQL, final Object... parameters) { if ((parameters.length == 1) && (parameters[0] != null)) { if (parameters[0] instanceof Object[] && ((((Object[]) parameters[0]).length) >= namedSQL.getParameterCount())) { return (Object[]) parameters[0]; } else if (parameters[0] instanceof List && (((List) parameters[0]).size() >= namedSQL.getParameterCount())) { final Collection c = (Collection) parameters[0]; return c.toArray(new Object[c.size()]); } } return parameters; } } public static abstract class AbstractResultSetExtractor implements ResultSetExtractor { @Override public T extractData(final Class targetClass, final NamedSQL namedSQL, final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { final List columnLabelList = getColumnLabelList(namedSQL, rs); final int columnCount = columnLabelList.size(); final List columnNameList = new ArrayList<>(columnCount); final List> columnList = new ArrayList<>(columnCount); for (int i = 0; i < columnCount; i++) { columnNameList.add(columnLabelList.get(i)); columnList.add(new ArrayList<>()); } long offset = jdbcSettings.getOffset(); long count = jdbcSettings.getCount(); while ((offset-- > 0) && rs.next()) { } if (offset <= 0) { while ((count-- > 0) && rs.next()) { for (int i = 0; i < columnCount;) { columnList.get(i).add(rs.getObject(++i)); } } } DataSet dataSet = new RowDataSet(columnNameList, columnList); return handle(targetClass, dataSet); } protected T handle(final Class targetClass, final DataSet dataSet) { return (T) dataSet; } protected List getColumnLabelList(final NamedSQL namedSQL, final ResultSet rs) throws SQLException { return SQLExecutor.getColumnLabelList(namedSQL.getPureSQL(), rs); } } static class DefaultStatementSetter extends AbstractStatementSetter { @Override protected void setParameters(final StatementDataChannel sdc, final int parameterCount, final Object[] a, final int[] sqlTypes) throws SQLException { if (N.isNullOrEmpty(sqlTypes)) { for (int i = 0; i < parameterCount; i++) { sdc.setObject(i, a[i]); } } else { for (int i = 0; i < parameterCount; i++) { sdc.setObject(i, a[i], sqlTypes[i]); } } } } static class DefaultResultSetExtractor extends AbstractResultSetExtractor { } public static class JdbcSettings { public static final String DEFAULT_GENERATED_ID_PROP_NAME = "id"; public static final int DEFAULT_BATCH_SIZE = 200; public static final int DEFAULT_NO_GENERATED_KEYS = Statement.NO_GENERATED_KEYS; public static final int DEFAULT_FETCH_DIRECTION = ResultSet.FETCH_FORWARD; public static final int DEFAULT_RESULT_SET_TYPE = ResultSet.TYPE_FORWARD_ONLY; public static final int DEFAULT_RESULT_SET_CONCURRENCY = ResultSet.CONCUR_READ_ONLY; public static final int DEFAULT_RESULT_SET_HOLDABILITY = ResultSet.HOLD_CURSORS_OVER_COMMIT; private boolean logSQL = false; private int batchSize = -1; private int queryTimeout = -1; private int autoGeneratedKeys = -1; private int[] columnIndexes = null; private String[] columnNames = null; private int maxRows = -1; private int maxFieldSize = -1; private int fetchSize = -1; private int fetchDirection = -1; private int resultSetType = -1; private int resultSetConcurrency = -1; private int resultSetHoldability = -1; private long offset = 0; private long count = Long.MAX_VALUE; private String generatedIdPropName = null; private String queryWithDataSource; private Collection queryWithDataSources; private boolean queryInParallel = false; private IsolationLevel isolationLevel = null; private boolean fozen = false; protected JdbcSettings() { } public static JdbcSettings create() { return new JdbcSettings(); } public JdbcSettings copy() { JdbcSettings copy = new JdbcSettings(); copy.logSQL = this.logSQL; copy.batchSize = this.batchSize; copy.queryTimeout = this.queryTimeout; copy.autoGeneratedKeys = this.autoGeneratedKeys; copy.columnIndexes = (this.columnIndexes == null) ? null : N.copyOf(this.columnIndexes, this.columnIndexes.length); copy.columnNames = (this.columnNames == null) ? null : N.copyOf(this.columnNames, this.columnNames.length); copy.maxRows = this.maxRows; copy.maxFieldSize = this.maxFieldSize; copy.fetchSize = this.fetchSize; copy.fetchDirection = this.fetchDirection; copy.resultSetType = this.resultSetType; copy.resultSetConcurrency = this.resultSetConcurrency; copy.resultSetHoldability = this.resultSetHoldability; copy.offset = this.offset; copy.count = this.count; copy.generatedIdPropName = this.generatedIdPropName; copy.queryWithDataSource = this.queryWithDataSource; copy.queryWithDataSources = this.queryWithDataSources == null ? null : new ArrayList<>(this.queryWithDataSources); copy.queryInParallel = this.queryInParallel; return copy; } public boolean isLogSQL() { return logSQL; } public JdbcSettings setLogSQL(final boolean logSQL) { assertNotFrozen(); this.logSQL = logSQL; return this; } public int getBatchSize() { return batchSize; } public JdbcSettings setBatchSize(final int batchSize) { assertNotFrozen(); this.batchSize = batchSize; return this; } public int getQueryTimeout() { return queryTimeout; } public JdbcSettings setQueryTimeout(final int queryTimeout) { assertNotFrozen(); this.queryTimeout = queryTimeout; return this; } public int getAutoGeneratedKeys() { return autoGeneratedKeys; } public JdbcSettings setAutoGeneratedKeys(final int autoGeneratedKeys) { assertNotFrozen(); this.autoGeneratedKeys = autoGeneratedKeys; return this; } public int[] getColumnIndexes() { return columnIndexes; } public JdbcSettings setColumnIndexes(final int[] columnIndexes) { assertNotFrozen(); this.columnIndexes = columnIndexes; return this; } public String[] getColumnNames() { return columnNames; } public JdbcSettings setColumnNames(final String[] columnNames) { assertNotFrozen(); this.columnNames = columnNames; return this; } public int getMaxRows() { return maxRows; } public JdbcSettings setMaxRows(final int maxRows) { assertNotFrozen(); this.maxRows = maxRows; return this; } public int getMaxFieldSize() { return maxFieldSize; } public JdbcSettings setMaxFieldSize(final int maxFieldSize) { assertNotFrozen(); this.maxFieldSize = maxFieldSize; return this; } public int getFetchSize() { return fetchSize; } public JdbcSettings setFetchSize(final int fetchSize) { assertNotFrozen(); this.fetchSize = fetchSize; return this; } public int getFetchDirection() { return fetchDirection; } public JdbcSettings setFetchDirection(final int fetchDirection) { assertNotFrozen(); this.fetchDirection = fetchDirection; return this; } public int getResultSetType() { return resultSetType; } public JdbcSettings setResultSetType(final int resultSetType) { assertNotFrozen(); this.resultSetType = resultSetType; return this; } public int getResultSetConcurrency() { return resultSetConcurrency; } public JdbcSettings setResultSetConcurrency(final int resultSetConcurrency) { assertNotFrozen(); this.resultSetConcurrency = resultSetConcurrency; return this; } public int getResultSetHoldability() { return resultSetHoldability; } public JdbcSettings setResultSetHoldability(final int resultSetHoldability) { assertNotFrozen(); this.resultSetHoldability = resultSetHoldability; return this; } public long getOffset() { return offset; } public JdbcSettings setOffset(final long offset) { assertNotFrozen(); this.offset = offset; return this; } public long getCount() { return count; } public JdbcSettings setCount(final long count) { assertNotFrozen(); this.count = count; return this; } public String getGeneratedIdPropName() { return generatedIdPropName; } /** * Set the name of auto-generated id property which will set back to the input entity in insert call. The * auto-generated id can't be set into the input entity if wrong name is set. Default name is {@code id} if it's * not set. * * @param generatedIdPropName * @return */ public JdbcSettings setGeneratedIdPropName(final String generatedIdPropName) { assertNotFrozen(); this.generatedIdPropName = generatedIdPropName; return this; } public String getQueryWithDataSource() { return queryWithDataSource; } public JdbcSettings setQueryWithDataSource(final String queryWithDataSource) { assertNotFrozen(); this.queryWithDataSource = queryWithDataSource; return this; } public Collection getQueryWithDataSources() { return queryWithDataSources; } public JdbcSettings setQueryWithDataSources(final Collection queryWithDataSources) { assertNotFrozen(); this.queryWithDataSources = queryWithDataSources; return this; } public boolean isQueryInParallel() { return queryInParallel; } public JdbcSettings setQueryInParallel(final boolean queryInParallel) { assertNotFrozen(); this.queryInParallel = queryInParallel; return this; } public IsolationLevel getIsolationLevel() { return isolationLevel; } public JdbcSettings setIsolationLevel(IsolationLevel isolationLevel) { assertNotFrozen(); this.isolationLevel = isolationLevel; return this; } void freeze() { fozen = true; } void assertNotFrozen() { if (fozen) { throw new AbacusException("It's finalized. No change is allowed"); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + (logSQL ? 1231 : 1237); result = (prime * result) + batchSize; result = (prime * result) + queryTimeout; result = (prime * result) + autoGeneratedKeys; result = (prime * result) + Arrays.hashCode(columnIndexes); result = (prime * result) + Arrays.hashCode(columnNames); result = (prime * result) + maxRows; result = (prime * result) + maxFieldSize; result = (prime * result) + fetchSize; result = (prime * result) + fetchDirection; result = (prime * result) + resultSetType; result = (prime * result) + resultSetConcurrency; result = (prime * result) + resultSetHoldability; result = (prime * result) + (int) offset; result = (prime * result) + (int) count; result = (prime * result) + ((generatedIdPropName == null) ? 0 : generatedIdPropName.hashCode()); result = (prime * result) + ((queryWithDataSource == null) ? 0 : queryWithDataSource.hashCode()); result = (prime * result) + ((queryWithDataSources == null) ? 0 : queryWithDataSources.hashCode()); result = (prime * result) + (queryInParallel ? 1231 : 1237); result = (prime * result) + ((isolationLevel == null) ? 0 : isolationLevel.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof JdbcSettings) { JdbcSettings other = (JdbcSettings) obj; return N.equals(logSQL, other.logSQL) && N.equals(batchSize, other.batchSize) && N.equals(queryTimeout, other.queryTimeout) && N.equals(autoGeneratedKeys, other.autoGeneratedKeys) && N.equals(columnIndexes, other.columnIndexes) && N.equals(columnNames, other.columnNames) && N.equals(maxRows, other.maxRows) && N.equals(maxFieldSize, other.maxFieldSize) && N.equals(fetchSize, other.fetchSize) && N.equals(fetchDirection, other.fetchDirection) && N.equals(resultSetType, other.resultSetType) && N.equals(resultSetConcurrency, other.resultSetConcurrency) && N.equals(resultSetHoldability, other.resultSetHoldability) && N.equals(offset, other.offset) && N.equals(count, other.count) && N.equals(generatedIdPropName, other.generatedIdPropName) && N.equals(queryWithDataSource, other.queryWithDataSource) && N.equals(queryWithDataSources, other.queryWithDataSources) && N.equals(queryInParallel, other.queryInParallel) && N.equals(isolationLevel, other.isolationLevel); } return false; } @Override public String toString() { return "{logSQL=" + logSQL + ", batchSize=" + batchSize + ", queryTimeout=" + queryTimeout + ", autoGeneratedKeys=" + autoGeneratedKeys + ", columnIndexes=" + N.toString(columnIndexes) + ", columnNames=" + N.toString(columnNames) + ", maxRows=" + maxRows + ", maxFieldSize=" + maxFieldSize + ", fetchSize=" + fetchSize + ", fetchDirection=" + fetchDirection + ", resultSetType=" + resultSetType + ", resultSetConcurrency=" + resultSetConcurrency + ", resultSetHoldability=" + resultSetHoldability + ", offset=" + offset + ", count=" + count + ", generatedIdPropName=" + generatedIdPropName + ", queryWithDataSource=" + queryWithDataSource + ", queryWithDataSources=" + queryWithDataSources + ", queryInParallel=" + queryInParallel + ", isolationLevel=" + isolationLevel + "}"; } } /** * * @author haiyangl * *@see http://docs.oracle.com/javase/7/docs/api/java/sql/Types.html */ public static class TypedParameters { protected final Object[] parameters; protected final int[] types; protected TypedParameters(final Object[] parameters, final int[] types) { this.parameters = parameters; this.types = types; } /** * * @param parameters * can be an array or entity, or map, or single property value. * @param types * @return */ @SafeVarargs public static TypedParameters of(final Object parameters, final int... types) { if (Object[].class.isAssignableFrom(parameters.getClass()) && (((Object[]) parameters).length == types.length)) { return new TypedParameters((Object[]) parameters, types); } else { return new TypedParameters(new Object[] { parameters }, types); } } // // /** // * // * @param m pairs of parameter and type. key is parameter, value is // * @return // */ // public static TypedParameters valueOf(Map m) { // Object[] parameters = new Object[m.size()]; // int[] types = new int[m.size()]; // int i = 0; // // for (Object k : m.keySet()) { // parameters[i] = k; // types[i] = m.get(k); // i++; // } // // return valueOf(parameters, types); // } // // // public static TypedParameters build(Object... // pairsOfParameterAndType) { // if ((pairsOfParameterAndType.length % 2) != 0) { // throw new // IllegalArgumentException("pairsOfParameterAndType must be the pairs of parameter and type."); // } // // Object[] parameters = new Object[pairsOfParameterAndType.length / 2]; // int[] types = new int[pairsOfParameterAndType.length / 2]; // // for (int i = 0; i < pairsOfParameterAndType.length; i++) { // parameters[i / 2] = pairsOfParameterAndType[i]; // types[i / 2] = (Integer) pairsOfParameterAndType[++i]; // } // // return new TypedParameters(parameters, types); // } // static TypedParameters[] batchOf(final Object[] parameters, final int... types) { TypedParameters[] typedParameters = new TypedParameters[parameters.length]; for (int i = 0; i < parameters.length; i++) { typedParameters[i] = of(parameters[i], types); } return typedParameters; } @SafeVarargs public static List batchOf(final List parameters, final int... types) { List typedParameters = new ArrayList<>(parameters.size()); for (int i = 0; i < parameters.size(); i++) { typedParameters.add(of(parameters.get(i), types)); } return typedParameters; } @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + Arrays.hashCode(parameters); result = (prime * result) + Arrays.hashCode(types); return result; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof TypedParameters) { TypedParameters other = (TypedParameters) obj; return N.equals(parameters, other.parameters) && N.equals(types, other.types); } return false; } @Override public String toString() { return "{parameters=" + Arrays.toString(parameters) + ", types=" + Arrays.toString(types) + "}"; } public static final class TP extends TypedParameters { private TP(final Object[] parameters, final int[] types) { super(parameters, types); } } } }