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

com.landawn.abacus.jdbc.dao.UncheckedCrudDao Maven / Gradle / Ivy

There is a newer version: 3.8.5
Show newest version
/*
 * Copyright (c) 2021, 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.jdbc.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.condition.ConditionFactory;
import com.landawn.abacus.condition.ConditionFactory.CF;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.jdbc.AbstractQuery;
import com.landawn.abacus.jdbc.IsolationLevel;
import com.landawn.abacus.jdbc.Jdbc;
import com.landawn.abacus.jdbc.JdbcUtil;
import com.landawn.abacus.jdbc.SQLExecutor;
import com.landawn.abacus.jdbc.SQLTransaction;
import com.landawn.abacus.jdbc.s;
import com.landawn.abacus.jdbc.annotation.NonDBOperation;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.BeanInfo;
import com.landawn.abacus.parser.ParserUtil.PropInfo;
import com.landawn.abacus.util.EntityId;
import com.landawn.abacus.util.Fn;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.QueryUtil;
import com.landawn.abacus.util.SQLBuilder;
import com.landawn.abacus.util.Seid;
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.stream.Stream;
import com.landawn.abacus.util.stream.Stream.StreamEx;

/**
 * The Interface CrudDao.
 *
 * @param 
 * @param  use {@code Void} if there is no id defined/annotated with {@code @Id} in target entity class {@code T}.
 * @param  {@code SQLBuilder} used to generate sql scripts. Only can be {@code SQLBuilder.PSC/PAC/PLC}
 * @see JdbcUtil#prepareQuery(javax.sql.DataSource, String)
 * @see JdbcUtil#prepareNamedQuery(javax.sql.DataSource, String)
 * @see JdbcUtil#beginTransaction(javax.sql.DataSource, IsolationLevel, boolean)
 * @see Dao
 * @see SQLExecutor.Mapper
 * @see com.landawn.abacus.condition.ConditionFactory
 * @see com.landawn.abacus.condition.ConditionFactory.CF
 */
