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

org.dbflute.bhv.AbstractBehaviorReadable Maven / Gradle / Ivy

There is a newer version: 1.2.8
Show newest version
/*
 * Copyright 2014-2021 the original author or authors.
 *
 * 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 org.dbflute.bhv;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.dbflute.Entity;
import org.dbflute.bhv.core.BehaviorCommand;
import org.dbflute.bhv.core.BehaviorCommandInvoker;
import org.dbflute.bhv.core.command.AbstractAllBehaviorCommand;
import org.dbflute.bhv.core.command.AbstractCountableUpdateCommand;
import org.dbflute.bhv.core.command.InsertEntityCommand;
import org.dbflute.bhv.core.command.SelectCountCBCommand;
import org.dbflute.bhv.core.command.SelectCursorCBCommand;
import org.dbflute.bhv.core.command.SelectEntityCBCommand;
import org.dbflute.bhv.core.command.SelectListCBCommand;
import org.dbflute.bhv.core.command.SelectNextValCommand;
import org.dbflute.bhv.core.command.SelectNextValSubCommand;
import org.dbflute.bhv.core.command.SelectScalarCBCommand;
import org.dbflute.bhv.exception.BehaviorExceptionThrower;
import org.dbflute.bhv.readable.CBCall;
import org.dbflute.bhv.readable.EntityRowHandler;
import org.dbflute.bhv.referrer.LoadReferrerOption;
import org.dbflute.bhv.referrer.NestedReferrerListGateway;
import org.dbflute.bhv.referrer.ReferrerConditionSetupper;
import org.dbflute.bhv.referrer.ReferrerListHandler;
import org.dbflute.bhv.referrer.ReferrerLoaderHandler;
import org.dbflute.bhv.writable.InsertOption;
import org.dbflute.cbean.ConditionBean;
import org.dbflute.cbean.chelper.HpFixedConditionQueryResolver;
import org.dbflute.cbean.chelper.HpSLSExecutor;
import org.dbflute.cbean.chelper.HpSLSFunction;
import org.dbflute.cbean.ckey.ConditionKey;
import org.dbflute.cbean.coption.CursorSelectOption;
import org.dbflute.cbean.exception.ConditionBeanExceptionThrower;
import org.dbflute.cbean.paging.PagingBean;
import org.dbflute.cbean.paging.PagingHandler;
import org.dbflute.cbean.paging.PagingInvoker;
import org.dbflute.cbean.result.ListResultBean;
import org.dbflute.cbean.result.PagingResultBean;
import org.dbflute.cbean.result.ResultBeanBuilder;
import org.dbflute.cbean.scoping.AndQuery;
import org.dbflute.cbean.scoping.OrQuery;
import org.dbflute.cbean.scoping.UnionQuery;
import org.dbflute.cbean.sqlclause.clause.SelectClauseType;
import org.dbflute.cbean.sqlclause.orderby.OrderByClause;
import org.dbflute.cbean.sqlclause.orderby.OrderByElement;
import org.dbflute.dbmeta.DBMeta;
import org.dbflute.dbmeta.accessory.DerivedMappable;
import org.dbflute.dbmeta.info.ColumnInfo;
import org.dbflute.dbmeta.info.ForeignInfo;
import org.dbflute.dbmeta.info.ReferrerInfo;
import org.dbflute.dbmeta.info.RelationInfo;
import org.dbflute.exception.EntityAlreadyDeletedException;
import org.dbflute.exception.FetchingOverSafetySizeException;
import org.dbflute.exception.IllegalBehaviorStateException;
import org.dbflute.exception.IllegalConditionBeanOperationException;
import org.dbflute.exception.PagingOverSafetySizeException;
import org.dbflute.helper.beans.DfBeanDesc;
import org.dbflute.helper.beans.DfPropertyDesc;
import org.dbflute.helper.beans.factory.DfBeanDescFactory;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.optional.OptionalEntity;
import org.dbflute.optional.OptionalThing;
import org.dbflute.optional.OptionalThingExceptionThrower;
import org.dbflute.optional.RelationOptionalFactory;
import org.dbflute.outsidesql.executor.OutsideSqlAllFacadeExecutor;
import org.dbflute.system.DBFluteSystem;
import org.dbflute.util.DfCollectionUtil;
import org.dbflute.util.DfReflectionUtil;
import org.dbflute.util.DfTypeUtil;
import org.dbflute.util.Srl;

/**
 * The abstract class of readable behavior.
 * @param  The type of entity handled by this behavior.
 * @param  The type of condition-bean handled by this behavior.
 * @author jflute
 */
public abstract class AbstractBehaviorReadable implements BehaviorReadable {

    // ===================================================================================
    //                                                                          Definition
    //                                                                          ==========
    /** The prefix mark for derived mapping alias. */
    protected static final String DERIVED_MAPPABLE_ALIAS_PREFIX = DerivedMappable.MAPPING_ALIAS_PREFIX;

    /** The empty instance for provider of list handling for nested referrer. (wild-card generic for downcast) */
    protected static final NestedReferrerListGateway EMPTY_NREF_LGWAY = new NestedReferrerListGateway() {
        public void withNestedReferrer(ReferrerListHandler handler) {
            final List emptyList = DfCollectionUtil.emptyList();
            handler.handle(emptyList);
        }
    };

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    /** Behavior-selector instance. It's basically referred at loadReferrer. (Required for loadReferrer) */
    protected BehaviorCommandInvoker _behaviorCommandInvoker;

    /** Behavior-selector instance. It's basically referred at loadReferrer. (Required for loadReferrer) */
    protected BehaviorSelector _behaviorSelector;

    // ===================================================================================
    //                                                                          Table name
    //                                                                          ==========
    /** {@inheritDoc} */
    @SuppressWarnings("unchecked")
    public ENTITY newEntity() {
        return (ENTITY) asDBMeta().newEntity();
    }

    protected CB createCB(CBCall cbCall) { // CB from callback
        assertCBCallNotNull(cbCall);
        final CB cb = newConditionBean();
        cbCall.callback(cb);
        return cb;
    }

    /** {@inheritDoc} */
    public abstract CB newConditionBean(); // defined here to resolve generic of return type

    // ===================================================================================
    //                                                                        Count Select
    //                                                                        ============
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    protected int facadeSelectCount(CB cb) {
        return doSelectCountUniquely(cb);
    }

    protected int doSelectCountUniquely(CB cb) { // called by selectCount(cb)
        assertCBStateValid(cb);
        return delegateSelectCountUniquely(cb);
    }

    protected int doSelectCountPlainly(CB cb) { // called by selectPage(cb)
        assertCBStateValid(cb);
        return delegateSelectCountPlainly(cb);
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    /** {@inheritDoc} */
    public int readCount(ConditionBean cb) {
        assertCBStateValid(cb);
        return doReadCount(cb);
    }

    protected int doReadCount(ConditionBean cb) {
        return facadeSelectCount(downcast(cb));
    }

    // ===================================================================================
    //                                                                       Entity Select
    //                                                                       =============
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    // *several methods cannot be resolved because they are changed by generator option
    protected  RESULT doSelectEntity(CB cb, Class entityType) {
        return helpSelectEntityInternally(cb, entityType);
    }

    protected ENTITY facadeSelectEntityWithDeletedCheck(CB cb) {
        return doSelectEntityWithDeletedCheck(cb, typeOfSelectedEntity());
    }

    protected  RESULT doSelectEntityWithDeletedCheck(CB cb, Class entityType) {
        assertCBStateValid(cb);
        assertObjectNotNull("entityType", entityType);
        return helpSelectEntityWithDeletedCheckInternally(cb, entityType);
    }

    // -----------------------------------------------------
    //                                       Internal Helper
    //                                       ---------------
    protected  RESULT helpSelectEntityInternally(CB cb, Class entityType) {
        assertConditionBeanSelectResource(cb, entityType);
        cb.xcheckSpecifyColumnRequiredIfNeeds();
        if (cb.hasSelectAllPossible() && cb.getFetchSize() != 1) { // if no condition for one
            throwSelectEntityConditionNotFoundException(cb);
        }
        final int preSafetyMaxResultSize = xcheckSafetyResultAsOne(cb);
        try {
            return delegateSelectEntity(cb, entityType);
        } catch (FetchingOverSafetySizeException e) {
            throwSelectEntityDuplicatedException("{over safetyMaxResultSize '1'}", cb, e);
            return null; // unreachable
        } finally {
            xrestoreSafetyResult(cb, preSafetyMaxResultSize);
        }
    }

    protected  RESULT helpSelectEntityWithDeletedCheckInternally(CB cb, Class entityType) {
        final RESULT entity = helpSelectEntityInternally(cb, entityType);
        assertEntityNotDeleted(entity, cb);
        return entity;
    }

    protected int xcheckSafetyResultAsOne(ConditionBean cb) {
        final int safetyMaxResultSize = cb.getSafetyMaxResultSize();
        cb.checkSafetyResult(1);
        return safetyMaxResultSize;
    }

    protected void xrestoreSafetyResult(ConditionBean cb, int preSafetyMaxResultSize) {
        cb.checkSafetyResult(preSafetyMaxResultSize);
    }

    // -----------------------------------------------------
    //                                       Result Handling
    //                                       ---------------
    /**
     * Assert that the entity is not deleted.
     * @param entity Selected entity. (NullAllowed)
     * @param searchKey Search-key for logging.
     * @throws org.dbflute.exception.EntityAlreadyDeletedException When the entity has already been deleted.
     */
    protected void assertEntityNotDeleted(Entity entity, Object searchKey) {
        if (entity == null) {
            throwSelectEntityAlreadyDeletedException(searchKey);
        }
    }

    protected void throwSelectEntityAlreadyDeletedException(Object searchKey) {
        createBhvExThrower().throwSelectEntityAlreadyDeletedException(searchKey);
    }

    protected void throwSelectEntityDuplicatedException(String resultCountExp, Object searchKey, Throwable cause) {
        createBhvExThrower().throwSelectEntityDuplicatedException(resultCountExp, searchKey, cause);
    }

    protected void throwSelectEntityConditionNotFoundException(ConditionBean cb) {
        createBhvExThrower().throwSelectEntityConditionNotFoundException(cb);
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    /** {@inheritDoc} */
    public Entity readEntity(ConditionBean cb) {
        assertCBStateValid(cb);
        return doReadEntity(cb);
    }

    protected abstract Entity doReadEntity(ConditionBean cb);

    protected  OptionalEntity createOptionalEntity(RESULT entity, Object... searchKey) {
        if (entity != null) {
            return OptionalEntity.of(entity);
        } else { // not serializable here because of select entity (while relation optional is able)
            return OptionalEntity.ofNullable(entity, () -> throwSelectEntityAlreadyDeletedException(searchKey));
        }
    }

    /** {@inheritDoc} */
    public Entity readEntityWithDeletedCheck(ConditionBean cb) {
        assertCBStateValid(cb);
        return doReadEntityWithDeletedCheck(cb);
    }

    protected Entity doReadEntityWithDeletedCheck(ConditionBean cb) {
        return facadeSelectEntityWithDeletedCheck(downcast(cb));
    }

    // ===================================================================================
    //                                                                         List Select
    //                                                                         ===========
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    protected ListResultBean facadeSelectList(CB cb) {
        return doSelectList(cb, typeOfSelectedEntity());
    }

    protected  ListResultBean doSelectList(CB cb, Class entityType) {
        return helpSelectListInternally(cb, entityType);
    }

    // -----------------------------------------------------
    //                                       Internal Helper
    //                                       ---------------
    protected  ListResultBean helpSelectListInternally(CB cb, Class entityType) {
        assertConditionBeanSelectResource(cb, entityType);
        cb.xcheckSpecifyColumnRequiredIfNeeds();
        try {
            final List selectedList = delegateSelectList(cb, entityType);
            return createListResultBean(cb, selectedList);
        } catch (FetchingOverSafetySizeException e) {
            createBhvExThrower().throwDangerousResultSizeException(cb, e);
            return null; // unreachable
        }
    }

    protected  ListResultBean createListResultBean(ConditionBean cb, List selectedList) {
        return new ResultBeanBuilder(asTableDbName()).buildListOfCB(cb, selectedList);
    }

    // -----------------------------------------------------
    //                                       Option Handling
    //                                       ---------------
    protected boolean isEntityDerivedMappable() {
        return false; // depends on generator
    }

    protected void throwSpecifyDerivedReferrerEntityPropertyNotFoundException(String alias, Class entityType) {
        createCBExThrower().throwSpecifyDerivedReferrerEntityPropertyNotFoundException(alias, entityType);
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    /** {@inheritDoc} */
    public  ListResultBean readList(ConditionBean cb) {
        assertCBStateValid(cb);
        @SuppressWarnings("unchecked")
        final ListResultBean entityList = (ListResultBean) doReadList(cb);
        return entityList;
    }

    protected ListResultBean doReadList(ConditionBean cb) {
        return facadeSelectList(downcast(cb));
    }

    // ===================================================================================
    //                                                                         Page Select
    //                                                                         ===========
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    protected PagingResultBean facadeSelectPage(CB cb) {
        return doSelectPage(cb, typeOfSelectedEntity());
    }

    protected  PagingResultBean doSelectPage(CB cb, Class entityType) {
        return helpSelectPageInternally(cb, entityType);
    }

    protected  PagingResultBean helpSelectPageInternally(CB cb, Class entityType) {
        assertConditionBeanSelectResource(cb, entityType);
        cb.xcheckSpecifyColumnRequiredIfNeeds();
        try {
            final PagingHandler handler = createPagingHandler(cb, entityType);
            final PagingInvoker invoker = createPagingInvoker(cb);
            return invoker.invokePaging(handler);
        } catch (PagingOverSafetySizeException e) {
            createBhvExThrower().throwDangerousResultSizeException(cb, e);
            return null; // unreachable
        }
    }

    protected  PagingHandler createPagingHandler(final CB cb, final Class entityType) {
        return new PagingHandler() {
            public PagingBean getPagingBean() {
                return cb;
            }

            public int count() {
                try {
                    cb.getSqlClause().enablePagingAdjustment();
                    return delegateSelectCountPlainly(cb);
                } finally {
                    cb.getSqlClause().disablePagingAdjustment();
                }
            }

            public List paging() {
                try {
                    cb.getSqlClause().enablePagingAdjustment();
                    return delegateSelectList(cb, entityType);
                } finally {
                    cb.getSqlClause().disablePagingAdjustment();
                }
            }
        };
    }

    protected  PagingInvoker createPagingInvoker(CB cb) {
        return cb.createPagingInvoker(asTableDbName());
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    /** {@inheritDoc} */
    public  PagingResultBean readPage(final ConditionBean cb) {
        assertCBStateValid(cb);
        @SuppressWarnings("unchecked")
        final PagingResultBean entityList = (PagingResultBean) doReadPage(cb);
        return entityList;
    }

    protected PagingResultBean doReadPage(ConditionBean cb) {
        return facadeSelectPage(downcast(cb));
    }

    // ===================================================================================
    //                                                                       Cursor Select
    //                                                                       =============
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    protected void facadeSelectCursor(CB cb, EntityRowHandler entityRowHandler) {
        doSelectCursor(cb, entityRowHandler, typeOfSelectedEntity());
    }

    protected  void doSelectCursor(CB cb, EntityRowHandler handler, Class entityType) {
        assertCBStateValid(cb);
        assertObjectNotNull("entityRowHandler", handler);
        assertObjectNotNull("entityType", entityType);
        assertSpecifyDerivedReferrerEntityProperty(cb, entityType);
        helpSelectCursorInternally(cb, handler, entityType);
    }

    // -----------------------------------------------------
    //                                       Internal Helper
    //                                       ---------------
    protected  void helpSelectCursorInternally(CB cb, EntityRowHandler handler,
            Class entityType) {
        assertObjectNotNull("entityRowHandler", handler);
        assertConditionBeanSelectResource(cb, entityType);
        cb.xcheckSpecifyColumnRequiredIfNeeds();
        final CursorSelectOption option = cb.getCursorSelectOption();
        if (option != null && option.isByPaging()) {
            helpSelectCursorHandlingByPaging(cb, handler, entityType, option);
        } else { // basically here
            delegateSelectCursor(cb, handler, entityType);
        }
    }

    protected  void helpSelectCursorHandlingByPaging(CB cb, EntityRowHandler entityRowHandler,
            Class entityType, CursorSelectOption option) {
        helpSelectCursorCheckingByPagingAllowed(cb, option);
        helpSelectCursorCheckingOrderByPK(cb, option);
        final int pageSize = option.getPageSize();
        int pageNumber = 1;
        rootLoop: // to stop on the way
        while (true) {
            cb.paging(pageSize, pageNumber);
            final List pageList = delegateSelectList(cb, entityType);
            for (RESULT entity : pageList) {
                entityRowHandler.handle(entity);
                if (entityRowHandler.isBreakCursor()) {
                    break rootLoop;
                }
            }
            if (pageList.size() < pageSize) { // means last page
                break;
            }
            ++pageNumber;
        }
    }

    protected void helpSelectCursorCheckingByPagingAllowed(CB cb, CursorSelectOption option) {
        if (!cb.getSqlClause().isCursorSelectByPagingAllowed()) {
            String msg = "The cursor select by paging is not allowed at the DBMS.";
            throw new IllegalConditionBeanOperationException(msg);
        }
    }

    protected void helpSelectCursorCheckingOrderByPK(CB cb, CursorSelectOption option) {
        if (option.isOrderByPK()) {
            final OrderByClause orderByClause = cb.getOrderByComponent();
            final OrderByElement orderByFirstElement = orderByClause.getOrderByFirstElement();
            if (orderByFirstElement == null || !orderByFirstElement.getColumnInfo().isPrimary()) {
                String msg = "The cursor select by paging needs order by primary key: " + cb.asTableDbName();
                throw new IllegalConditionBeanOperationException(msg);
            }
        }
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    @SuppressWarnings("unchecked")
    public  void readCursor(ConditionBean cb, EntityRowHandler entityLambda) {
        facadeSelectCursor(downcast(cb), (EntityRowHandler) entityLambda);
    }

    // ===================================================================================
    //                                                                       Scalar Select
    //                                                                       =============
    // -----------------------------------------------------
    //                                         Main Entrance
    //                                         -------------
    // internal name is ScalarSelect
    protected  HpSLSFunction facadeScalarSelect(Class resultType) {
        return doScalarSelect(resultType, newConditionBean());
    }

    protected  HpSLSFunction doScalarSelect(final Class resultType, final CB cb) {
        assertObjectNotNull("resultType", resultType);
        assertCBStateValid(cb);
        cb.xsetupForScalarSelect();
        cb.getSqlClause().disableSelectIndex(); // for when you use union
        HpSLSExecutor executor = createHpSLSExecutor(); // variable to resolve generic
        return createSLSFunction(cb, resultType, executor);
    }

    protected  HpSLSExecutor createHpSLSExecutor() {
        return new HpSLSExecutor() {
            public RESULT execute(CB cb, Class resultType, SelectClauseType selectClauseType) {
                return invoke(createSelectScalarCBCommand(cb, resultType, selectClauseType));
            }
        };
    }

    protected  HpSLSFunction createSLSFunction(CB cb, Class resultType, HpSLSExecutor exec) {
        return newSLSFunction(cb, resultType, exec);
    }

    protected  HpSLSFunction newSLSFunction(CB cb, Class resultType, HpSLSExecutor exec) {
        return new HpSLSFunction(cb, resultType, exec);
    }

    protected  HpSLSFunction doReadScalar(Class resultTYpe) {
        return facadeScalarSelect(resultTYpe);
    }

    // -----------------------------------------------------
    //                                    Interface Dispatch
    //                                    ------------------
    /** {@inheritDoc} */
    public  HpSLSFunction readScalar(Class resultType) {
        @SuppressWarnings("unchecked")
        final HpSLSFunction func = (HpSLSFunction) doReadScalar(resultType);
        return func;
    }

    // ===================================================================================
    //                                                                          OutsideSql
    //                                                                          ==========
    /** {@inheritDoc} */
    public  OutsideSqlAllFacadeExecutor readyOutsideSql() {
        return doOutsideSql();
    }

    /**
     * Prepare an outside-SQL execution by returning an instance of the executor for outside-SQL. 
* It's an extension point for your adding original customization to outside-SQL executions. * @param The type of behavior. * @return The facade for outside-SQL. (NotNull) */ protected OutsideSqlAllFacadeExecutor doOutsideSql() { assertBehaviorCommandInvoker("outsideSql"); return _behaviorCommandInvoker.createOutsideSqlAllFacadeExecutor(asTableDbName()); } // =================================================================================== // Sequence // ======== /** {@inheritDoc} */ public Number readNextVal() { return doReadNextVal(); } protected abstract Number doReadNextVal(); // =================================================================================== // Load Referrer // ============= // ----------------------------------------------------- // New Entry Point // --------------- /** * Help load referrer internally. (new entry point)
* About internal policy, the value of primary key(and others too) is treated as CaseInsensitive. * @param The type of base entity. * @param The type of primary key. * @param The type of referrer condition-bean. * @param The type of referrer entity. * @param localEntityList The list of local entity. (NotNull) * @param loadReferrerOption The option of loadReferrer. (NotNull) * @param referrerProperty The property name of referrer. (NotNull) * @return The callback to load nested referrer. (NotNull) */ protected // generic NestedReferrerListGateway helpLoadReferrerInternally(List localEntityList, LoadReferrerOption loadReferrerOption, String referrerProperty) { return doHelpLoadReferrerInternally(localEntityList, loadReferrerOption, referrerProperty); } protected // generic NestedReferrerListGateway doHelpLoadReferrerInternally(List localEntityList, LoadReferrerOption loadReferrerOption, final String referrerProperty) { final DBMeta dbmeta = asDBMeta(); final ReferrerInfo referrerInfo = dbmeta.findReferrerInfo(referrerProperty); final BehaviorReadable referrerBhv = xfindReferrerBehavior(referrerInfo); final Set pkColSet = referrerInfo.getLocalReferrerColumnInfoMap().keySet(); // might be unique key final Map mappingColMap = referrerInfo.getReferrerLocalColumnInfoMap(); // key is referrer's final InternalLoadReferrerCallback callback; if (pkColSet.size() == 1) { // simple key final ColumnInfo pkCol = pkColSet.iterator().next(); final ColumnInfo fkCol = mappingColMap.keySet().iterator().next(); callback = xcreateLoadReferrerCallback(referrerProperty, dbmeta, referrerInfo, referrerBhv, pkCol, fkCol); } else { // compound key final Set fkColSet = mappingColMap.keySet(); callback = xcreateLoadReferrerCallback(referrerProperty, dbmeta, referrerInfo, referrerBhv, pkColSet, fkColSet, mappingColMap); } return helpLoadReferrerInternally(localEntityList, loadReferrerOption, callback); } protected BehaviorReadable xfindReferrerBehavior(final ReferrerInfo referrerInfo) { final String behaviorName = referrerInfo.getReferrerDBMeta().getBehaviorTypeName(); @SuppressWarnings("unchecked") final Class behaviorType = (Class) DfReflectionUtil.forName(behaviorName); return xgetBSFLR().select(behaviorType); } // ----------------------------------------------------- // Loading Callback // ---------------- protected // generic InternalLoadReferrerCallback // return xcreateLoadReferrerCallback(final String referrerProperty, final DBMeta dbmeta, final ReferrerInfo referrerInfo, final BehaviorReadable referrerBhv, final ColumnInfo pkCol, final ColumnInfo fkCol) { return new InternalLoadReferrerCallback() { // for simple key public KEY getPKVal(LOCAL_ENTITY entity) { return pkCol.read(entity); // (basically) PK cannot be optional because of not-null } public void setRfLs(LOCAL_ENTITY entity, List referrerList) { referrerInfo.write(entity, referrerList); } @SuppressWarnings("unchecked") public REFERRER_CB newMyCB() { return (REFERRER_CB) referrerBhv.newConditionBean(); } public void qyFKIn(REFERRER_CB cb, Collection pkList) { final String conditionKey = ConditionKey.CK_IN_SCOPE.getConditionKey(); final String columnDbName = fkCol.getColumnDbName(); if (pkList.size() == 1) { // one local entity cb.localCQ().invokeQueryEqual(columnDbName, pkList.iterator().next()); } else { // multiple local entities cb.localCQ().invokeQuery(columnDbName, conditionKey, pkList); } } public void qyOdFKAsc(REFERRER_CB cb) { cb.localCQ().invokeOrderBy(fkCol.getColumnDbName(), true); } public void spFKCol(REFERRER_CB cb) { cb.localSp().xspecifyColumn(fkCol.getColumnDbName()); } public List selRfLs(REFERRER_CB cb) { return referrerBhv.readList(cb); } public KEY getFKVal(REFERRER_ENTITY entity) { final Class fkType = fkCol.getObjectNativeType(); final Class pkType = pkCol.getObjectNativeType(); final Object fkValue = fkCol.read(entity); return xconvertFK2PKImplicitly(referrerProperty, fkType, pkType, fkValue); } public void setlcEt(REFERRER_ENTITY referrerEntity, LOCAL_ENTITY localEntity) { final RelationInfo reverseInfo = referrerInfo.getReverseRelation(); final Object written = xconvertToRelationOptionalEntityIfNeeds(localEntity, reverseInfo); reverseInfo.write(referrerEntity, written); } public String getRfPrNm() { return referrerProperty; } }; } protected // generic InternalLoadReferrerCallback // return xcreateLoadReferrerCallback(final String referrerProperty, final DBMeta dbmeta, final ReferrerInfo referrerInfo, final BehaviorReadable referrerBhv, final Set pkColSet, final Set fkColSet, final Map mappingColMap) { return new InternalLoadReferrerCallback() { // for compound key @SuppressWarnings("unchecked") public KEY getPKVal(LOCAL_ENTITY entity) { final Map keyMap = xnewLoadReferrerCompoundKeyMap(); for (ColumnInfo pkCol : pkColSet) { keyMap.put(pkCol.getColumnDbName(), pkCol.read(entity)); // key is DB name } return (KEY) keyMap; // cannot use because it might be unique key //return (KEY) dbmeta.extractPrimaryKeyMap(entity); } public void setRfLs(LOCAL_ENTITY entity, List referrerList) { referrerInfo.write(entity, referrerList); } @SuppressWarnings("unchecked") public REFERRER_CB newMyCB() { return (REFERRER_CB) referrerBhv.newConditionBean(); } public void qyFKIn(REFERRER_CB cb, final Collection pkList) { // compound key doesn't use InScope so OrScopeQuery cb.invokeOrScopeQuery(new OrQuery() { public void query(ConditionBean orCB) { for (final KEY pkKey : pkList) { @SuppressWarnings("unchecked") final Map pkMap = (Map) pkKey; orCB.invokeOrScopeQueryAndPart(new AndQuery() { public void query(ConditionBean andCB) { for (ColumnInfo fkCol : fkColSet) { final ColumnInfo pkCol = mappingColMap.get(fkCol); final Object pkValue = pkMap.get(pkCol.getColumnDbName()); // key is DB name andCB.localCQ().invokeQueryEqual(fkCol.getColumnDbName(), pkValue); } } }); } } }); } public void qyOdFKAsc(REFERRER_CB cb) { for (ColumnInfo fkCol : fkColSet) { cb.localCQ().invokeOrderBy(fkCol.getColumnDbName(), true); } } public void spFKCol(REFERRER_CB cb) { for (ColumnInfo fkCol : fkColSet) { cb.localSp().xspecifyColumn(fkCol.getColumnDbName()); } } public List selRfLs(REFERRER_CB cb) { return referrerBhv.readList(cb); } @SuppressWarnings("unchecked") public KEY getFKVal(REFERRER_ENTITY entity) { final Map fkMap = xnewLoadReferrerCompoundKeyMap(); for (ColumnInfo fkCol : fkColSet) { final Object fkValue = fkCol.read(entity); final ColumnInfo pkCol = mappingColMap.get(fkCol); final String mapKey = pkCol.getColumnDbName(); // key is DB name final Class fkType = fkCol.getObjectNativeType(); final Class pkType = pkCol.getObjectNativeType(); final Object realValue; if (fkType.equals(pkType)) { // basically true realValue = fkValue; } else { // different type (needs implicit conversion) realValue = xconvertFK2PKImplicitly(referrerProperty, fkType, pkType, fkValue); } fkMap.put(mapKey, realValue); } return (KEY) fkMap; } public void setlcEt(REFERRER_ENTITY referrerEntity, LOCAL_ENTITY localEntity) { final RelationInfo reverseInfo = referrerInfo.getReverseRelation(); // always exists final Object written = xconvertToRelationOptionalEntityIfNeeds(localEntity, reverseInfo); reverseInfo.write(referrerEntity, written); } public String getRfPrNm() { return referrerProperty; } }; } @SuppressWarnings("unchecked") protected KEY xconvertFK2PKImplicitly(String referrerProperty, Class fkType, Class pkType, Object fkValue) { // DB-able entity does not support optional for property // only supported in immutable entity final KEY realValue; if (fkType.equals(pkType)) { // basically true realValue = (KEY) fkValue; } else { // different type (needs implicit conversion) if (String.class.equals(pkType)) { // e.g. Integer to String realValue = (KEY) fkValue.toString(); } else if (Number.class.isAssignableFrom(pkType)) { // e.g. Long to Integer realValue = (KEY) DfTypeUtil.toNumber(fkValue, pkType); } else if (Date.class.isAssignableFrom(fkType)) { if (Date.class.equals(pkType)) { // e.g. Timestamp to Date realValue = (KEY) new Date(((Date) fkValue).getTime()); } else if (Timestamp.class.equals(pkType)) { // e.g. Date to Timestamp realValue = (KEY) new Timestamp(((Date) fkValue).getTime()); } else { // cannot conversion realValue = (KEY) fkValue; } } else { // cannot conversion realValue = (KEY) fkValue; } } return realValue; } protected Object xconvertToRelationOptionalEntityIfNeeds(Object localEntity, RelationInfo reverseInfo) { final Object writtenObj; if (isRelationOptional(reverseInfo.getPropertyAccessType())) { writtenObj = toRelationOptional(reverseInfo.getRelationPropertyName(), localEntity); } else { writtenObj = localEntity; } return writtenObj; } // ----------------------------------------------------- // Ancient Entry Point // ------------------- /** * Help load referrer internally. (ancient entry point)
* About internal policy, the value of primary key(and others too) is treated as CaseInsensitive. * @param The type of base entity. * @param The type of primary key. * @param The type of referrer condition-bean. * @param The type of referrer entity. * @param localEntityList The list of local entity. (NotNull) * @param loadReferrerOption The option of loadReferrer. (NotNull) * @param callback The internal callback of loadReferrer. (NotNull) * @return The callback to load nested referrer. (NotNull) */ protected // generic NestedReferrerListGateway helpLoadReferrerInternally(List localEntityList, LoadReferrerOption loadReferrerOption, InternalLoadReferrerCallback callback) { return doHelpLoadReferrerInternally(localEntityList, loadReferrerOption, callback); } protected // generic NestedReferrerListGateway doHelpLoadReferrerInternally(List localEntityList, LoadReferrerOption loadReferrerOption, final InternalLoadReferrerCallback callback) { // - - - - - - - - - - // Assert precondition // - - - - - - - - - - assertBehaviorSelectorNotNull("loadReferrer"); assertObjectNotNull("localEntityList", localEntityList); assertObjectNotNull("loadReferrerOption", loadReferrerOption); if (localEntityList.isEmpty()) { @SuppressWarnings("unchecked") final NestedReferrerListGateway empty = (NestedReferrerListGateway) EMPTY_NREF_LGWAY; return empty; } // - - - - - - - - - - - - - - // Prepare temporary container // - - - - - - - - - - - - - - final Map pkLocalEntityMap = new LinkedHashMap(); final Set pkSet = new LinkedHashSet(); // might be same PK e.g. when entity of pull-out for (LOCAL_ENTITY localEntity : localEntityList) { final KEY primaryKeyValue = callback.getPKVal(localEntity); if (primaryKeyValue == null) { String msg = "PK value of local entity should not be null: " + localEntity; throw new IllegalArgumentException(msg); } pkSet.add(primaryKeyValue); pkLocalEntityMap.put(toLoadReferrerMappingKey(primaryKeyValue), localEntity); } // - - - - - - - - - - - - - - - - // Prepare referrer condition bean // - - - - - - - - - - - - - - - - final REFERRER_CB cb; if (loadReferrerOption.getReferrerConditionBean() != null) { cb = loadReferrerOption.getReferrerConditionBean(); } else { cb = callback.newMyCB(); } // - - - - - - - - - - - - - - // Select the list of referrer // - - - - - - - - - - - - - - callback.qyFKIn(cb, pkSet); final String referrerPropertyName = callback.getRfPrNm(); final String fixedCondition = xbuildReferrerCorrelatedFixedCondition(cb, referrerPropertyName); final String basePointAliasName = cb.getSqlClause().getBasePointAliasName(); final boolean hasFixedCondition = fixedCondition != null && fixedCondition.trim().length() > 0; if (hasFixedCondition) { cb.getSqlClause().registerWhereClause(fixedCondition, basePointAliasName); } cb.xregisterUnionQuerySynchronizer(new UnionQuery() { public void query(ConditionBean unionCB) { @SuppressWarnings("unchecked") REFERRER_CB referrerUnionCB = (REFERRER_CB) unionCB; // for when application uses union query in condition-bean set-upper. callback.qyFKIn(referrerUnionCB, pkSet); if (hasFixedCondition) { referrerUnionCB.getSqlClause().registerWhereClause(fixedCondition, basePointAliasName); } } }); if (pkSet.size() > 1) { callback.qyOdFKAsc(cb); cb.getOrderByComponent().exchangeFirstOrderByElementForLastOne(); } loadReferrerOption.delegateConditionBeanSettingUp(cb); if (cb.getSqlClause().hasSpecifiedSelectColumn(basePointAliasName)) { callback.spFKCol(cb); // specify required columns for relation } final List referrerList = callback.selRfLs(cb); loadReferrerOption.delegateEntitySettingUp(referrerList); // - - - - - - - - - - - - - - - - - - - - - - - - // Create the map of {primary key / referrer list} // - - - - - - - - - - - - - - - - - - - - - - - - final Map> pkReferrerListMap = new LinkedHashMap>(); for (REFERRER_ENTITY referrerEntity : referrerList) { final KEY referrerListKey; { final KEY foreignKeyValue = callback.getFKVal(referrerEntity); referrerListKey = toLoadReferrerMappingKey(foreignKeyValue); } if (!pkReferrerListMap.containsKey(referrerListKey)) { pkReferrerListMap.put(referrerListKey, new ArrayList()); } (pkReferrerListMap.get(referrerListKey)).add(referrerEntity); // for Reverse Reference. final LOCAL_ENTITY localEntity = pkLocalEntityMap.get(referrerListKey); callback.setlcEt(referrerEntity, localEntity); } // - - - - - - - - - - - - - - - - - - // Relate referrer list to base entity // - - - - - - - - - - - - - - - - - - for (LOCAL_ENTITY localEntity : localEntityList) { final KEY referrerListKey; { final KEY primaryKey = callback.getPKVal(localEntity); referrerListKey = toLoadReferrerMappingKey(primaryKey); } if (pkReferrerListMap.containsKey(referrerListKey)) { callback.setRfLs(localEntity, pkReferrerListMap.get(referrerListKey)); } else { callback.setRfLs(localEntity, new ArrayList()); } } // - - - - - - - - - - - - - - - - - - - - // Return callback to load nested referrer // - - - - - - - - - - - - - - - - - - - - return new NestedReferrerListGateway() { public void withNestedReferrer(ReferrerListHandler handler) { handler.handle(Collections.unmodifiableList(referrerList)); } }; } protected String xbuildReferrerCorrelatedFixedCondition(ConditionBean cb, String referrerPropertyName) { if (referrerPropertyName == null) { return null; } final DBMeta localDBMeta = asDBMeta(); if (!localDBMeta.hasReferrer(referrerPropertyName)) { // one-to-one referrer return null; } final ReferrerInfo referrerInfo = localDBMeta.findReferrerInfo(referrerPropertyName); return xdoBuildReferrerCorrelatedFixedCondition(cb, referrerInfo); } protected String xdoBuildReferrerCorrelatedFixedCondition(ConditionBean cb, ReferrerInfo referrerInfo) { final RelationInfo reverseRelation = referrerInfo.getReverseRelation(); if (reverseRelation == null) { return null; } if (!(reverseRelation instanceof ForeignInfo)) { String msg = "The reverse relation (referrer's reverse) should be foreign info: " + referrerInfo; throw new IllegalStateException(msg); } final ForeignInfo foreignInfo = (ForeignInfo) reverseRelation; final String fixedCondition = foreignInfo.getFixedCondition(); if (fixedCondition == null || fixedCondition.trim().length() == 0) { return null; } final String localAliasMark = HpFixedConditionQueryResolver.LOCAL_ALIAS_MARK; final String basePointAliasName = cb.getSqlClause().getBasePointAliasName(); return Srl.replace(fixedCondition, localAliasMark, basePointAliasName); } /** * Convert the primary key to mapping key for load-referrer.
* This default implementation is to-lower if string type. * @param The type of primary key. * @param value The value of primary key. (NotNull) * @return The value of primary key. (NotNull) */ @SuppressWarnings("unchecked") protected PK toLoadReferrerMappingKey(PK value) { if (value instanceof String) { // simple key return (PK) toLowerCaseIfString(value); } if (value instanceof Map) { // compound key final Map pkMap = (Map) value; final Map filteredMap = xnewLoadReferrerCompoundKeyMap(); for (Map.Entry entry : pkMap.entrySet()) { final String key = entry.getKey(); final Object element = entry.getValue(); if (element instanceof String) { filteredMap.put(key, toLowerCaseIfString(element)); } else { filteredMap.put(key, element); } } return (PK) filteredMap; } return value; } protected Map xnewLoadReferrerCompoundKeyMap() { return new LinkedHashMap(); } /** * @param The type of base entity. * @param The type of primary key. * @param The type of referrer conditionBean. * @param The type of referrer entity. */ protected static interface InternalLoadReferrerCallback { // for Base PK getPKVal(LOCAL_ENTITY entity); // getPrimaryKeyValue() void setRfLs(LOCAL_ENTITY entity, List referrerList); // setReferrerList() // for Referrer REFERRER_CB newMyCB(); // newMyConditionBean() void qyFKIn(REFERRER_CB cb, Collection pkList); // queryForeignKeyInScope() void qyOdFKAsc(REFERRER_CB cb); // queryAddOrderByForeignKeyAsc() void spFKCol(REFERRER_CB cb); // specifyForeignKeyColumn() List selRfLs(REFERRER_CB cb); // selectReferrerList() PK getFKVal(REFERRER_ENTITY entity); // getForeignKeyValue() void setlcEt(REFERRER_ENTITY referrerEntity, LOCAL_ENTITY localEntity); // setLocalEntity() String getRfPrNm(); // getReferrerPropertyName() } protected List xnewLRAryLs(ELEMENT entity) { final List ls = new ArrayList(1); ls.add(entity); return ls; } // assertLoadReferrerArgument() as Internal protected void xassLRArg(List entityList, ReferrerLoaderHandler handler) { assertObjectNotNull("LoadReferrer's entityList", entityList); assertObjectNotNull("LoadReferrer's handler", handler); } protected void xassLRArg(Entity entity, ReferrerLoaderHandler handler) { assertObjectNotNull("LoadReferrer's entity", entity); assertObjectNotNull("LoadReferrer's handler", handler); } protected void xassLRArg(List entityList, ReferrerConditionSetupper setupper) { assertObjectNotNull("LoadReferrer's entityList", entityList); assertObjectNotNull("LoadReferrer's setupper", setupper); } protected void xassLRArg(Entity entity, ReferrerConditionSetupper setupper) { assertObjectNotNull("LoadReferrer's entity", entity); assertObjectNotNull("LoadReferrer's setupper", setupper); } protected void xassLRArg(List entityList, LoadReferrerOption loadReferrerOption) { assertObjectNotNull("LoadReferrer's entityList", entityList); assertObjectNotNull("LoadReferrer's loadReferrerOption", loadReferrerOption); } protected void xassLRArg(Entity entity, LoadReferrerOption loadReferrerOption) { assertObjectNotNull("LoadReferrer's entity", entity); assertObjectNotNull("LoadReferrer's loadReferrerOption", loadReferrerOption); } protected BehaviorSelector xgetBSFLR() { // getBehaviorSelectorForLoadReferrer() as Internal assertBehaviorSelectorNotNull("loadReferrer"); return getBehaviorSelector(); } private void assertBehaviorSelectorNotNull(String methodName) { if (_behaviorSelector != null) { return; } final ExceptionMessageBuilder br = createExceptionMessageBuilder(); br.addNotice("Not found the selector of behavior in the behavior!"); br.addItem("Advice"); br.addElement("Please confirm the definition of the selector at your component configuration of DBFlute."); br.addElement("It is precondition that '" + methodName + "()' needs the selector instance."); br.addItem("Behavior"); br.addElement("Behavior for " + asTableDbName()); br.addItem("Attribute"); br.addElement("behaviorCommandInvoker : " + _behaviorCommandInvoker); br.addElement("behaviorSelector : " + _behaviorSelector); final String msg = br.buildExceptionMessage(); throw new IllegalBehaviorStateException(msg); } protected List xnewLRLs(ELEMENT element) { // newLoadReferrerList() as Internal List ls = new ArrayList(1); ls.add(element); return ls; } // =================================================================================== // Pull out Relation // ================= protected List helpPulloutInternally( List localEntityList, String foreignPropertyName) { assertObjectNotNull("localEntityList", localEntityList); assertObjectNotNull("foreignPropertyName", foreignPropertyName); final DBMeta dbmeta = asDBMeta(); final ForeignInfo foreignInfo = dbmeta.findForeignInfo(foreignPropertyName); final RelationInfo reverseInfo = foreignInfo.getReverseRelation(); final boolean existsReferrer = reverseInfo != null; final RelationOptionalFactory optionalFactory = xgetROpFactory(); final Map foreignBasicMap = new LinkedHashMap(); Map> foreignCollisionMap = null; // lazy-loaded final Map> foreignReferrerMap = new LinkedHashMap>(); for (LOCAL_ENTITY localEntity : localEntityList) { final FOREIGN_ENTITY foreignEntity = xextractPulloutForeignEntity(foreignInfo, reverseInfo, optionalFactory, localEntity); if (foreignEntity == null) { continue; } // _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // basically mapped foreign entities are unique instances // but according to circumstances, same PK different instance (rare case) // // e.g. normal pattern (no problem) // A - M - X // B - M - X // C - N - X // D - O - Y // // e.g. when OverRelation, might be following pattern // A - M - X // B - M - Y *same relation but different nested relation // C - N - X // D - O - Y // // when the latter pattern, the instance of A's M is different from the one of B's M // so it need to handle the unique as instance (don't use overridden equals() of entity) // _/_/_/_/_/_/_/_/_/_/ foreignCollisionMap = xsavePulloutForeignEntity(foreignBasicMap, foreignCollisionMap, foreignEntity); if (existsReferrer) { if (!foreignReferrerMap.containsKey(foreignEntity)) { foreignReferrerMap.put(foreignEntity, new ArrayList()); } foreignReferrerMap.get(foreignEntity).add(localEntity); } } if (existsReferrer) { for (Entry> entry : foreignReferrerMap.entrySet()) { final FOREIGN_ENTITY foreignEntity = entry.getKey(); final List mappedLocalList = entry.getValue(); final Object writtenObj = xextractPulloutReverseWrittenObject(foreignInfo, reverseInfo, optionalFactory, mappedLocalList); reverseInfo.write(foreignEntity, writtenObj); } } return xpreparePulloutResultList(foreignBasicMap, foreignCollisionMap); } @SuppressWarnings("unchecked") protected FOREIGN_ENTITY xextractPulloutForeignEntity( ForeignInfo foreignInfo, RelationInfo reverseInfo, RelationOptionalFactory optionalFactory, LOCAL_ENTITY localEntity) { final Object mightBeOptional = foreignInfo.read(localEntity); // non-reflection final FOREIGN_ENTITY foreignEntity; if (optionalFactory.isOptional(mightBeOptional)) { foreignEntity = (FOREIGN_ENTITY) optionalFactory.orElseNull(mightBeOptional); } else { foreignEntity = (FOREIGN_ENTITY) mightBeOptional; } return foreignEntity; } protected Map> xsavePulloutForeignEntity( Map foreignBasicMap, Map> foreignCollisionMap, FOREIGN_ENTITY foreignEntity) { final int instanceHash = foreignEntity.instanceHash(); if (!foreignBasicMap.containsKey(instanceHash)) { // first hash foreignBasicMap.put(instanceHash, foreignEntity); } else { // already exists, might be identical entity or collision final FOREIGN_ENTITY firstEntity = foreignBasicMap.get(instanceHash); // might be null if second collision if (firstEntity != null) { // means first collision in the hash if (foreignEntity == firstEntity) { // identical (not collision) return foreignCollisionMap; // no need to save it } } // _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // means hash collision, so use collision map // instance hash does not always provide unique value // so it need to handle collision // e.g. normal pattern // foreignBasicMap : {A - M, B - N, C - O, D - P} // foreignCollisionMap : null // // e.g. collision, B comes again // foreignBasicMap : {A - M, B - N} // foreignCollisionMap : null // ...{B - O} comes here // foreignBasicMap : {A - M, B - null} // foreignCollisionMap : {B - [N,O]} // _/_/_/_/_/_/_/_/_/_/ if (foreignCollisionMap == null) { // means first collision of all entities foreignCollisionMap = new LinkedHashMap>(2); // lazy-loaded } if (firstEntity != null) { // means first collision in the hash foreignBasicMap.put(instanceHash, null); // existence mark only } if (!foreignCollisionMap.containsKey(instanceHash)) { // means first collision in the hash foreignCollisionMap.put(instanceHash, new ArrayList(2)); } final List collisionList = foreignCollisionMap.get(instanceHash); for (FOREIGN_ENTITY collision : collisionList) { if (collision == foreignEntity) { // already exists return foreignCollisionMap; // no need to save it } } if (firstEntity != null) { // means first collision in the hash collisionList.add(firstEntity); } collisionList.add(foreignEntity); } return foreignCollisionMap; } protected Object xextractPulloutReverseWrittenObject(ForeignInfo foreignInfo, RelationInfo reverseInfo, RelationOptionalFactory optionalFactory, List mappedLocalList) { final Object writtenObj; if (foreignInfo.isOneToOne()) { if (mappedLocalList != null && !mappedLocalList.isEmpty()) { // should have only one element final LOCAL_ENTITY plainFirstElement = mappedLocalList.get(0); if (plainFirstElement != null && optionalFactory.isOptionalType(reverseInfo.getPropertyAccessType())) { writtenObj = optionalFactory.createOptionalPresentEntity(plainFirstElement); } else { writtenObj = plainFirstElement; } } else { writtenObj = null; } } else { // many-to-one so reverse is list writtenObj = mappedLocalList; } return writtenObj; } protected List xpreparePulloutResultList(Map foreignBasicMap, Map> foreignCollisionMap) { final List resultList = new ArrayList(foreignBasicMap.size() + 1); // + collision for (Entry entry : foreignBasicMap.entrySet()) { final FOREIGN_ENTITY foreignEntity = entry.getValue(); if (foreignEntity != null) { // basically here resultList.add(foreignEntity); } else { // means hash collision, so use collision map // _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/ // e.g. collision {B - O} comes // foreignBasicMap : {A - M, B - null} // foreignCollisionMap : {B - [N,O]} // _/_/_/_/_/_/_/_/_/_/ if (foreignCollisionMap == null) { // no way just in case String msg = "Not lazy-loaded the collision map: basicMap=" + foreignBasicMap; throw new IllegalStateException(msg); } final Integer instanceHash = entry.getKey(); final List savedList = foreignCollisionMap.get(instanceHash); // already exists if (savedList == null) { // no way just in case String msg = "Not found the saved entity list in the collision map:"; msg = msg + " key=" + instanceHash + ", collisionMap=" + foreignCollisionMap; throw new IllegalStateException(msg); } for (FOREIGN_ENTITY savedEntity : savedList) { // also contains first entity of collision resultList.add(savedEntity); } } } return resultList; } // =================================================================================== // Extract Column // ============== protected List helpExtractListInternally(List localEntityList, String propertyName) { assertObjectNotNull("localEntityList", localEntityList); assertObjectNotNull("propertyName", propertyName); final List valueList = new ArrayList(); return xdoHelpExtractSetInternally(localEntityList, propertyName, valueList); } protected Set helpExtractSetInternally(List localEntityList, String propertyName) { assertObjectNotNull("localEntityList", localEntityList); assertObjectNotNull("propertyName", propertyName); final Set valueSet = new LinkedHashSet(); return xdoHelpExtractSetInternally(localEntityList, propertyName, valueSet); } protected > COLLECTION xdoHelpExtractSetInternally( List localEntityList, String propertyName, COLLECTION collection) { assertObjectNotNull("localEntityList", localEntityList); assertObjectNotNull("propertyName", propertyName); final ColumnInfo columnInfo = asDBMeta().findColumnInfo(propertyName); for (LOCAL_ENTITY entity : localEntityList) { final COLUMN column = columnInfo.read(entity); if (column != null) { collection.add(column); } } return collection; } // =================================================================================== // Process Method // ============== // defined here (on the readable interface) for non-primary key value /** * {Framework Method} Filter the entity of insert. * @param entity The entity for insert. (NotNull) * @param option The optional option of insert. (NotNull, EmptyAllowed: when no option) */ protected void frameworkFilterEntityOfInsert(Entity entity, OptionalThing> option) { } /** * Filter the entity of insert. (basically for non-primary-key insert) * @param entity Target entity that the type is entity interface. (NotNull) * @param option The optional option of insert. (NotNull, EmptyAllowed: when no option) */ protected void filterEntityOfInsert(Entity entity, OptionalThing> option) { } // =================================================================================== // Delegate Entry // ============== protected int delegateSelectCountUniquely(ConditionBean cb) { return invoke(createSelectCountCBCommand(cb, true)); } protected int delegateSelectCountPlainly(ConditionBean cb) { return invoke(createSelectCountCBCommand(cb, false)); } protected RESULT delegateSelectEntity(ConditionBean cb, Class entityType) { return invoke(createSelectEntityCBCommand(cb, entityType)); } protected List delegateSelectList(ConditionBean cb, Class entityType) { return invoke(createSelectListCBCommand(cb, entityType)); } protected void delegateSelectCursor(ConditionBean cb, EntityRowHandler handler, Class entityType) { invoke(createSelectCursorCBCommand(cb, handler, entityType)); } protected RESULT delegateSelectNextVal(Class resultType) { return invoke(createSelectNextValCommand(resultType)); } protected RESULT delegateSelectNextValSub(Class resultType, String columnDbName, String sequenceName, Integer incrementSize, Integer cacheSize) { return invoke(createSelectNextValSubCommand(resultType, columnDbName, sequenceName, incrementSize, cacheSize)); } protected int delegateInsertNoPK(Entity entity, InsertOption option) { // only filtering for extension is supported (filtering for common columns is unsupported) assertEntityNotNull(entity); filterEntityOfInsert(entity, createOptionalInsertOption(option)); return invoke(createInsertEntityCommand(entity, option)); } protected OptionalThing> createOptionalInsertOption( InsertOption option) { return OptionalThing.ofNullable(option, () -> { throw new IllegalStateException("Not found the insert option."); }); } // =================================================================================== // Behavior Command // ================ // ----------------------------------------------------- // Warm up // ------- public void warmUpCommand() { { newConditionBean().localCQ(); // to load classes of condition-bean and condition-query } { final SelectCountCBCommand cmd = createSelectCountCBCommand(newConditionBean(), true); cmd.setInitializeOnly(true); invoke(cmd); } { final SelectCountCBCommand cmd = createSelectCountCBCommand(newConditionBean(), false); cmd.setInitializeOnly(true); invoke(cmd); } { @SuppressWarnings("unchecked") final Class entityType = (Class) asDBMeta().getEntityType(); final SelectEntityCBCommand cmd = createSelectEntityCBCommand(newConditionBean(), entityType); cmd.setInitializeOnly(true); invoke(cmd); } { @SuppressWarnings("unchecked") final Class entityType = (Class) asDBMeta().getEntityType(); final SelectListCBCommand cmd = createSelectListCBCommand(newConditionBean(), entityType); cmd.setInitializeOnly(true); invoke(cmd); } } // ----------------------------------------------------- // Read // ---- protected SelectCountCBCommand createSelectCountCBCommand(ConditionBean cb, boolean uniqueCount) { assertBehaviorCommandInvoker("createSelectCountCBCommand"); final SelectCountCBCommand cmd = newSelectCountCBCommand(); xsetupSelectCommand(cmd); cmd.setConditionBean(cb); cmd.setUniqueCount(uniqueCount); return cmd; } protected SelectCountCBCommand newSelectCountCBCommand() { return new SelectCountCBCommand(); } protected SelectEntityCBCommand createSelectEntityCBCommand(ConditionBean cb, Class entityType) { assertBehaviorCommandInvoker("createSelectEntityCBCommand"); final SelectEntityCBCommand cmd = newSelectEntityCBCommand(); xsetupSelectCommand(cmd); cmd.setConditionBean(cb); cmd.setEntityType(entityType); return cmd; } protected SelectEntityCBCommand newSelectEntityCBCommand() { return new SelectEntityCBCommand(); } protected SelectListCBCommand createSelectListCBCommand(ConditionBean cb, Class entityType) { assertBehaviorCommandInvoker("createSelectListCBCommand"); final SelectListCBCommand cmd = newSelectListCBCommand(); xsetupSelectCommand(cmd); cmd.setConditionBean(cb); cmd.setEntityType(entityType); return cmd; } protected SelectListCBCommand newSelectListCBCommand() { return new SelectListCBCommand(); } protected SelectCursorCBCommand createSelectCursorCBCommand(ConditionBean cb, EntityRowHandler entityRowHandler, Class entityType) { assertBehaviorCommandInvoker("createSelectCursorCBCommand"); final SelectCursorCBCommand cmd = newSelectCursorCBCommand(); xsetupSelectCommand(cmd); cmd.setConditionBean(cb); cmd.setEntityType(entityType); cmd.setEntityRowHandler(entityRowHandler); return cmd; } protected SelectCursorCBCommand newSelectCursorCBCommand() { return new SelectCursorCBCommand(); } protected SelectNextValCommand createSelectNextValCommand(Class resultType) { assertBehaviorCommandInvoker("createSelectNextValCommand"); final SelectNextValCommand cmd = newSelectNextValCommand(); xsetupSelectCommand(cmd); cmd.setResultType(resultType); cmd.setDBMeta(asDBMeta()); cmd.setSequenceCacheHandler(_behaviorCommandInvoker.getSequenceCacheHandler()); return cmd; } protected SelectNextValCommand newSelectNextValCommand() { return new SelectNextValCommand(); } protected SelectNextValCommand createSelectNextValSubCommand(Class resultType, String columnDbName, String sequenceName, Integer incrementSize, Integer cacheSize) { assertBehaviorCommandInvoker("createSelectNextValCommand"); final SelectNextValSubCommand cmd = newSelectNextValSubCommand(); xsetupSelectCommand(cmd); cmd.setResultType(resultType); cmd.setDBMeta(asDBMeta()); cmd.setSequenceCacheHandler(_behaviorCommandInvoker.getSequenceCacheHandler()); cmd.setColumnInfo(asDBMeta().findColumnInfo(columnDbName)); cmd.setSequenceName(sequenceName); cmd.setIncrementSize(incrementSize); cmd.setCacheSize(cacheSize); return cmd; } protected SelectNextValSubCommand newSelectNextValSubCommand() { return new SelectNextValSubCommand(); } protected SelectScalarCBCommand createSelectScalarCBCommand(ConditionBean cb, Class resultType, SelectClauseType selectClauseType) { assertBehaviorCommandInvoker("createSelectScalarCBCommand"); final SelectScalarCBCommand cmd = newSelectScalarCBCommand(); xsetupSelectCommand(cmd); cmd.setConditionBean(cb); cmd.setResultType(resultType); cmd.setSelectClauseType(selectClauseType); return cmd; } protected SelectScalarCBCommand newSelectScalarCBCommand() { return new SelectScalarCBCommand(); } protected void xsetupSelectCommand(AbstractAllBehaviorCommand cmd) { cmd.setTableDbName(asTableDbName()); _behaviorCommandInvoker.injectComponentProperty(cmd); } // ----------------------------------------------------- // Write // ----- // defined here (on the readable interface) for non-primary key value protected InsertEntityCommand createInsertEntityCommand(Entity entity, InsertOption option) { assertBehaviorCommandInvoker("createInsertEntityCommand"); final InsertEntityCommand cmd = newInsertEntityCommand(); xsetupEntityCommand(cmd, entity); cmd.setInsertOption(option); return cmd; } protected InsertEntityCommand newInsertEntityCommand() { return new InsertEntityCommand(); } protected void xsetupEntityCommand(AbstractCountableUpdateCommand cmd, Entity entity) { cmd.setTableDbName(asTableDbName()); _behaviorCommandInvoker.injectComponentProperty(cmd); cmd.setEntity(entity); } // ----------------------------------------------------- // Assist Helper // ------------- /** * Invoke the command of behavior. * @param The type of result. * @param behaviorCommand The command of behavior. (NotNull) * @return The instance of result. (NullAllowed) */ protected RESULT invoke(BehaviorCommand behaviorCommand) { return _behaviorCommandInvoker.invoke(behaviorCommand); } protected void assertBehaviorCommandInvoker(String methodName) { if (_behaviorCommandInvoker != null) { return; } // don't use exception thrower because the thrower is created by BehaviorCommandInvoker final ExceptionMessageBuilder br = createExceptionMessageBuilder(); br.addNotice("Not found the invoker of behavior command in the behavior!"); br.addItem("Advice"); br.addElement("Please confirm the definition of the set-upper at your component configuration of DBFlute."); br.addElement("It is precondition that '" + methodName + "()' needs the invoker instance."); br.addItem("Behavior"); br.addElement("Behavior for " + asTableDbName()); br.addItem("Attribute"); br.addElement("behaviorCommandInvoker : " + _behaviorCommandInvoker); br.addElement("behaviorSelector : " + _behaviorSelector); final String msg = br.buildExceptionMessage(); throw new IllegalBehaviorStateException(msg); } // =================================================================================== // Optimistic Lock Info // ==================== /** * Does the entity have a value of version-no? * @param entity The instance of entity. (NotNull) * @return The determination, true or false. */ protected boolean hasVersionNoValue(Entity entity) { return false; // as default } /** * Does the entity have a value of update-date? * @param entity The instance of entity. (NotNull) * @return The determination, true or false. */ protected boolean hasUpdateDateValue(Entity entity) { return false; // as default } // =================================================================================== // Optional Handling // ================= /** * Create present or null entity as relation optional. * @param relationTitle The title of relation for exception message. (NotNull) * @param relationRow The entity instance of relation row. (NullAllowed) * @return The optional object for the entity, which has present or null entity. (NotNull) */ protected Object toRelationOptional(final String relationTitle, Object relationRow) { assertObjectNotNull("relationTitle", relationTitle); final RelationOptionalFactory factory = xgetROpFactory(); final Object result; if (relationRow != null) { result = factory.createOptionalPresentEntity(relationRow); } else { result = factory.createOptionalNullEntity(new OptionalThingExceptionThrower() { public void throwNotFoundException() { String msg = "Not found the relation row for: " + relationTitle; throw new EntityAlreadyDeletedException(msg); } }); } return result; } /** * Is the property type optional for relation? * @param relationPropertyType The type of relation property. (NotNull) * @return The determination, true or false. */ protected boolean isRelationOptional(Class relationPropertyType) { assertObjectNotNull("relationPropertyType", relationPropertyType); final RelationOptionalFactory factory = xgetROpFactory(); return factory.isOptionalType(relationPropertyType); } protected RelationOptionalFactory xgetROpFactory() { assertBehaviorCommandInvoker("xgetROpFactory"); return _behaviorCommandInvoker.getRelationOptionalFactory(); } // =================================================================================== // Type Helper // =========== protected abstract Class typeOfSelectedEntity(); protected abstract Class typeOfHandlingEntity(); protected abstract Class typeOfHandlingConditionBean(); protected ENTITY downcast(Entity entity) { return helpEntityDowncastInternally(entity, typeOfHandlingEntity()); } @SuppressWarnings("unchecked") protected RESULT helpEntityDowncastInternally(Entity entity, Class clazz) { assertEntityNotNull(entity); assertObjectNotNull("clazz", clazz); try { return (RESULT) entity; } catch (ClassCastException e) { String classTitle = DfTypeUtil.toClassTitle(clazz); String msg = "The entity should be " + classTitle + " but it was: " + entity.getClass(); throw new IllegalStateException(msg, e); } } protected CB downcast(ConditionBean cb) { return helpConditionBeanDowncastInternally(cb, typeOfHandlingConditionBean()); } @SuppressWarnings("unchecked") protected CB helpConditionBeanDowncastInternally(ConditionBean cb, Class clazz) { assertCBNotNull(cb); assertObjectNotNull("clazz", clazz); try { return (CB) cb; } catch (ClassCastException e) { String classTitle = DfTypeUtil.toClassTitle(clazz); String msg = "The condition-bean should be " + classTitle + " but it was: " + cb.getClass(); throw new IllegalStateException(msg, e); } } @SuppressWarnings("unchecked") protected List downcast(List entityList) { return (List) entityList; } // =================================================================================== // Exception Helper // ================ protected BehaviorExceptionThrower createBhvExThrower() { assertBehaviorCommandInvoker("createBhvExThrower"); return _behaviorCommandInvoker.createBehaviorExceptionThrower(); } protected ConditionBeanExceptionThrower createCBExThrower() { return new ConditionBeanExceptionThrower(); } protected ExceptionMessageBuilder createExceptionMessageBuilder() { return new ExceptionMessageBuilder(); } // =================================================================================== // Assert Helper // ============= // ----------------------------------------------------- // Assert Object // ------------- /** * Assert that the object is not null. * @param variableName The variable name for message. (NotNull) * @param value The value the checked variable. (NotNull) * @throws IllegalArgumentException When the variable name or the variable is null. */ protected void assertObjectNotNull(String variableName, Object value) { if (variableName == null) { String msg = "The value should not be null: variableName=null value=" + value; throw new IllegalArgumentException(msg); } if (value == null) { String msg = "The value should not be null: variableName=" + variableName; throw new IllegalArgumentException(msg); } } /** * Assert that the entity is not null. * @param entity The instance of entity to be checked. (NotNull) */ protected void assertEntityNotNull(Entity entity) { if (entity == null) { String msg = "The entity should not be null: table=" + asTableDbName(); throw new IllegalArgumentException(msg); } } /** * Assert that the entity has primary-key value. e.g. insert(), update(), delete() * @param entity The instance of entity to be checked. (NotNull) */ protected void assertEntityNotNullAndHasPrimaryKeyValue(Entity entity) { assertEntityNotNull(entity); final Set uniqueDrivenPropSet = entity.myuniqueDrivenProperties(); if (uniqueDrivenPropSet.isEmpty()) { // PK, basically here if (!entity.hasPrimaryKeyValue()) { createBhvExThrower().throwEntityPrimaryKeyNotFoundException(entity); } } else { // unique-driven for (String prop : uniqueDrivenPropSet) { final ColumnInfo columnInfo = asDBMeta().findColumnInfo(prop); if (columnInfo != null) { final Object value = columnInfo.read(entity); if (value == null) { createBhvExThrower().throwEntityUniqueKeyNotFoundException(entity); } } } } } /** * Assert that the entity list is not null. * @param entityList The list of entity to be checked. (NotNull) */ protected void assertEntityListNotNull(List entityList) { if (entityList == null) { String msg = "The list of entity should not be null: table=" + asTableDbName(); throw new IllegalArgumentException(msg); } } /** * Assert that the callback of condition-bean is not null. * @param cbCall The interface of condition-bean to be checked. (NotNull) */ protected void assertCBCallNotNull(CBCall cbCall) { assertObjectNotNull("cbLambda", cbCall); } /** * Assert that the condition-bean is not null. * @param cb The instance of condition-bean to be checked. (NotNull) */ protected void assertCBNotNull(ConditionBean cb) { if (cb == null) { String msg = "The condition-bean should not be null: table=" + asTableDbName(); throw new IllegalArgumentException(msg); } } /** * Assert that the condition-bean state is valid. * @param cb The instance of condition-bean to be checked. (NotNull) */ protected void assertCBStateValid(ConditionBean cb) { assertCBNotNull(cb); assertCBNotDreamCruise(cb); } /** * Assert that the condition-bean is not dream cruise. * @param cb The instance of condition-bean to be checked. (NotNull) */ protected void assertCBNotDreamCruise(ConditionBean cb) { if (cb.xisDreamCruiseShip()) { String msg = "The condition-bean should not be dream cruise: " + cb.getClass(); throw new IllegalConditionBeanOperationException(msg); } } protected void assertConditionBeanSelectResource(CB cb, Class entityType) { assertCBStateValid(cb); assertObjectNotNull("entityType", entityType); assertSpecifyDerivedReferrerEntityProperty(cb, entityType); } protected void assertSpecifyDerivedReferrerEntityProperty(ConditionBean cb, Class entityType) { final List aliasList = cb.getSqlClause().getSpecifiedDerivingAliasList(); if (aliasList.isEmpty()) { return; } final DfBeanDesc beanDesc = DfBeanDescFactory.getBeanDesc(entityType); for (String alias : aliasList) { if (isEntityDerivedMappable() && alias.startsWith(DERIVED_MAPPABLE_ALIAS_PREFIX)) { continue; } DfPropertyDesc pd = null; if (beanDesc.hasPropertyDesc(alias)) { // case insensitive pd = beanDesc.getPropertyDesc(alias); } else { final String noUnsco = Srl.replace(alias, "_", ""); if (beanDesc.hasPropertyDesc(noUnsco)) { // flexible name pd = beanDesc.getPropertyDesc(noUnsco); } } if (pd != null && pd.hasWriteMethod()) { continue; } throwSpecifyDerivedReferrerEntityPropertyNotFoundException(alias, entityType); } } // ----------------------------------------------------- // Assert String // ------------- /** * Assert that the entity is not null and not trimmed empty. * @param variableName The variable name for message. (NotNull) * @param value The value the checked variable. (NotNull) * @throws IllegalArgumentException When the argument is null or empty. */ protected void assertStringNotNullAndNotTrimmedEmpty(String variableName, String value) { assertObjectNotNull("variableName", variableName); assertObjectNotNull(variableName, value); if (value.trim().length() == 0) { String msg = "The value should not be empty: variableName=" + variableName + " value=" + value; throw new IllegalArgumentException(msg); } } // ----------------------------------------------------- // Assert List // ----------- /** * Assert that the list is empty. * @param ls The instance of list to be checked. (NotNull) */ protected void assertListNotNullAndEmpty(List ls) { assertObjectNotNull("ls", ls); if (!ls.isEmpty()) { String msg = "The list should be empty: ls=" + ls.toString(); throw new IllegalArgumentException(msg); } } /** * Assert that the list is not empty. * @param ls The instance of list to be checked. (NotNull) */ protected void assertListNotNullAndNotEmpty(List ls) { assertObjectNotNull("ls", ls); if (ls.isEmpty()) { String msg = "The list should not be empty: ls=" + ls.toString(); throw new IllegalArgumentException(msg); } } /** * Assert that the list having only one. * @param ls The instance of list to be checked. (NotNull) */ protected void assertListNotNullAndHasOnlyOne(List ls) { assertObjectNotNull("ls", ls); if (ls.size() != 1) { String msg = "The list should contain only one object: ls=" + ls.toString(); throw new IllegalArgumentException(msg); } } // =================================================================================== // General Helper // ============== /** * To lower case if the type is String. * @param obj The object might be string. (NullAllowed) * @return The lower string or plain object. (NullAllowed) */ protected Object toLowerCaseIfString(Object obj) { if (obj != null && obj instanceof String) { return ((String) obj).toLowerCase(); } return obj; } /** * Get the value of line separator. * @return The value of line separator. (NotNull) */ protected String ln() { return DBFluteSystem.ln(); } // =================================================================================== // Accessor // ======== /** * Get the invoker of behavior command. * @return The invoker of behavior command. (NullAllowed: But normally NotNull) */ protected BehaviorCommandInvoker getBehaviorCommandInvoker() { return _behaviorCommandInvoker; } /** * Set the invoker of behavior command. * @param behaviorCommandInvoker The invoker of behavior command. (NotNull) */ public void setBehaviorCommandInvoker(BehaviorCommandInvoker behaviorCommandInvoker) { this._behaviorCommandInvoker = behaviorCommandInvoker; } /** * Get the selector of behavior. * @return The select of behavior. (NullAllowed: But normally NotNull) */ protected BehaviorSelector getBehaviorSelector() { return _behaviorSelector; } /** * Set the selector of behavior. * @param behaviorSelector The selector of behavior. (NotNull) */ public void setBehaviorSelector(BehaviorSelector behaviorSelector) { this._behaviorSelector = behaviorSelector; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy