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.2.9
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 java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
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 java.util.concurrent.atomic.AtomicInteger;

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.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.util.Options.Query;
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 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 RowIterator.of(rs, jdbcSettings.getOffset(), jdbcSettings.getCount()); } }; 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(); } }; @SuppressWarnings("rawtypes") private static final ResultSetExtractor ENTITY_RESULT_SET_EXTRACTOR = new AbstractResultSetExtractor() { @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++) { RefUtil.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>() { @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 List columnLabelList = getColumnLabelList(namedSQL, rs); final int columnCount = columnLabelList.size(); 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(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++) { RefUtil.setPropValue(entity, columnLabelList.get(i), rs.getObject(i + 1), true); } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } } resultList.add(entity); } } return resultList; } }; private static final int SQL_CACHE_SIZE = 3000; 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; // } public static SQLExecutor w(final String url, final String user, final String password) { return new SQLExecutor(JdbcUtil.createDataSource(url, user, password)); } 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)); } 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), RefUtil.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 = RefUtil.getPropGetMethod(entity.getClass(), idPropName); Method idSetMethod = RefUtil.getPropSetMethod(entity.getClass(), idPropName); if ((idGetMethod != null) && (idSetMethod != null)) { Object idPropValue = RefUtil.getPropValue(entity, idGetMethod); if ((idPropValue == null) || (idPropValue instanceof Number && (((Number) idPropValue).longValue() == 0))) { RefUtil.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[] batchParameters) { return batchInsert(null, sql, null, null, batchParameters); } List batchInsert(final String sql, final StatementSetter statementSetter, final Object[] batchParameters) { return batchInsert(null, sql, statementSetter, null, batchParameters); } List batchInsert(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] batchParameters) { return batchInsert(null, sql, statementSetter, jdbcSettings, batchParameters); } List batchInsert(final Connection conn, final String sql, final Object[] batchParameters) { return batchInsert(conn, sql, null, null, batchParameters); } List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final Object[] batchParameters) { return batchInsert(conn, sql, statementSetter, null, batchParameters); } /** * 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 batchParameters * @return */ List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] batchParameters) { return batchInsert(conn, sql, statementSetter, jdbcSettings, Arrays.asList(batchParameters)); } public List batchInsert(final String sql, final List batchParameters) { return batchInsert(null, sql, null, null, batchParameters); } public List batchInsert(final String sql, final StatementSetter statementSetter, final List batchParameters) { return batchInsert(null, sql, statementSetter, null, batchParameters); } public List batchInsert(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final List batchParameters) { return batchInsert(null, sql, statementSetter, jdbcSettings, batchParameters); } public List batchInsert(final Connection conn, final String sql, final List batchParameters) { return batchInsert(conn, sql, null, null, batchParameters); } public List batchInsert(final Connection conn, final String sql, final StatementSetter statementSetter, final List batchParameters) { return batchInsert(conn, sql, statementSetter, null, batchParameters); } /** * * @see #batchInsert(Connection, String, StatementSetter, JdbcSettings, Object[]) */ @SuppressWarnings("deprecation") public List batchInsert(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final List batchParameters) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); String idPropName = checkGeneratedIdPropName(jdbcSettings); final int len = batchParameters.size(); final int batchSize = getBatchSize(jdbcSettings); List resultIdList = new ArrayList<>(len); DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; boolean autoCommit = true; try { ds = getDataSource(namedSQL.getPureSQL(), batchParameters, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; try { autoCommit = localConn.getAutoCommit(); } catch (SQLException e) { closeQuietly(null, localConn, conn); throw new UncheckedSQLException(e, namedSQL.toString()); } if ((conn == null) && (len > batchSize)) { localConn.setAutoCommit(false); } stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.RETURN_GENERATED_KEYS, true, batchParameters); if (len <= batchSize) { for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, batchParameters.get(i)); stmt.addBatch(); } executeBatchInsert(resultIdList, namedSQL, stmt); } else { int num = 0; for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, batchParameters.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); } catch (SQLException e) { logger.error("Failed to reset AutoCommit", e); } } closeQuietly(stmt, localConn, conn); } if (N.notNullOrEmpty(resultIdList)) { if (isEntityOrMapParameter(namedSQL, batchParameters.get(0))) { if (resultIdList.size() == len) { boolean isTypedParameter = batchParameters.get(0) instanceof TypedParameters; Object parameter_0 = isTypedParameter ? ((TypedParameters) batchParameters.get(0)).parameters[0] : batchParameters.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 = RefUtil.getPropGetMethod(parameter_0.getClass(), idPropName); Method idSetMethod = RefUtil.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) batchParameters.get(i)).parameters[0] : batchParameters.get(i)); idPropValue = RefUtil.invokeMethod(entity, idGetMethod); if ((idPropValue == null) || (idPropValue instanceof Number && (((Number) idPropValue).longValue() == 0))) { RefUtil.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[] batchParameters) { return batchUpdate(null, sql, null, null, batchParameters); } int batchUpdate(final String sql, final StatementSetter statementSetter, final Object[] batchParameters) { return batchUpdate(null, sql, statementSetter, null, batchParameters); } int batchUpdate(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] batchParameters) { return batchUpdate(null, sql, statementSetter, jdbcSettings, batchParameters); } int batchUpdate(final Connection conn, final String sql, final Object[] batchParameters) { return batchUpdate(conn, sql, null, null, batchParameters); } int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final Object[] batchParameters) { return batchUpdate(conn, sql, statementSetter, null, batchParameters); } /** * batch insert/update/delete sql scripts are supported * * @param conn * @param sql * @param statementSetter * @param props * @param batchParameters * @return */ int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object[] batchParameters) { return batchUpdate(conn, sql, statementSetter, jdbcSettings, Arrays.asList(batchParameters)); } public int batchUpdate(final String sql, final List batchParameters) { return batchUpdate(null, sql, null, null, batchParameters); } public int batchUpdate(final String sql, final StatementSetter statementSetter, final List batchParameters) { return batchUpdate(null, sql, statementSetter, null, batchParameters); } public int batchUpdate(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final List batchParameters) { return batchUpdate(null, sql, statementSetter, jdbcSettings, batchParameters); } public int batchUpdate(final Connection conn, final String sql, final List batchParameters) { return batchUpdate(conn, sql, null, null, batchParameters); } public int batchUpdate(final Connection conn, final String sql, final StatementSetter statementSetter, final List batchParameters) { return batchUpdate(conn, sql, statementSetter, null, batchParameters); } /** * @see #batchUpdate(Connection, String, StatementSetter, JdbcSettings, Object[]) */ public int batchUpdate(final Connection conn, final String sql, StatementSetter statementSetter, JdbcSettings jdbcSettings, final List batchParameters) { final NamedSQL namedSQL = getNamedSQL(sql); statementSetter = checkStatementSetter(namedSQL, statementSetter); jdbcSettings = checkJdbcSettings(jdbcSettings, namedSQL); final int len = batchParameters.size(); final int batchSize = getBatchSize(jdbcSettings); DataSource ds = null; Connection localConn = null; PreparedStatement stmt = null; boolean autoCommit = true; try { ds = getDataSource(namedSQL.getPureSQL(), batchParameters, jdbcSettings); localConn = (conn == null) ? ds.getConnection() : conn; try { autoCommit = localConn.getAutoCommit(); } catch (SQLException e) { closeQuietly(null, localConn, conn); throw new UncheckedSQLException(e, namedSQL.toString()); } if ((conn == null) && (len > batchSize)) { localConn.setAutoCommit(false); } stmt = prepareStatement(ds, localConn, namedSQL, statementSetter, jdbcSettings, Statement.NO_GENERATED_KEYS, true, batchParameters); int result = 0; if (len <= batchSize) { for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, batchParameters.get(i)); stmt.addBatch(); } result += executeBatchUpdate(namedSQL, stmt); } else { int num = 0; for (int i = 0; i < len; i++) { statementSetter.setParameters(namedSQL, stmt, batchParameters.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); } 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).or(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); } public 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") public 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 List find(final Class targetClass, final String sql, final Object... parameters) { return find(targetClass, sql, null, null, parameters); } public 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") public 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); } public 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) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sql, statementSetter, newJdbcSettings, parameters); try (final Stream stream = (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)).skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount())) { final NamedSQL namedSQL = getNamedSQL(sql); final ResultSet rs = iterators.get(0).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); final List resultList = new ArrayList<>(); stream.forEach(new Consumer() { @Override public void accept(Object[] a) { if (isMap) { final Map m = (Map) N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { m.put(columnLabels[i], a[i]); } resultList.add((T) m); } else { final Object entity = N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { if (columnLabels[i] == null) { continue; } if (RefUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } resultList.add((T) entity); } } }); return resultList; } catch ( SQLException e) { throw new UncheckedSQLException(e); } finally { IOUtil.closeQuietly(iterators); } } @SafeVarargs public final List findAll(final Class targetClass, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return findAll(targetClass, sqls, null, jdbcSettings, parameters); } public 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) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sqls, statementSetter, newJdbcSettings, parameters); try (final Stream stream = (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)).skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount())) { final NamedSQL namedSQL = getNamedSQL(sqls.get(0)); final ResultSet rs = iterators.get(0).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); final List resultList = new ArrayList<>(); stream.forEach(new Consumer() { @Override public void accept(Object[] a) { if (isMap) { final Map m = (Map) N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { m.put(columnLabels[i], a[i]); } resultList.add((T) m); } else { final Object entity = N.newInstance(targetClass); for (int i = 0; i < columnCount; i++) { if (columnLabels[i] == null) { continue; } if (RefUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } resultList.add((T) entity); } } }); return resultList; } catch ( SQLException e) { throw new UncheckedSQLException(e); } finally { IOUtil.closeQuietly(iterators); } } /** * @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); } @SafeVarargs public final NullabLe queryForSingleResult(final Class targetClass, final String sql, final Object... parameters) { return queryForSingleResult(targetClass, sql, null, null, parameters); } public 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") public 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); } public 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") public 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); } @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 */ public 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); } public 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 */ public 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) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sql, statementSetter, newJdbcSettings, parameters); try (final Stream stream = (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)).skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount())) { final NamedSQL namedSQL = getNamedSQL(sql); final ResultSet rs = iterators.get(0).resultSet(); final List columnLabelList = getColumnLabelList(namedSQL.getPureSQL(), 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<>()); } stream.forEach(new Consumer() { @Override public void accept(Object[] a) { for (int i = 0; i < columnCount; i++) { columnList.get(i).add(a[i]); } } }); return new RowDataSet(columnNameList, columnList); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { IOUtil.closeQuietly(iterators); } } @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) { if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sqls, statementSetter, newJdbcSettings, parameters); try (final Stream stream = (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)).skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount())) { final NamedSQL namedSQL = getNamedSQL(sqls.get(0)); final ResultSet rs = iterators.get(0).resultSet(); final List columnLabelList = getColumnLabelList(namedSQL.getPureSQL(), 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<>()); } stream.forEach(new Consumer() { @Override public void accept(Object[] a) { for (int i = 0; i < columnCount; i++) { columnList.get(i).add(a[i]); } } }); return new RowDataSet(columnNameList, columnList); } catch (SQLException e) { throw new UncheckedSQLException(e); } finally { IOUtil.closeQuietly(iterators); } } // // 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); // } RowIterator iterate(final String sql, final Object... parameters) { return iterate(sql, null, parameters); } RowIterator iterate(final String sql, final StatementSetter statementSetter, final Object... parameters) { return iterate(sql, statementSetter, null, parameters); } /** * Remember to close the returned RowIterator to close the underlying ResultSet. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ RowIterator iterate(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return iterate(null, sql, statementSetter, jdbcSettings, parameters); } RowIterator iterate(final Connection conn, final String sql, final Object... parameters) { return iterate(conn, sql, null, parameters); } RowIterator iterate(final Connection conn, final String sql, final StatementSetter statementSetter, final Object... parameters) { return iterate(conn, sql, statementSetter, null, parameters); } /** * Remember to close the returned RowIterator to close the underlying ResultSet. * * @param conn * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ RowIterator iterate(final Connection conn, final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if RowIterator is closed. that means the specified connection can't be held independently. return query(conn, sql, statementSetter, ROW_ITERATOR_RESULT_SET_EXTRACTOR, jdbcSettings, parameters); } List iterateAll(final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(sql, null, jdbcSettings, parameters); } /** * Remember to close the returned RowIterator list to close the underlying ResultSet list. * * @param sql * @param statementSetter * @param jdbcSettings * @param parameters * @return */ List iterateAll(final String sql, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(null, sql, statementSetter, jdbcSettings, parameters); } List iterateAll(final Connection conn, final String sql, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(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 */ List iterateAll(final Connection conn, final String sql, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { if (jdbcSettings != null && (jdbcSettings.getOffset() != 0 || jdbcSettings.getCount() != Long.MAX_VALUE)) { throw new IllegalArgumentException("Can't specify 'offset' or 'count' for iterateAll method"); } if (jdbcSettings == null) { jdbcSettings = _jdbcSettings; } if (jdbcSettings == null || N.isNullOrEmpty(jdbcSettings.getQueryWithDataSources())) { return N.asList(iterate(conn, sql, statementSetter, jdbcSettings, parameters)); } else { final List iterators = new ArrayList<>(); final Holder errorHolder = new Holder<>(); final AtomicInteger activeThreadNum = new AtomicInteger(0); for (String dataSource : jdbcSettings.getQueryWithDataSources()) { final JdbcSettings newJdbcSettings = jdbcSettings == null ? _jdbcSettings.copy() : jdbcSettings.copy(); newJdbcSettings.setOffset(0).setCount(Long.MAX_VALUE); newJdbcSettings.setQueryWithDataSource(dataSource); final Runnable cmd = new Runnable() { @Override public void run() { try { if (errorHolder.value() != null) { return; } final RowIterator tmp = iterate(conn, sql, statementSetter, newJdbcSettings, parameters); synchronized (iterators) { iterators.add(tmp); } } catch (Throwable e) { if (JdbcUtil.isTableNotExistsException(e)) { // ignore; } else { setException(errorHolder, e); } } finally { activeThreadNum.decrementAndGet(); } } }; activeThreadNum.incrementAndGet(); if (newJdbcSettings.isQueryInParallel()) { _asyncExecutor.execute(cmd); } else { cmd.run(); } } while (activeThreadNum.get() > 0) { N.sleep(1); } if (errorHolder.value() != null) { try { throw N.toRuntimeException(errorHolder.value()); } finally { IOUtil.closeQuietly(iterators); } } return iterators; } } List iterateAll(final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(sqls, null, jdbcSettings, parameters); } /** * Remember to close the returned RowIterator list to close the underlying ResultSet list. * * @param sqls * @param statementSetter * @param jdbcSettings * @param parameters * @return */ List iterateAll(final List sqls, final StatementSetter statementSetter, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(null, sqls, statementSetter, jdbcSettings, parameters); } List iterateAll(final Connection conn, final List sqls, final JdbcSettings jdbcSettings, final Object... parameters) { return iterateAll(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 */ List iterateAll(final Connection conn, final List sqls, final StatementSetter statementSetter, JdbcSettings jdbcSettings, final Object... parameters) { // TODO the specified connection will be closed if RowIterator is closed. that means the specified connection can't be held independently. N.checkNullOrEmpty(sqls, "sqls"); if (jdbcSettings != null && (jdbcSettings.getOffset() != 0 || jdbcSettings.getCount() != Long.MAX_VALUE)) { throw new IllegalArgumentException("Can't specify 'offset' or 'count' for iterateAll method"); } if (jdbcSettings == null) { jdbcSettings = _jdbcSettings; } final List iterators = new ArrayList<>(); final Holder errorHolder = new Holder<>(); final AtomicInteger activeThreadNum = new AtomicInteger(0); for (String e : sqls) { final String sql = e; final JdbcSettings newJdbcSettings = jdbcSettings == null ? _jdbcSettings.copy() : jdbcSettings.copy(); newJdbcSettings.setOffset(0).setCount(Long.MAX_VALUE); final Runnable cmd = new Runnable() { @Override public void run() { try { if (errorHolder.value() != null) { return; } final List tmp = iterateAll(conn, sql, statementSetter, newJdbcSettings, parameters); synchronized (iterators) { iterators.addAll(tmp); } } catch (Throwable e) { if (JdbcUtil.isTableNotExistsException(e)) { // ignore; } else { setException(errorHolder, e); } } finally { activeThreadNum.decrementAndGet(); } } }; activeThreadNum.incrementAndGet(); if (newJdbcSettings.isQueryInParallel()) { _asyncExecutor.execute(cmd); } else { cmd.run(); } } while (activeThreadNum.get() > 0) { N.sleep(1); } if (errorHolder.value() != null) { try { throw N.toRuntimeException(errorHolder.value()); } finally { IOUtil.closeQuietly(iterators); } } return iterators; } @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. final RowIterator iterator = this.iterate(conn, sql, statementSetter, jdbcSettings, parameters); return Stream.of(iterator).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterator); } }).tried(); } @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 */ public 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. if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sql, statementSetter, newJdbcSettings, parameters); return (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)) .skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount()).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterators); } }).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 */ public 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. if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sqls, statementSetter, newJdbcSettings, parameters); return (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)) .skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount()).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterators); } }).tried(); } @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 */ public 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. final RowIterator iterator = this.iterate(conn, sql, statementSetter, jdbcSettings, parameters); 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 Stream.of(iterator).map(new Function() { @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 (RefUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } return (T) entity; } } }).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterator); } }).tried(); } catch (SQLException e) { IOUtil.closeQuietly(iterator); throw new UncheckedSQLException(e); } } @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 */ public 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. if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sql, statementSetter, newJdbcSettings, parameters); try { final NamedSQL namedSQL = getNamedSQL(sql); final ResultSet rs = iterators.get(0).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 (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)) .skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount()).map(new Function() { @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 (RefUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } return (T) entity; } } }).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterators); } }).tried(); } catch (SQLException e) { IOUtil.closeQuietly(iterators); throw new UncheckedSQLException(e); } } @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 */ public 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. if (jdbcSettings == null) { jdbcSettings = _jdbcSettings.copy(); } final JdbcSettings newJdbcSettings = jdbcSettings.copy().setOffset(0).setCount(Long.MAX_VALUE); final List iterators = this.iterateAll(conn, sqls, statementSetter, newJdbcSettings, parameters); try { final NamedSQL namedSQL = getNamedSQL(sqls.get(0)); final ResultSet rs = iterators.get(0).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 (jdbcSettings.isQueryInParallel() ? Stream.parallelConcat2(iterators, iterators.size()) : Stream.concat2(iterators)) .skip(jdbcSettings.getOffset()).limit(jdbcSettings.getCount()).map(new Function() { @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 (RefUtil.setPropValue(entity, columnLabels[i], a[i], true) == false) { columnLabels[i] = null; } } if (isDirtyMarker) { ((DirtyMarker) entity).markDirty(false); } return (T) entity; } } }).onClose(new Runnable() { private volatile boolean isClosed = false; @Override public void run() { if (isClosed) { return; } isClosed = true; IOUtil.closeQuietly(iterators); } }).tried(); } catch (SQLException e) { IOUtil.closeQuietly(iterators); throw new UncheckedSQLException(e); } } private void setException(final Holder errorHolder, Throwable e) { synchronized (errorHolder) { if (errorHolder.value() == null) { errorHolder.setValue(e); } else { errorHolder.value().addSuppressed(e); } } } /** * 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 */ public 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 */ public 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 */ public 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 */ public 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 */ public void closeQuietly(final ResultSet rs, final PreparedStatement stmt, final Connection conn) { closeQuietly(rs, stmt, conn, 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.asOptions(Query.QUERY_WITH_DATA_SOURCE, jdbcSettings.getQueryWithDataSource())); } } } protected DataSource getDataSource(final String sql, final List batchParameters, 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, batchParameters, null); } else { return _dss.select(_dsm, null, sql, batchParameters, N.asOptions(Query.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; } 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); } protected static List getColumnLabelList(final String sql, final ResultSet rs) throws SQLException { List labelList = _sqlColumnLabelPool.get(sql); if (labelList == null) { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); labelList = new ArrayList<>(columnCount); for (int i = 1, n = columnCount + 1; i < n; i++) { labelList.add(metaData.getColumnLabel(i)); } // int fromIndex = SQLParser.indexWord(sql, D.FROM, 0, false); // // boolean isCachable = true; // for (int i = 6; i < fromIndex; i++) { // char ch = sql.charAt(i); // // if (ch == '*' && (sql.charAt(i - 1) != '(' || sql.charAt(i + 1) != ')')) { // isCachable = false; // // break; // } // } // // if (isCachable) { if (sql.length() < 4098) { labelList = ImmutableList.of(labelList); if (_sqlColumnLabelPool.size() >= SQL_CACHE_SIZE) { _sqlColumnLabelPool.remove(_sqlColumnLabelPool.keySet().iterator().next()); } _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 { static final List EXISTS_SELECT_PROP_NAMES = ImmutableList.of(NE._1); static final List COUNT_SELECT_PROP_NAMES = ImmutableList.of(NE.COUNT_ALL); static final Map, String> entityIdMap = new ConcurrentHashMap<>(); static final Map, Set> readOnlyPropNamesMap = new ConcurrentHashMap<>(); static final Map, Set> readOrWriteOnlyPropNamesMap = new ConcurrentHashMap<>(); private final Class targetClass; private final SQLExecutor sqlExecutor; private final NamingPolicy namingPolicy; private final AsyncExecutor asyncExecutor; private final String idName; private final String sql_get_by_id; private final String sql_delete_by_id; // TODO cache more sqls to improve performance. Mapper(final Class targetClass, final SQLExecutor sqlExecutor, final NamingPolicy namingPolicy) { this.targetClass = targetClass; this.sqlExecutor = sqlExecutor; this.namingPolicy = namingPolicy; this.asyncExecutor = sqlExecutor._asyncExecutor; this.idName = Maps.getOrDefault(entityIdMap, targetClass, ID); this.sql_get_by_id = this.prepareQuery(null, L.eq(idName)).sql; this.sql_delete_by_id = this.prepareDelete(L.eq(idName)).sql; } public static void registerEntityId(Class targetClass, String idName) { N.checkArgument(N.isEntity(targetClass), RefUtil.getCanonicalClassName(targetClass) + " is not an entity class with getter/setter methods"); entityIdMap.put(targetClass, RefUtil.getPropNameByMethod(RefUtil.getPropGetMethod(targetClass, idName))); } /** * The properties will be excluded by add/addAll/batchAdd and update/updateAll/batchUpdate operations if the input is an entity. * * @param targetClass * @param readOnlyPropNames */ public static void registerReadOnlyProps(Class targetClass, Collection readOnlyPropNames) { N.checkArgument(N.isEntity(targetClass), RefUtil.getCanonicalClassName(targetClass) + " is not an entity class with getter/setter methods"); N.checkNullOrEmpty(readOnlyPropNames, "'readOnlyPropNames'"); final Set set = new HashSet(); for (String propName : readOnlyPropNames) { set.add(RefUtil.getPropNameByMethod(RefUtil.getPropGetMethod(targetClass, propName))); } readOnlyPropNamesMap.put(targetClass, set); if (readOrWriteOnlyPropNamesMap.containsKey(targetClass)) { readOrWriteOnlyPropNamesMap.get(targetClass).addAll(set); } else { readOrWriteOnlyPropNamesMap.put(targetClass, new HashSet<>(set)); } } /** * The properties will be ignored by update/updateAll/batchUpdate operations if the input is an entity. * * @param targetClass * @param writeOnlyPropNames */ public static void registerWriteOnlyProps(Class targetClass, Collection writeOnlyPropNames) { N.checkArgument(N.isEntity(targetClass), RefUtil.getCanonicalClassName(targetClass) + " is not an entity class with getter/setter methods"); N.checkNullOrEmpty(writeOnlyPropNames, "'writeOnlyPropNames'"); final Set set = new HashSet(); for (String propName : writeOnlyPropNames) { set.add(RefUtil.getPropNameByMethod(RefUtil.getPropGetMethod(targetClass, propName))); } if (readOrWriteOnlyPropNamesMap.containsKey(targetClass)) { readOrWriteOnlyPropNamesMap.get(targetClass).addAll(set); } else { readOrWriteOnlyPropNamesMap.put(targetClass, new HashSet<>(set)); } } public boolean exists(final Object id) { return exists(L.eq(idName, id)); } public boolean exists(final Condition whereCause) { return exists(null, whereCause); } 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()); } public T get(final Object id) { return get(id, (Collection) null); } @SafeVarargs public final T get(final Object id, final String... selectPropNames) { return get(id, Arrays.asList(selectPropNames)); } public T get(final Object id, final Collection selectPropNames) { return get(null, id, selectPropNames); } public T get(final Connection conn, final Object id, final Collection selectPropNames) { 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, L.eq(idName), 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); } } 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 Connection conn, final Collection selectPropNames, final Condition whereCause) { return find(conn, 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, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.find(targetClass, conn, 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 Connection conn, final Collection selectPropNames, final Condition whereCause) { return query(conn, 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, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause); return sqlExecutor.query(conn, pair.sql, null, 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 queryForSingleResult(final Class targetValueClass, final String propName, final Object id) { return queryForSingleResult(targetValueClass, propName, L.eq(idName, id)); } 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 Connection conn, final String propName, final Condition whereCause) { return queryForSingleResult(targetValueClass, conn, 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, 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 Connection conn, final Collection selectPropNames, final Condition whereCause) { return queryForEntity(conn, 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, final JdbcSettings jdbcSettings) { final SP pair = prepareQuery(selectPropNames, whereCause, 1); return sqlExecutor.queryForEntity(targetClass, 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 Connection conn, final Collection selectPropNames, final Condition whereCause) { return stream(conn, selectPropNames, whereCause, null); } public Try> stream(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return stream(null, selectPropNames, whereCause, jdbcSettings); } public 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()); } private SP prepareQuery(final Collection selectPropNames, final Condition whereCause) { return prepareQuery(selectPropNames, whereCause, 0); } private SP prepareQuery(final Collection selectPropNames, final Condition whereCause, final int count) { 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 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) { 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 E add(final Object entity) { return add(null, entity); } public E add(final Connection conn, final Object entity) { final SP pair = prepareAdd(entity); final E id = sqlExecutor.insert(conn, pair.sql, pair.parameters.toArray()); postAdd(entity, id); return id; } /** * * @param props * @return the auto-generated id or null if there is no auto-generated id. */ public E add(final Map props) { return add(null, props); } /** * @param conn * @param props * @return the auto-generated id or null if there is no auto-generated id. */ public E add(final Connection conn, final Map 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. */ 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. */ public List addAll(final Collection entities, final IsolationLevel isolationLevel) { 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((E) add(conn, entity)); } isOk = true; } finally { if (tran != null) { if (isOk) { tran.commit(); } else { tran.rollback(); } } } return result; } public List addAll(final Connection conn, final Collection entities) { final List result = new ArrayList<>(entities.size()); for (Object entity : entities) { result.add((E) 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 Connection conn, final Collection entities) { return batchAdd(conn, entities, JdbcSettings.DEFAULT_BATCH_SIZE); } public List batchAdd(final Collection entities, int batchSize) { return batchAdd(null, entities, batchSize); } /** * 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, int batchSize) { N.checkArgument(batchSize > 0, "Invalid batch size: %s", batchSize); final SP pair = prepareAdd(entities.iterator().next()); final JdbcSettings jdbcSettings = batchSize == JdbcSettings.DEFAULT_BATCH_SIZE ? null : JdbcSettings.create().setBatchSize(batchSize); final List batchParameters = entities instanceof List ? (List) entities : new ArrayList<>(entities); final List ids = sqlExecutor.batchInsert(conn, pair.sql, null, jdbcSettings, batchParameters); 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 Object entity) { checkEntity(entity); final Class cls = entity.getClass(); final boolean isEntity = N.isEntity(cls); final Set readOnlyPropNames = isEntity ? readOnlyPropNamesMap.get(cls) : null; final Map props = entity instanceof Map ? (Map) entity : (readOnlyPropNames == null ? Maps.entity2Map(entity) : Maps.entity2Map(entity, readOnlyPropNames)); return prepareAdd(props); } private SP prepareAdd(final Map props) { 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 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())) { RefUtil.setPropValue(entity, idName, idVal); } if (entity instanceof DirtyMarker) { ((DirtyMarker) entity).dirtyPropNames().clear(); } } public int update(final Object entity) { return update(null, entity); } public int update(final Connection conn, final Object entity) { final SP pair = prepareUpdate(entity); final int updateCount = sqlExecutor.update(conn, pair.sql, pair.parameters.toArray()); if (updateCount > 0) { postUpdate(entity); } return updateCount; } public int update(final Object id, final Map props) { return update(L.eq(idName, id), props); } public int update(final Connection conn, final Object id, final Map props) { return update(conn, L.eq(idName, id), props); } public int update(final Condition whereCause, final Map props) { return update(null, whereCause, props); } public int update(final Connection conn, final Condition whereCause, final Map props) { 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 */ public int updateAll(final Collection entities) { return updateAll(entities, IsolationLevel.DEFAULT); } /** * Update All the records one by one in transaction. * * @param entities * @param isolationLevel * @return */ public int updateAll(final Collection entities, final IsolationLevel isolationLevel) { 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); } isOk = true; } finally { if (tran != null) { if (isOk) { tran.commit(); } else { tran.rollback(); } } } return result; } public int updateAll(final Connection conn, final Collection entities) { int result = 0; for (Object entity : entities) { result += update(conn, entity); } 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, JdbcSettings.DEFAULT_BATCH_SIZE); } public int batchUpdate(final Connection conn, final Collection entities) { return batchUpdate(conn, entities, JdbcSettings.DEFAULT_BATCH_SIZE); } /** * Update All the records by batch operation. * * @param entities which must have the same updated properties set. * @param batchSize Default value is 200. * @return */ public int batchUpdate(final Collection entities, int batchSize) { return batchUpdate(null, entities, batchSize); } public int batchUpdate(final Connection conn, final Collection entities, int batchSize) { N.checkArgument(batchSize > 0, "Invalid batch size: %s", batchSize); final SP pair = prepareUpdate(entities.iterator().next()); final JdbcSettings jdbcSettings = batchSize == JdbcSettings.DEFAULT_BATCH_SIZE ? null : JdbcSettings.create().setBatchSize(batchSize); final List batchParameters = entities instanceof List ? (List) entities : new ArrayList<>(entities); final int updateCount = sqlExecutor.batchUpdate(conn, pair.sql, null, jdbcSettings, batchParameters); if (updateCount > 0) { for (Object entity : entities) { postUpdate(entity); } } return updateCount; } @SuppressWarnings("deprecation") private SP prepareUpdate(final Object entity) { checkEntity(entity); final Class cls = entity.getClass(); final boolean isDirtyMarker = N.isDirtyMarker(cls); final Set readOrWriteOnlyPropNames = readOrWriteOnlyPropNamesMap.get(cls); Map props = null; if (isDirtyMarker) { props = new HashMap<>(); for (String propName : ((DirtyMarker) entity).dirtyPropNames()) { props.put(propName, RefUtil.getPropValue(entity, propName)); } if (N.notNullOrEmpty(readOrWriteOnlyPropNames)) { Maps.removeAll(props, readOrWriteOnlyPropNames); } } else { props = readOrWriteOnlyPropNames == null ? Maps.entity2Map(entity) : Maps.entity2Map(entity, readOrWriteOnlyPropNames); } final Object idVal = props.containsKey(idName) ? props.remove(idName) : getId(entity); if (idVal == null) { throw new IllegalArgumentException("No id property found in Class: " + RefUtil.getCanonicalClassName(entity.getClass()) + " with name: " + idName + ". Please register the id property first by calling 'registerEntityId'"); } return prepareUpdate(L.eq(idName, idVal), props); } private SP prepareUpdate(final Condition whereCause, final Map props) { 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 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) { if (entity instanceof DirtyMarker) { ((DirtyMarker) entity).dirtyPropNames().clear(); } } public int delete(final Object idOrEntity) { return delete(null, idOrEntity); } public int delete(final Connection conn, final Object idOrEntity) { checkEntity(idOrEntity); final Class cls = idOrEntity.getClass(); final Object idVal = N.isEntity(cls) ? getId(idOrEntity) : idOrEntity; return delete(conn, L.eq(idName, idVal)); } public int delete(final Condition whereCause) { return delete(null, whereCause); } public int delete(final Connection conn, final Condition whereCause) { if (whereCause instanceof Equal && ((Equal) whereCause).getPropName().equals(idName)) { 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 */ 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 */ public int deleteAll(final Collection idsOrEntities, final IsolationLevel isolationLevel) { 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; } public int deleteAll(final Connection conn, final Collection idsOrEntities) { int result = 0; for (Object idOrEntity : idsOrEntities) { result += delete(conn, idOrEntity); } return result; } 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 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), "Dlete wrong type: " + RefUtil.getCanonicalClassName(cls) + " in " + toString()); } } private Object getId(final Object entity) { final Class cls = entity.getClass(); final Method getMethod = RefUtil.getPropGetMethod(cls, idName); if (getMethod == null) { throw new IllegalArgumentException("No id property found in Class: " + RefUtil.getCanonicalClassName(cls) + " with name: " + idName + ". Please register the id property first by calling 'registerEntityId'"); } return RefUtil.getPropValue(entity, getMethod); } public CompletableFuture asyncExists(final Object id) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return exists(id); } }); } public CompletableFuture asyncExists(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return exists(whereCause); } }); } public CompletableFuture asyncExists(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Boolean call() throws Exception { return exists(conn, whereCause); } }); } public CompletableFuture asyncCount(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return count(whereCause); } }); } public CompletableFuture asyncCount(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return count(conn, whereCause); } }); } public CompletableFuture asyncGet(final Object id) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return get(id); } }); } @SafeVarargs public final CompletableFuture asyncGet(final Object id, final String... selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return get(id, selectPropNames); } }); } public CompletableFuture asyncGet(final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return get(id, selectPropNames); } }); } public CompletableFuture asyncGet(final Connection conn, final Object id, final Collection selectPropNames) { return asyncExecutor.execute(new Callable() { @Override public T call() throws Exception { return get(conn, id, selectPropNames); } }); } public CompletableFuture> asyncFind(final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return find(whereCause); } }); } public CompletableFuture> asyncFind(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return find(selectPropNames, whereCause); } }); } public CompletableFuture> asyncFind(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return find(conn, selectPropNames, whereCause); } }); } public CompletableFuture> asyncFind(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return find(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> asyncFind(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return find(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture asyncQuery(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(whereCause); } }); } public CompletableFuture asyncQuery(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(selectPropNames, whereCause); } }); } public CompletableFuture asyncQuery(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(conn, selectPropNames, whereCause); } }); } public CompletableFuture asyncQuery(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture asyncQuery(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable() { @Override public DataSet call() throws Exception { return query(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture asyncQueryForBoolean(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalBoolean call() throws Exception { return queryForBoolean(propName, whereCause); } }); } public CompletableFuture asyncQueryForByte(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalByte call() throws Exception { return queryForByte(propName, whereCause); } }); } public CompletableFuture asyncQueryForShort(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalShort call() throws Exception { return queryForShort(propName, whereCause); } }); } public CompletableFuture asyncQueryForInt(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalInt call() throws Exception { return queryForInt(propName, whereCause); } }); } public CompletableFuture asyncQueryForLong(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalLong call() throws Exception { return queryForLong(propName, whereCause); } }); } public CompletableFuture asyncQueryForFloat(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalFloat call() throws Exception { return queryForFloat(propName, whereCause); } }); } public CompletableFuture asyncQueryForDouble(final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public OptionalDouble call() throws Exception { return queryForDouble(propName, whereCause); } }); } public CompletableFuture> asyncQueryForSingleResult(final Class targetValueClass, final String propName, final Object id) { return asyncExecutor.execute(new Callable>() { @Override public NullabLe call() throws Exception { return queryForSingleResult(targetValueClass, propName, id); } }); } public CompletableFuture> asyncQueryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public NullabLe call() throws Exception { return queryForSingleResult(targetValueClass, propName, whereCause); } }); } public CompletableFuture> asyncQueryForSingleResult(final Class targetValueClass, final Connection conn, final String propName, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public NullabLe call() throws Exception { return queryForSingleResult(targetValueClass, conn, propName, whereCause); } }); } public CompletableFuture> asyncQueryForSingleResult(final Class targetValueClass, final String propName, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public NullabLe call() throws Exception { return queryForSingleResult(targetValueClass, propName, whereCause, jdbcSettings); } }); } public CompletableFuture> asyncQueryForSingleResult(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 queryForSingleResult(targetValueClass, conn, propName, whereCause, jdbcSettings); } }); } public CompletableFuture> asyncQueryForEntity(final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return queryForEntity(whereCause); } }); } public CompletableFuture> asyncQueryForEntity(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return queryForEntity(selectPropNames, whereCause); } }); } public CompletableFuture> asyncQueryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return queryForEntity(conn, selectPropNames, whereCause); } }); } public CompletableFuture> asyncQueryForEntity(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return queryForEntity(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture> asyncQueryForEntity(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>() { @Override public Optional call() throws Exception { return queryForEntity(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture>> asyncStream(final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return stream(whereCause); } }); } public CompletableFuture>> asyncStream(final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return stream(selectPropNames, whereCause); } }); } public CompletableFuture>> asyncStream(final Connection conn, final Collection selectPropNames, final Condition whereCause) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return stream(conn, selectPropNames, whereCause); } }); } public CompletableFuture>> asyncStream(final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return stream(selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture>> asyncStream(final Connection conn, final Collection selectPropNames, final Condition whereCause, final JdbcSettings jdbcSettings) { return asyncExecutor.execute(new Callable>>() { @Override public Try> call() throws Exception { return stream(conn, selectPropNames, whereCause, jdbcSettings); } }); } public CompletableFuture asyncAdd(final Object entity) { return asyncExecutor.execute(new Callable() { @Override public E call() throws Exception { return add(entity); } }); } public CompletableFuture asyncAdd(final Connection conn, final Object entity) { return asyncExecutor.execute(new Callable() { @Override public E call() throws Exception { return add(conn, entity); } }); } public CompletableFuture asyncAdd(final Map props) { return asyncExecutor.execute(new Callable() { @Override public E call() throws Exception { return add(props); } }); } public CompletableFuture asyncAdd(final Connection conn, final Map props) { return asyncExecutor.execute(new Callable() { @Override public E call() throws Exception { return add(conn, props); } }); } public CompletableFuture> asyncAddAll(final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return addAll(entities); } }); } public CompletableFuture> asyncAddAll(final Collection entities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return addAll(entities, isolationLevel); } }); } public CompletableFuture> asyncAddAll(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return addAll(conn, entities); } }); } public CompletableFuture> asyncBatchAdd(final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return batchAdd(entities); } }); } public CompletableFuture> asyncBatchAdd(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return batchAdd(conn, entities); } }); } public CompletableFuture> asyncBatchAdd(final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return batchAdd(entities, batchSize); } }); } public CompletableFuture> asyncBatchAdd(final Connection conn, final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable>() { @Override public List call() throws Exception { return batchAdd(conn, entities, batchSize); } }); } public CompletableFuture asyncUpdate(final Object entity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(entity); } }); } public CompletableFuture asyncUpdate(final Connection conn, final Object entity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(conn, entity); } }); } public CompletableFuture asyncUpdate(final Object id, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(id, props); } }); } public CompletableFuture asyncUpdate(final Connection conn, final Object id, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(conn, id, props); } }); } public CompletableFuture asyncUpdate(final Condition whereCause, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(whereCause, props); } }); } public CompletableFuture asyncUpdate(final Connection conn, final Condition whereCause, final Map props) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return update(conn, whereCause, props); } }); } public CompletableFuture asyncUpdateAll(final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return updateAll(entities); } }); } public CompletableFuture asyncUpdateAll(final Collection entities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return updateAll(entities, isolationLevel); } }); } public CompletableFuture asyncUpdateAll(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return updateAll(conn, entities); } }); } public CompletableFuture asyncBatchUpdate(final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return batchUpdate(entities); } }); } public CompletableFuture asyncBatchUpdate(final Connection conn, final Collection entities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return batchUpdate(conn, entities); } }); } public CompletableFuture asyncBatchUpdate(final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return batchUpdate(entities, batchSize); } }); } public CompletableFuture asyncBatchUpdate(final Connection conn, final Collection entities, final int batchSize) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return batchUpdate(conn, entities, batchSize); } }); } public CompletableFuture asyncDelete(final Object idOrEntity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return delete(idOrEntity); } }); } public CompletableFuture asyncDelete(final Connection conn, final Object idOrEntity) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return delete(conn, idOrEntity); } }); } public CompletableFuture asyncDelete(final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return delete(whereCause); } }); } public CompletableFuture asyncDelete(final Connection conn, final Condition whereCause) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return delete(conn, whereCause); } }); } public CompletableFuture asyncDeleteAll(final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return deleteAll(idsOrEntities); } }); } public CompletableFuture asyncDeleteAll(final Collection idsOrEntities, final IsolationLevel isolationLevel) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return deleteAll(idsOrEntities, isolationLevel); } }); } public CompletableFuture asyncDeleteAll(final Connection conn, final Collection idsOrEntities) { return asyncExecutor.execute(new Callable() { @Override public Integer call() throws Exception { return deleteAll(conn, idsOrEntities); } }); } public String toStirng() { return "Mapper: " + RefUtil.getCanonicalClassName(targetClass); } } /** * 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 public 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 = RefUtil.getPropGetMethod(clazz, namedParameters.get(i)); if (propGetMethod == null) { throw new IllegalArgumentException("Parameter for property '" + namedParameters.get(i) + "' is missed"); } a[i] = RefUtil.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_QUERY_TIMEOUT = 0; public static final int DEFAULT_NO_GENERATED_KEYS = Statement.NO_GENERATED_KEYS; public static final int DEFAULT_MAX_ROWS = 0; public static final int DEFAULT_MAX_FIELD_SIZE = 0; public static final int DEFAULT_FETCH_SIZE = 0; 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; public static final long DEFAULT_OFFSET = 0; public static final long DEFAULT_COUNT = Long.MAX_VALUE; 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 = DEFAULT_OFFSET; private long count = DEFAULT_COUNT; private String generatedIdPropName = null; private String queryWithDataSource; private Collection queryWithDataSources; private boolean queryInParallel = true; 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; } 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); 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); } 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 + "}"; } } /** * * @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); } } } }