@Beta
public interface UncheckedCrudDao>
        extends UncheckedDao, CrudDao {

    /**
     *
     * @return
     * @throws UncheckedSQLException
     * @throws UnsupportedOperationException the unchecked SQL exception
     * @deprecated unsupported Operation
     */
    @Deprecated
    @NonDBOperation
    @Override
    default ID generateId() throws UncheckedSQLException, UnsupportedOperationException {
        throw new UnsupportedOperationException();
    }

    /**
     *
     * @param entityToInsert
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    ID insert(final T entityToInsert) throws UncheckedSQLException;

    /**
     *
     * @param entityToInsert
     * @param propNamesToInsert
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    ID insert(final T entityToInsert, final Collection propNamesToInsert) throws UncheckedSQLException;

    /**
     *
     * @param namedInsertSQL
     * @param entityToInsert
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    ID insert(final String namedInsertSQL, final T entityToInsert) throws UncheckedSQLException;

    /**
     *
     * @param entities
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchInsert(final Collection entities) throws UncheckedSQLException {
        return batchInsert(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    List batchInsert(final Collection entities, final int batchSize) throws UncheckedSQLException;

    /**
     *
     * @param entities
     * @param propNamesToInsert
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchInsert(final Collection entities, final Collection propNamesToInsert) throws UncheckedSQLException {
        return batchInsert(entities, propNamesToInsert, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param propNamesToInsert
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    List batchInsert(final Collection entities, final Collection propNamesToInsert, final int batchSize) throws UncheckedSQLException;

    /**
     *
     * @param namedInsertSQL
     * @param entities
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Beta
    @Override
    default List batchInsert(final String namedInsertSQL, final Collection entities) throws UncheckedSQLException {
        return batchInsert(namedInsertSQL, entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param namedInsertSQL
     * @param entities
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Beta
    @Override
    List batchInsert(final String namedInsertSQL, final Collection entities, final int batchSize) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalBoolean} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalBoolean}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForBoolean()
     */
    @Override
    OptionalBoolean queryForBoolean(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalChar} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalChar}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForChar()
     */
    @Override
    OptionalChar queryForChar(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalByte} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalByte}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForByte()
     */
    @Override
    OptionalByte queryForByte(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalShort} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalShort}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForShort()
     */
    @Override
    OptionalShort queryForShort(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalInt} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalInt}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForInt()
     */
    @Override
    OptionalInt queryForInt(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalLong} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalLong}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForLong()
     */
    @Override
    OptionalLong queryForLong(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalFloat} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalFloat}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForFloat()
     */
    @Override
    OptionalFloat queryForFloat(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns an {@code OptionalDouble} describing the value in the first row/column if it exists, otherwise return an empty {@code OptionalDouble}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForDouble()
     */
    @Override
    OptionalDouble queryForDouble(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForString()
     */
    @Override
    Nullable queryForString(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForDate()
     */
    @Override
    Nullable queryForDate(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForTime()
     */
    @Override
    Nullable queryForTime(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForTimestamp()
     */
    @Override
    Nullable queryForTimestamp(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForBytes()
     */
    @Override
    Nullable queryForBytes(final String singleSelectPropName, final ID id) throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     *
     * @param 
     * @param targetValueClass
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForSingleResult(Class)
     */
    @Override
     Nullable queryForSingleResult(final Class targetValueClass, final String singleSelectPropName, final ID id)
            throws UncheckedSQLException;

    /**
     * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
     *
     * @param  the value type
     * @param targetValueClass
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForSingleNonNull(Class)
     */
    @Override
     Optional queryForSingleNonNull(final Class targetValueClass, final String singleSelectPropName, final ID id)
            throws UncheckedSQLException;

    /**
     * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
     *
     * @param  the value type
     * @param singleSelectPropName
     * @param id
     * @param rowMapper
     * @return
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForSingleNonNull(Class)
     */
    @Override
     Optional queryForSingleNonNull(final String singleSelectPropName, final ID id, final Jdbc.RowMapper rowMapper)
            throws UncheckedSQLException;

    /**
     * Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
     * And throws {@code DuplicatedResultException} if more than one record found.
     *
     * @param  the value type
     * @param targetValueClass
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForUniqueResult(Class)
     */
    @Override
     Nullable queryForUniqueResult(final Class targetValueClass, final String singleSelectPropName, final ID id)
            throws DuplicatedResultException, UncheckedSQLException;

    /**
     * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
     *
     * @param  the value type
     * @param targetValueClass
     * @param singleSelectPropName
     * @param id
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForUniqueNonNull(Class)
     */
    @Override
     Optional queryForUniqueNonNull(final Class targetValueClass, final String singleSelectPropName, final ID id)
            throws DuplicatedResultException, UncheckedSQLException;

    /**
     * Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
     *
     * @param  the value type
     * @param singleSelectPropName
     * @param id
     * @param rowMapper
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException
     * @see ConditionFactory
     * @see ConditionFactory.CF
     * @see AbstractQuery#queryForUniqueNonNull(Class)
     */
    @Override
     Optional queryForUniqueNonNull(final String singleSelectPropName, final ID id, final Jdbc.RowMapper rowMapper)
            throws DuplicatedResultException, UncheckedSQLException;

    /**
     *
     * @param id
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default Optional get(final ID id) throws DuplicatedResultException, UncheckedSQLException {
        return Optional.ofNullable(gett(id));
    }

    /**
     *
     * @param id
     * @param selectPropNames the properties (columns) to be selected, excluding the properties of joining entities. All the properties (columns) will be selected if the specified {@code selectPropNames} is {@code null}.
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default Optional get(final ID id, final Collection selectPropNames) throws DuplicatedResultException, UncheckedSQLException {
        return Optional.ofNullable(gett(id, selectPropNames));
    }

    /**
     *
     * @param id
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    T gett(final ID id) throws DuplicatedResultException, UncheckedSQLException;

    /**
     *
     * @param id
     * @param selectPropNames the properties (columns) to be selected, excluding the properties of joining entities. All the properties (columns) will be selected if the specified {@code selectPropNames} is {@code null}.
     *
     * @return
     * @throws DuplicatedResultException if more than one record found by the specified {@code id} (or {@code condition}).
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    T gett(final ID id, final Collection selectPropNames) throws DuplicatedResultException, UncheckedSQLException;

    /**
     *
     *
     * @param ids
     * @return
     * @throws DuplicatedResultException if the size of result is bigger than the size of input {@code ids}.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchGet(final Collection ids) throws DuplicatedResultException, UncheckedSQLException {
        return batchGet(ids, (Collection) null);
    }

    /**
     *
     * @param ids
     * @param batchSize
     * @return
     * @throws DuplicatedResultException if the size of result is bigger than the size of input {@code ids}.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchGet(final Collection ids, final int batchSize) throws DuplicatedResultException, UncheckedSQLException {
        return batchGet(ids, (Collection) null, batchSize);
    }

    /**
     *
     *
     * @param ids
     * @param selectPropNames the properties (columns) to be selected, excluding the properties of joining entities. All the properties (columns) will be selected if the specified {@code selectPropNames} is {@code null}.
     * @return
     * @throws DuplicatedResultException if the size of result is bigger than the size of input {@code ids}.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchGet(final Collection ids, final Collection selectPropNames)
            throws DuplicatedResultException, UncheckedSQLException {
        return batchGet(ids, selectPropNames, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param ids
     * @param selectPropNames the properties (columns) to be selected, excluding the properties of joining entities. All the properties (columns) will be selected if the specified {@code selectPropNames} is {@code null}.
     * @param batchSize
     * @return
     * @throws DuplicatedResultException if the size of result is bigger than the size of input {@code ids}.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    List batchGet(final Collection ids, final Collection selectPropNames, final int batchSize)
            throws DuplicatedResultException, UncheckedSQLException;

    /**
     *
     * @param id
     * @return {@code true}, if successful
     * @throws UncheckedSQLException the unchecked SQL exception
     * @see AbstractQuery#exists()
     */
    @Override
    boolean exists(final ID id) throws UncheckedSQLException;

    /**
     *
     *
     * @param id
     * @return
     * @throws UncheckedSQLException
     * @see AbstractQuery#notExists()
     */
    @Beta
    @Override
    default boolean notExists(final ID id) throws UncheckedSQLException {
        return !exists(id);
    }

    /**
     *
     * @param ids
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Beta
    @Override
    int count(final Collection ids) throws UncheckedSQLException;

    /**
     *
     * @param entityToUpdate
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int update(final T entityToUpdate) throws UncheckedSQLException;

    /**
     *
     * @param entityToUpdate
     * @param propNamesToUpdate
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int update(final T entityToUpdate, final Collection propNamesToUpdate) throws UncheckedSQLException;

    /**
     *
     * @param propName
     * @param propValue
     * @param id
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int update(final String propName, final Object propValue, final ID id) throws UncheckedSQLException {
        final Map updateProps = new HashMap<>();
        updateProps.put(propName, propValue);

        return update(updateProps, id);
    }

    /**
     *
     * @param updateProps
     * @param id
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int update(final Map updateProps, final ID id) throws UncheckedSQLException;

    /**
     *
     * @param entities
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchUpdate(final Collection entities) throws UncheckedSQLException {
        return batchUpdate(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int batchUpdate(final Collection entities, final int batchSize) throws UncheckedSQLException;

    /**
     *
     * @param entities
     * @param propNamesToUpdate
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchUpdate(final Collection entities, final Collection propNamesToUpdate) throws UncheckedSQLException {
        return batchUpdate(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param propNamesToUpdate
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int batchUpdate(final Collection entities, final Collection propNamesToUpdate, final int batchSize) throws UncheckedSQLException;

    /**
     * Execute {@code add} and return the added entity if the record doesn't, otherwise, {@code update} is executed and updated db record is returned.
     *
     * @param entity
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default T upsert(final T entity) throws UncheckedSQLException {
        N.checkArgNotNull(entity, s.entity);

        final Class cls = entity.getClass();
        @SuppressWarnings("deprecation")
        final List idPropNameList = QueryUtil.getIdFieldNames(cls); // must not empty.

        return upsert(entity, idPropNameList);
    }

    /**
     *
     * @param entity
     * @param uniquePropNamesForQuery
     * @return
     * @throws UncheckedSQLException
     */
    @Override
    default T upsert(final T entity, final List uniquePropNamesForQuery) throws UncheckedSQLException {
        N.checkArgNotNull(entity, s.entity);
        N.checkArgNotEmpty(uniquePropNamesForQuery, s.uniquePropNamesForQuery);

        final Condition cond = CF.eqAnd(entity, uniquePropNamesForQuery);

        return upsert(entity, cond);
    }

    /**
     * Execute {@code add} and return the added entity if the record doesn't, otherwise, {@code update} is executed and updated db record is returned.
     *
     * @param entity
     * @param cond to verify if the record exists or not.
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default T upsert(final T entity, final Condition cond) throws UncheckedSQLException {
        N.checkArgNotNull(cond, s.cond);

        final T dbEntity = findOnlyOne(cond).orElseNull();

        if (dbEntity == null) {
            insert(entity);
            return entity;
        } else {
            final Class cls = entity.getClass();
            @SuppressWarnings("deprecation")
            final List idPropNameList = QueryUtil.getIdFieldNames(cls);
            N.merge(entity, dbEntity, false, N.newHashSet(idPropNameList));
            update(dbEntity);
            return dbEntity;
        }
    }

    /**
     *
     * @param entities
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchUpsert(final Collection entities) throws UncheckedSQLException {
        return batchUpsert(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default List batchUpsert(final Collection entities, final int batchSize) throws UncheckedSQLException {
        N.checkArgPositive(batchSize, s.batchSize);

        if (N.isEmpty(entities)) {
            return new ArrayList<>();
        }

        final T entity = N.firstOrNullIfEmpty(entities);
        final Class cls = entity.getClass();
        @SuppressWarnings("deprecation")
        final List idPropNameList = QueryUtil.getIdFieldNames(cls); // must not empty.

        return batchUpsert(entities, idPropNameList, batchSize);
    }

    /**
     *
     * @param entities
     * @param uniquePropNamesForQuery
     * @return
     * @throws UncheckedSQLException
     */
    @Override
    default List batchUpsert(final Collection entities, final List uniquePropNamesForQuery) throws UncheckedSQLException {
        return batchUpsert(entities, uniquePropNamesForQuery, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param uniquePropNamesForQuery
     * @param batchSize
     * @return
     * @throws UncheckedSQLException
     */
    @Override
    default List batchUpsert(final Collection entities, final List uniquePropNamesForQuery, final int batchSize)
            throws UncheckedSQLException {
        N.checkArgPositive(batchSize, s.batchSize);
        N.checkArgNotEmpty(uniquePropNamesForQuery, s.uniquePropNamesForQuery);

        if (N.isEmpty(entities)) {
            return new ArrayList<>();
        }

        final List propNameListForQuery = uniquePropNamesForQuery;
        final T first = N.firstOrNullIfEmpty(entities);
        final Class cls = first.getClass();
        final BeanInfo entityInfo = ParserUtil.getBeanInfo(cls);

        final PropInfo uniquePropInfo = entityInfo.getPropInfo(propNameListForQuery.get(0));
        final List uniquePropInfos = N.map(propNameListForQuery, entityInfo::getPropInfo);

        final com.landawn.abacus.util.function.Function singleKeyExtractor = uniquePropInfo::getPropValue;

        @SuppressWarnings("deprecation")
        final com.landawn.abacus.util.function.Function entityIdExtractor = it -> {
            final Seid entityId = Seid.of(entityInfo.simpleClassName);

            for (final PropInfo propInfo : uniquePropInfos) {
                entityId.set(propInfo.name, propInfo.getPropValue(it));
            }

            return entityId;
        };

        final com.landawn.abacus.util.function.Function keysExtractor = propNameListForQuery.size() == 1 ? singleKeyExtractor
                : entityIdExtractor;

        final List dbEntities = propNameListForQuery.size() == 1
                ? Stream.of(entities).split(batchSize).flatmap(it -> list(CF.in(propNameListForQuery.get(0), N.map(it, singleKeyExtractor)))).toList()
                : Stream.of(entities).split(batchSize).flatmap(it -> list(CF.id2Cond(N.map(it, entityIdExtractor)))).toList();

        final Map dbIdEntityMap = StreamEx.of(dbEntities).toMap(keysExtractor, Fn.identity(), Fn.ignoringMerger());
        final Map> map = StreamEx.of(entities).groupTo(it -> dbIdEntityMap.containsKey(keysExtractor.apply(it)), Fn.identity());
        final List entitiesToUpdate = map.get(true);
        final List entitiesToInsert = map.get(false);

        final List result = new ArrayList<>(entities.size());
        final SQLTransaction tran = N.notEmpty(entitiesToInsert) && N.notEmpty(entitiesToUpdate) ? JdbcUtil.beginTransaction(dataSource()) : null;

        try {
            if (N.notEmpty(entitiesToInsert)) {
                batchInsert(entitiesToInsert, batchSize);
                result.addAll(entitiesToInsert);
            }

            if (N.notEmpty(entitiesToUpdate)) {
                final Set ignoredPropNames = N.newHashSet(propNameListForQuery);

                @SuppressWarnings("deprecation")
                final List idPropNameList = QueryUtil.getIdFieldNames(cls);

                if (N.notEmpty(idPropNameList)) {
                    ignoredPropNames.addAll(idPropNameList);
                }

                final List dbEntitiesToUpdate = StreamEx.of(entitiesToUpdate)
                        .map(it -> N.merge(it, dbIdEntityMap.get(keysExtractor.apply(it)), false, ignoredPropNames))
                        .toList();

                batchUpdate(dbEntitiesToUpdate, batchSize);

                result.addAll(dbEntitiesToUpdate);
            }

            if (tran != null) {
                tran.commit();
            }
        } finally {
            if (tran != null) {
                tran.rollbackIfNotCommitted();
            }
        }

        return result;
    }

    /**
     *
     * @param entity
     * @return {@code true}, if successful
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default boolean refresh(final T entity) throws UncheckedSQLException {
        N.checkArgNotNull(entity, s.entity);

        final Class cls = entity.getClass();
        final Collection propNamesToRefresh = JdbcUtil.getSelectPropNames(cls);

        return refresh(entity, propNamesToRefresh);
    }

    /**
     *
     * @param entity
     * @param propNamesToRefresh
     * @return {@code false} if no record found by the ids in the specified {@code entity}.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    @SuppressWarnings("deprecation")
    default boolean refresh(final T entity, final Collection propNamesToRefresh) throws UncheckedSQLException {
        N.checkArgNotNull(entity, s.entity);
        N.checkArgNotEmpty(propNamesToRefresh, s.propNamesToRefresh);

        final Class cls = entity.getClass();
        final List idPropNameList = QueryUtil.getIdFieldNames(cls); // must not empty.
        final BeanInfo entityInfo = ParserUtil.getBeanInfo(cls);

        final ID id = DaoUtil.extractId(entity, idPropNameList, entityInfo);
        final Collection selectPropNames = DaoUtil.getRefreshSelectPropNames(propNamesToRefresh, idPropNameList);

        final T dbEntity = gett(id, selectPropNames);

        if (dbEntity == null) {
            return false;
        } else {
            N.merge(dbEntity, entity, propNamesToRefresh);

            return true;
        }
    }

    /**
     *
     * @param entities
     * @return the count of refreshed entities.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchRefresh(final Collection entities) throws UncheckedSQLException {
        return batchRefresh(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param batchSize
     * @return the count of refreshed entities.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchRefresh(final Collection entities, final int batchSize) throws UncheckedSQLException {
        if (N.isEmpty(entities)) {
            return 0;
        }

        final T first = N.firstOrNullIfEmpty(entities);
        final Class cls = first.getClass();
        final Collection propNamesToRefresh = JdbcUtil.getSelectPropNames(cls);

        return batchRefresh(entities, propNamesToRefresh, batchSize);
    }

    /**
     *
     * @param entities
     * @param propNamesToRefresh
     * @return the count of refreshed entities.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchRefresh(final Collection entities, final Collection propNamesToRefresh) throws UncheckedSQLException {
        return batchRefresh(entities, propNamesToRefresh, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param propNamesToRefresh
     * @param batchSize
     * @return the count of refreshed entities.
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    @SuppressWarnings("deprecation")
    default int batchRefresh(final Collection entities, final Collection propNamesToRefresh, final int batchSize)
            throws UncheckedSQLException {
        N.checkArgNotEmpty(propNamesToRefresh, s.propNamesToRefresh);
        N.checkArgPositive(batchSize, s.batchSize);

        if (N.isEmpty(entities)) {
            return 0;
        }

        final T first = N.firstOrNullIfEmpty(entities);
        final Class cls = first.getClass();
        final List idPropNameList = QueryUtil.getIdFieldNames(cls); // must not empty.
        final BeanInfo entityInfo = ParserUtil.getBeanInfo(cls);

        final com.landawn.abacus.util.function.Function idExtractorFunc = DaoUtil.createIdExtractor(idPropNameList, entityInfo);
        final Map> idEntityMap = StreamEx.of(entities).groupTo(idExtractorFunc, Fn.identity());
        final Collection selectPropNames = DaoUtil.getRefreshSelectPropNames(propNamesToRefresh, idPropNameList);

        final List dbEntities = batchGet(idEntityMap.keySet(), selectPropNames, batchSize);

        if (N.isEmpty(dbEntities)) {
            return 0;
        } else {
            return dbEntities.stream().mapToInt(dbEntity -> {
                final ID id = idExtractorFunc.apply(dbEntity);
                final List tmp = idEntityMap.get(id);

                if (N.notEmpty(tmp)) {
                    for (final T entity : tmp) {
                        N.merge(dbEntity, entity, propNamesToRefresh);
                    }
                }

                return N.size(tmp);
            }).sum();
        }
    }

    /**
     *
     * @param entity
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int delete(final T entity) throws UncheckedSQLException;

    /**
     * Delete by id.
     *
     * @param id
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int deleteById(final ID id) throws UncheckedSQLException;

    /**
     *
     * @param entities
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchDelete(final Collection entities) throws UncheckedSQLException {
        return batchDelete(entities, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param entities
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int batchDelete(final Collection entities, final int batchSize) throws UncheckedSQLException;

    //    /**
    //     *
    //     * @param entities
    //     * @param onDeleteAction It should be defined and done in DB server side.
    //     * @return
    //     * @throws UncheckedSQLException the unchecked SQL exception
    //     */
    //    @Beta
    //    default int batchDelete(final Collection entities, final OnDeleteAction onDeleteAction) throws UncheckedSQLException {
    //        return batchDelete(entities, onDeleteAction, DEFAULT_BATCH_SIZE);
    //    }
    //
    //    /**
    //     *
    //     * @param entities
    //     * @param onDeleteAction It should be defined and done in DB server side.
    //     * @param batchSize
    //     * @return
    //     * @throws UncheckedSQLException the unchecked SQL exception
    //     */
    //    @Beta
    //    int batchDelete(final Collection entities, final OnDeleteAction onDeleteAction, final int batchSize) throws UncheckedSQLException;

    /**
     *
     * @param ids
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    default int batchDeleteByIds(final Collection ids) throws UncheckedSQLException {
        return batchDeleteByIds(ids, JdbcUtil.DEFAULT_BATCH_SIZE);
    }

    /**
     *
     * @param ids
     * @param batchSize
     * @return
     * @throws UncheckedSQLException the unchecked SQL exception
     */
    @Override
    int batchDeleteByIds(final Collection ids, final int batchSize) throws UncheckedSQLException;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy