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: 3.8.5
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.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.Driver;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

import com.landawn.abacus.DataSet;
import com.landawn.abacus.DataSourceManager;
import com.landawn.abacus.DataSourceSelector;
import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.EntityId;
import com.landawn.abacus.IsolationLevel;
import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.condition.ConditionFactory.CF;
import com.landawn.abacus.core.DirtyMarkerUtil;
import com.landawn.abacus.core.Seid;
import com.landawn.abacus.dataSource.SQLDataSource;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.EntityInfo;
import com.landawn.abacus.parser.ParserUtil.PropInfo;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.type.TypeFactory;
import com.landawn.abacus.util.Columns.ColumnOne;
import com.landawn.abacus.util.ExceptionalStream.StreamE;
import com.landawn.abacus.util.Fn.IntFunctions;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.JdbcUtil.BiParametersSetter;
import com.landawn.abacus.util.JdbcUtil.BiRowMapper;
import com.landawn.abacus.util.JdbcUtil.RowExtractor;
import com.landawn.abacus.util.JdbcUtil.RowFilter;
import com.landawn.abacus.util.SQLBuilder.NAC;
import com.landawn.abacus.util.SQLBuilder.NLC;
import com.landawn.abacus.util.SQLBuilder.NSC;
import com.landawn.abacus.util.SQLBuilder.PAC;
import com.landawn.abacus.util.SQLBuilder.PLC;
import com.landawn.abacus.util.SQLBuilder.PSC;
import com.landawn.abacus.util.SQLBuilder.SP;
import com.landawn.abacus.util.SQLTransaction.CreatedBy;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.Tuple.Tuple2;
import com.landawn.abacus.util.Tuple.Tuple3;
import com.landawn.abacus.util.u.Nullable;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalBoolean;
import com.landawn.abacus.util.u.OptionalByte;
import com.landawn.abacus.util.u.OptionalChar;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalFloat;
import com.landawn.abacus.util.u.OptionalInt;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.u.OptionalShort;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.BinaryOperator;
import com.landawn.abacus.util.function.Consumer;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.stream.Collector;
import com.landawn.abacus.util.stream.ObjIteratorEx;
import com.landawn.abacus.util.stream.Stream;
import com.landawn.abacus.util.stream.Stream.StreamEx;

/**
 * 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 Object[]/List parameters are supported for parameterized SQL({@code id = ?}). * Parameters with format of Object[]/List/Map/Entity are supported for named parameterized SQL({@code id = :id}). * DO NOT use primitive array {@code boolean[]/char[]/byte[]/short[]/int[]/long[]/float[]/double[]} for passing multiple 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.findFirst(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.findFirst(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.
    *
    * * Transaction can be started: *
     * 
     * final SQLTransaction tran = sqlExecutor.beginTransaction(IsolationLevel.READ_COMMITTED);
     *
     * try {
     *     // sqlExecutor.insert(...);
     *     // sqlExecutor.update(...);
     *     // sqlExecutor.query(...);
     *
     *     tran.commit();
     * } finally {
     *     // The connection will be automatically closed after the transaction is committed or rolled back.
     *     tran.rollbackIfNotCommitted();
     * }
     * 
     * 
    * * * Spring Transaction is also supported and Integrated. * If a method of this class is called where a Spring transaction is started with the {@code DataSource} inside this {@code SQLExecutor}, without {@code Connection} parameter specified, * the {@code Connection} started the Spring Transaction will be used. Otherwise a {@code Connection} directly from the inside {@code DataSource}(Connection pool) will be borrowed and used. * * * SQLExecutor is tread-safe.

    * * @author Haiyang Li * @see JdbcUtil * @see com.landawn.abacus.annotation.ReadOnly * @see com.landawn.abacus.annotation.ReadOnlyId * @see com.landawn.abacus.annotation.NonUpdatable * @see com.landawn.abacus.annotation.Transient * @see com.landawn.abacus.annotation.Table * @see com.landawn.abacus.annotation.Column * @see com.landawn.abacus.condition.ConditionFactory * @see com.landawn.abacus.condition.ConditionFactory.CF * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html * @see http://docs.oracle.com/javase/8/docs/api/java/sql/ResultSet.html * @since 0.8 */ public class SQLExecutor { /** The Constant logger. */ private static final Logger logger = LoggerFactory.getLogger(SQLExecutor.class); /** The Constant ID. */ static final String ID = "id"; /** The Constant QUERY_WITH_DATA_SOURCE. */ static final String QUERY_WITH_DATA_SOURCE = "queryWithDataSource"; /** The Constant EXISTS_RESULT_SET_EXTRACTOR. */ private static final ResultExtractor EXISTS_RESULT_SET_EXTRACTOR = new ResultExtractor() { @Override public Boolean apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); return rs.next(); } }; /** The Constant COUNT_RESULT_SET_EXTRACTOR. */ private static final ResultExtractor COUNT_RESULT_SET_EXTRACTOR = new ResultExtractor() { @Override public Integer apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); int cnt = 0; while (rs.next()) { cnt++; } return cnt; } }; /** The Constant SINGLE_BOOLEAN_EXTRACTOR. */ private static final ResultExtractor SINGLE_BOOLEAN_EXTRACTOR = new ResultExtractor() { @Override public OptionalBoolean apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalBoolean.of(rs.getBoolean(1)); } return OptionalBoolean.empty(); } }; /** The Constant charType. */ private static final Type charType = TypeFactory.getType(char.class); /** The Constant SINGLE_CHAR_EXTRACTOR. */ private static final ResultExtractor SINGLE_CHAR_EXTRACTOR = new ResultExtractor() { @Override public OptionalChar apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalChar.of(charType.get(rs, 1)); } return OptionalChar.empty(); } }; /** The Constant SINGLE_BYTE_EXTRACTOR. */ private static final ResultExtractor SINGLE_BYTE_EXTRACTOR = new ResultExtractor() { @Override public OptionalByte apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalByte.of(rs.getByte(1)); } return OptionalByte.empty(); } }; /** The Constant SINGLE_SHORT_EXTRACTOR. */ private static final ResultExtractor SINGLE_SHORT_EXTRACTOR = new ResultExtractor() { @Override public OptionalShort apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalShort.of(rs.getShort(1)); } return OptionalShort.empty(); } }; /** The Constant SINGLE_INT_EXTRACTOR. */ private static final ResultExtractor SINGLE_INT_EXTRACTOR = new ResultExtractor() { @Override public OptionalInt apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalInt.of(rs.getInt(1)); } return OptionalInt.empty(); } }; /** The Constant SINGLE_LONG_EXTRACTOR. */ private static final ResultExtractor SINGLE_LONG_EXTRACTOR = new ResultExtractor() { @Override public OptionalLong apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalLong.of(rs.getLong(1)); } return OptionalLong.empty(); } }; /** The Constant SINGLE_FLOAT_EXTRACTOR. */ private static final ResultExtractor SINGLE_FLOAT_EXTRACTOR = new ResultExtractor() { @Override public OptionalFloat apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalFloat.of(rs.getFloat(1)); } return OptionalFloat.empty(); } }; /** The Constant SINGLE_DOUBLE_EXTRACTOR. */ private static final ResultExtractor SINGLE_DOUBLE_EXTRACTOR = new ResultExtractor() { @Override public OptionalDouble apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return OptionalDouble.of(rs.getDouble(1)); } return OptionalDouble.empty(); } }; /** The Constant SINGLE_BIG_DECIMAL_EXTRACTOR. */ private static final ResultExtractor> SINGLE_BIG_DECIMAL_EXTRACTOR = new ResultExtractor>() { @Override public Nullable apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return Nullable.of(rs.getBigDecimal(1)); } return Nullable.empty(); } }; /** The Constant SINGLE_STRING_EXTRACTOR. */ private static final ResultExtractor> SINGLE_STRING_EXTRACTOR = new ResultExtractor>() { @Override public Nullable apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return Nullable.of(rs.getString(1)); } return Nullable.empty(); } }; /** The Constant SINGLE_DATE_EXTRACTOR. */ private static final ResultExtractor> SINGLE_DATE_EXTRACTOR = new ResultExtractor>() { @Override public Nullable apply(final ResultSet rs, final JdbcSettings jdbcSettings) throws SQLException { JdbcUtil.skip(rs, jdbcSettings.getOffset()); if (rs.next()) { return Nullable.of(rs.getDate(1)); } return Nullable.empty(); } }; /** The Constant SINGLE_TIME_EXTRACTOR. */ private static final ResultExtractor> SINGLE_TIME_EXTRACTOR = new ResultExtractor>() { @Override public Nullable