org.dbflute.bhv.AbstractBehaviorReadable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dbflute-runtime Show documentation
Show all versions of dbflute-runtime Show documentation
The runtime library of DBFlute
/*
* 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 extends RESULT> entityType) {
return helpSelectEntityInternally(cb, entityType);
}
protected ENTITY facadeSelectEntityWithDeletedCheck(CB cb) {
return doSelectEntityWithDeletedCheck(cb, typeOfSelectedEntity());
}
protected RESULT doSelectEntityWithDeletedCheck(CB cb, Class extends RESULT> entityType) {
assertCBStateValid(cb);
assertObjectNotNull("entityType", entityType);
return helpSelectEntityWithDeletedCheckInternally(cb, entityType);
}
// -----------------------------------------------------
// Internal Helper
// ---------------
protected RESULT helpSelectEntityInternally(CB cb, Class extends RESULT> 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 extends RESULT> 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 extends RESULT> entityType) {
return helpSelectListInternally(cb, entityType);
}
// -----------------------------------------------------
// Internal Helper
// ---------------
protected ListResultBean helpSelectListInternally(CB cb, Class extends RESULT> 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 extends Entity> 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 extends RESULT> entityType) {
return helpSelectPageInternally(cb, entityType);
}
protected PagingResultBean helpSelectPageInternally(CB cb, Class extends RESULT> 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 extends RESULT> 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 extends Entity> 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 extends RESULT> 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 extends RESULT> 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 extends RESULT> 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 extends ConditionBean, RESULT> 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 extends Entity> 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 extends Entity> entityList, ReferrerConditionSetupper extends ConditionBean> setupper) {
assertObjectNotNull("LoadReferrer's entityList", entityList);
assertObjectNotNull("LoadReferrer's setupper", setupper);
}
protected void xassLRArg(Entity entity, ReferrerConditionSetupper extends ConditionBean> setupper) {
assertObjectNotNull("LoadReferrer's entity", entity);
assertObjectNotNull("LoadReferrer's setupper", setupper);
}
protected void xassLRArg(List extends Entity> entityList,
LoadReferrerOption extends ConditionBean, ? extends Entity> loadReferrerOption) {
assertObjectNotNull("LoadReferrer's entityList", entityList);
assertObjectNotNull("LoadReferrer's loadReferrerOption", loadReferrerOption);
}
protected void xassLRArg(Entity entity, LoadReferrerOption extends ConditionBean, ? extends Entity> 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 extends RESULT> entityType) {
return invoke(createSelectEntityCBCommand(cb, entityType));
}
protected List delegateSelectList(ConditionBean cb, Class extends RESULT> entityType) {
return invoke(createSelectListCBCommand(cb, entityType));
}
protected void delegateSelectCursor(ConditionBean cb, EntityRowHandler handler,
Class extends RESULT> 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 extends ConditionBean> 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 extends ConditionBean> 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 extends ENTITY> entityType = (Class extends ENTITY>) asDBMeta().getEntityType();
final SelectEntityCBCommand extends ENTITY> cmd = createSelectEntityCBCommand(newConditionBean(), entityType);
cmd.setInitializeOnly(true);
invoke(cmd);
}
{
@SuppressWarnings("unchecked")
final Class extends ENTITY> entityType = (Class extends ENTITY>) asDBMeta().getEntityType();
final SelectListCBCommand extends ENTITY> 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 extends RESULT> 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 extends RESULT> 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 extends RESULT> 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