org.dbflute.bhv.AbstractBehaviorWritable 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-2023 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.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.dbflute.Entity;
import org.dbflute.bhv.core.BehaviorCommandMeta;
import org.dbflute.bhv.core.command.AbstractBatchUpdateCommand;
import org.dbflute.bhv.core.command.BatchDeleteCommand;
import org.dbflute.bhv.core.command.BatchDeleteNonstrictCommand;
import org.dbflute.bhv.core.command.BatchInsertCommand;
import org.dbflute.bhv.core.command.BatchUpdateCommand;
import org.dbflute.bhv.core.command.BatchUpdateNonstrictCommand;
import org.dbflute.bhv.core.command.DeleteEntityCommand;
import org.dbflute.bhv.core.command.DeleteNonstrictEntityCommand;
import org.dbflute.bhv.core.command.InsertEntityCommand;
import org.dbflute.bhv.core.command.QueryDeleteCBCommand;
import org.dbflute.bhv.core.command.QueryInsertCBCommand;
import org.dbflute.bhv.core.command.QueryUpdateCBCommand;
import org.dbflute.bhv.core.command.UpdateEntityCommand;
import org.dbflute.bhv.core.command.UpdateNonstrictEntityCommand;
import org.dbflute.bhv.core.context.ResourceContext;
import org.dbflute.bhv.writable.DeleteOption;
import org.dbflute.bhv.writable.InsertOption;
import org.dbflute.bhv.writable.QueryInsertSetupper;
import org.dbflute.bhv.writable.UpdateOption;
import org.dbflute.bhv.writable.WritableOption;
import org.dbflute.bhv.writable.WritableOptionCall;
import org.dbflute.cbean.ConditionBean;
import org.dbflute.cbean.scoping.SpecifyQuery;
import org.dbflute.dbmeta.DBMeta;
import org.dbflute.dbmeta.info.ColumnInfo;
import org.dbflute.dbmeta.info.UniqueInfo;
import org.dbflute.exception.EntityAlreadyDeletedException;
import org.dbflute.exception.EntityAlreadyUpdatedException;
import org.dbflute.exception.IllegalBehaviorStateException;
import org.dbflute.exception.IllegalConditionBeanOperationException;
import org.dbflute.exception.OptimisticLockColumnValueNullException;
import org.dbflute.helper.message.ExceptionMessageBuilder;
import org.dbflute.hook.CommonColumnAutoSetupper;
import org.dbflute.optional.OptionalThing;
/**
* The abstract class of writable 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 AbstractBehaviorWritable extends AbstractBehaviorReadable
implements BehaviorWritable {
// ===================================================================================
// Definition
// ==========
protected static final int[] EMPTY_INT_ARRAY = new int[] {};
// ===================================================================================
// Attribute
// =========
/** The auto-set-upper of common column. (NotNull) */
protected CommonColumnAutoSetupper _commonColumnAutoSetupper;
// ===================================================================================
// Entity Update
// =============
// -----------------------------------------------------
// Insert
// ------
protected void doInsert(ENTITY entity, InsertOption option) {
assertEntityNotNull(entity);
prepareInsertOption(option);
delegateInsert(entity, option);
}
protected void prepareInsertOption(InsertOption option) {
if (option == null) {
return;
}
assertInsertOptionStatus(option);
if (option.hasSpecifiedInsertColumn()) {
final CB cb = createCBForSpecifiedUpdate();
option.resolveInsertColumnSpecification(cb);
}
}
protected void assertInsertOptionStatus(InsertOption extends ConditionBean> option) {
if (option.isCommonColumnAutoSetupDisabled() && !asDBMeta().hasCommonColumn()) {
String msg = "The common column auto-setup disabling was set to the table not defined common columns:";
msg = msg + " table=" + asTableDbName() + " option=" + option;
throw new IllegalStateException(msg);
}
if (option.isPrimaryKeyIdentityDisabled() && !asDBMeta().hasIdentity()) {
String msg = "The identity disabling was set to the table not defined identity:";
msg = msg + " table=" + asTableDbName() + " option=" + option;
throw new IllegalStateException(msg);
}
}
protected CB createCBForSpecifiedUpdate() {
final CB cb = newConditionBean();
cb.xsetupForSpecifiedUpdate();
return cb;
}
protected InsertOption createInsertOption(WritableOptionCall> opCall) {
assertInsertOpCallNotNull(opCall);
final InsertOption op = newInsertOption();
opCall.callback(op);
return op;
}
protected InsertOption createPlainInsertOption() { // for e.g. default use
return newInsertOption();
}
protected InsertOption newInsertOption() {
return new InsertOption();
}
protected void assertInsertOpCallNotNull(WritableOptionCall> opCall) { // for varyingInsert()
assertObjectNotNull("opLambda (for insert)", opCall);
}
// -----------------------------------------------------
// Create
// ------
/** {@inheritDoc} */
public void create(Entity entity, InsertOption extends ConditionBean> option) {
doCreate(entity, option);
}
protected void doCreate(Entity entity, InsertOption extends ConditionBean> option) {
doInsert(downcast(entity), downcast(option));
}
// -----------------------------------------------------
// Update
// ------
protected void doUpdate(ENTITY entity, UpdateOption option) {
prepareEntityUpdate(entity, option);
helpUpdateInternally(entity, option);
}
protected void doUpdateNonstrict(ENTITY entity, UpdateOption option) {
prepareEntityUpdate(entity, option);
helpUpdateNonstrictInternally(entity, option);
}
protected void prepareEntityUpdate(ENTITY entity, UpdateOption option) {
assertEntityNotNull(entity);
prepareUpdateOption(option);
prepareEntityUpdateOption(entity, option);
}
protected void prepareUpdateOption(UpdateOption option) { // all update commands
if (option == null) {
return;
}
assertUpdateOptionStatus(option);
if (option.hasSelfSpecification()) {
option.resolveSelfSpecification(() -> createCBForVaryingUpdate());
}
if (option.hasSpecifiedUpdateColumn()) {
final CB cb = createCBForSpecifiedUpdate();
option.resolveUpdateColumnSpecification(cb);
}
}
protected void prepareEntityUpdateOption(ENTITY entity, UpdateOption option) { // only for entity update
if (option == null) {
return;
}
if (option.hasUniqueByUniqueInfo()) {
reflectUniqueDriven(entity, option.getUniqueByUniqueInfo());
}
}
protected void reflectUniqueDriven(ENTITY entity, UniqueInfo uniqueInfo) {
final List uniqueColumnList = uniqueInfo.getUniqueColumnList();
for (ColumnInfo columnInfo : uniqueColumnList) {
entity.myuniqueByProperty(columnInfo.getPropertyName());
}
}
protected CB createCBForVaryingUpdate() {
final CB cb = newConditionBean();
cb.xsetupForVaryingUpdate();
return cb;
}
protected void helpUpdateInternally(RESULT entity, UpdateOption option) {
assertEntityNotNull(entity);
assertEntityHasOptimisticLockValue(entity);
final int updatedCount = delegateUpdate(entity, option);
if (updatedCount == 0) {
throwUpdateEntityAlreadyDeletedException(entity);
} else if (updatedCount > 1) {
throwUpdateEntityDuplicatedException(entity, updatedCount);
}
helpReloadPrimaryKeyIfUniqueByIfNeeds(entity, option);
}
protected void helpUpdateNonstrictInternally(RESULT entity, UpdateOption option) {
assertEntityNotNull(entity);
final int updatedCount = delegateUpdateNonstrict(entity, option);
if (updatedCount == 0) {
throwUpdateEntityAlreadyDeletedException(entity);
} else if (updatedCount > 1) {
throwUpdateEntityDuplicatedException(entity, updatedCount);
}
helpReloadPrimaryKeyIfUniqueByIfNeeds(entity, option);
}
protected void throwUpdateEntityAlreadyDeletedException(ENTITY entity) {
createBhvExThrower().throwUpdateEntityAlreadyDeletedException(entity);
}
protected void throwUpdateEntityDuplicatedException(ENTITY entity, int count) {
createBhvExThrower().throwUpdateEntityDuplicatedException(entity, count);
}
protected void assertUpdateOptionStatus(UpdateOption extends ConditionBean> option) {
if (option.isCommonColumnAutoSetupDisabled() && !asDBMeta().hasCommonColumn()) {
String msg = "The common column auto-setup disabling was set to the table not defined common columns:";
msg = msg + " table=" + asTableDbName() + " option=" + option;
throw new IllegalStateException(msg);
}
}
protected UpdateOption createUpdateOption(WritableOptionCall> opCall) {
assertUpdateOpCallNotNull(opCall);
final UpdateOption op = newUpdateOption();
opCall.callback(op);
return op;
}
protected UpdateOption createPlainUpdateOption() { // for e.g. default use
return newUpdateOption();
}
protected UpdateOption newUpdateOption() {
return new UpdateOption();
}
protected void assertUpdateOpCallNotNull(WritableOptionCall> opCall) { // for varyingUpdate()
assertObjectNotNull("opLambda (for update)", opCall);
}
protected void helpReloadPrimaryKeyIfUniqueByIfNeeds(RESULT entity, UpdateOption option) {
if (option == null || !option.isReloadPrimaryKeyIfUniqueBy()) {
return;
}
final Set uniqueProp = entity.myuniqueDrivenProperties();
if (uniqueProp.isEmpty()) { // updated by PK normally
return;
}
final DBMeta dbmeta = entity.asDBMeta();
if (!dbmeta.hasPrimaryKey()) { // no PK table but has unique key
return;
}
final CB cb = newConditionBean();
final List pkList = dbmeta.getPrimaryInfo().getPrimaryColumnList();
for (ColumnInfo pk : pkList) {
cb.invokeSpecifyColumn(pk.getPropertyName());
}
for (String uq : uniqueProp) {
cb.localCQ().invokeQueryEqual(uq, dbmeta.findColumnInfo(uq).read(entity));
}
final Entity read = readEntityWithDeletedCheck(cb);
dbmeta.acceptPrimaryKeyMap(entity, dbmeta.extractPrimaryKeyMap(read));
}
// -----------------------------------------------------
// Modify
// ------
/** {@inheritDoc} */
public void modify(Entity entity, UpdateOption extends ConditionBean> option) {
doModify(entity, option);
}
protected void doModify(Entity entity, UpdateOption extends ConditionBean> option) {
doUpdate(downcast(entity), downcast(option));
}
/** {@inheritDoc} */
public void modifyNonstrict(Entity entity, UpdateOption extends ConditionBean> option) {
doModifyNonstrict(entity, option);
}
protected void doModifyNonstrict(Entity entity, UpdateOption extends ConditionBean> option) {
if (asDBMeta().hasOptimisticLock()) {
doUpdateNonstrict(downcast(entity), downcast(option));
} else {
doUpdate(downcast(entity), downcast(option));
}
}
// -----------------------------------------------------
// Insert or Update
// ----------------
protected void doInsertOrUpdate(ENTITY entity, InsertOption insertOption, UpdateOption updateOption) {
assertEntityNotNull(entity);
insertOption = filterInsertOrUpdateInsertOption(insertOption);
updateOption = filterInsertOrUpdateUpdateOption(updateOption);
helpInsertOrUpdateInternally(entity, insertOption, updateOption);
}
protected void doInsertOrUpdateNonstrict(ENTITY entity, InsertOption insertOption, UpdateOption updateOption) {
assertEntityNotNull(entity);
insertOption = filterInsertOrUpdateInsertOption(insertOption);
updateOption = filterInsertOrUpdateUpdateOption(updateOption);
helpInsertOrUpdateNonstrictInternally(entity, insertOption, updateOption);
}
// #for_now jflute option may be null so use filtering way here, want to unify (2021/11/20)
protected InsertOption filterInsertOrUpdateInsertOption(InsertOption insertOption) {
return insertOption;
}
protected UpdateOption filterInsertOrUpdateUpdateOption(UpdateOption updateOption) {
if (isInsertOrUpdateCountPreCheck()) {
if (updateOption == null) { // the argument is null allowed
updateOption = newUpdateOption();
}
updateOption.precheckInsertOrUpdateCount();
}
return updateOption;
}
protected boolean isInsertOrUpdateCountPreCheck() {
return false; // might be overridden by generator option
}
protected void helpInsertOrUpdateInternally(RESULT entity, InsertOption insertOption,
UpdateOption updateOption) {
assertEntityNotNull(entity);
if (helpDetermineInsertOrUpdateDirectInsert(entity, insertOption, updateOption)) {
doCreate(entity, insertOption);
return;
}
RuntimeException updateException = null;
try {
doModify(entity, updateOption);
} catch (EntityAlreadyUpdatedException e) { // already updated (or means not found)
updateException = e;
} catch (EntityAlreadyDeletedException e) { // means not found
updateException = e;
} catch (OptimisticLockColumnValueNullException e) { // means insert?
updateException = e;
}
if (updateException == null) {
return;
}
final CB cb = newConditionBean();
final Set uniqueDrivenProperties = entity.myuniqueDrivenProperties();
if (uniqueDrivenProperties != null && !uniqueDrivenProperties.isEmpty()) {
for (String prop : uniqueDrivenProperties) {
final DBMeta dbmeta = entity.asDBMeta();
final ColumnInfo columnInfo = dbmeta.findColumnInfo(prop);
final Object value = columnInfo.read(entity); // already checked in update process
cb.localCQ().invokeQueryEqual(columnInfo.getColumnDbName(), value);
}
} else {
cb.acceptPrimaryKeyMap(asDBMeta().extractPrimaryKeyMap(entity));
}
if (readCount(cb) == 0) { // anyway if not found, insert
doCreate(entity, insertOption);
} else {
throw updateException;
}
}
protected void helpInsertOrUpdateNonstrictInternally(RESULT entity, InsertOption insertOption,
UpdateOption updateOption) {
assertEntityNotNull(entity);
if (helpDetermineInsertOrUpdateDirectInsert(entity, insertOption, updateOption)) {
doCreate(entity, insertOption);
} else {
try {
doModifyNonstrict(entity, updateOption);
} catch (EntityAlreadyDeletedException ignored) { // means not found
doCreate(entity, insertOption);
}
}
}
protected boolean helpDetermineInsertOrUpdateDirectInsert(Entity entity, InsertOption insertOption, UpdateOption updateOption) {
final Set uniqueDrivenProperties = entity.myuniqueDrivenProperties();
if (uniqueDrivenProperties.isEmpty() && !entity.hasPrimaryKeyValue()) {
return true; // cannot update, may be identity PK so insert only
}
// no identity here
if (updateOption != null && updateOption.isInsertOrUpdateCountPreCheck()) {
// basically to avoid Otegaru Deadlock of MySQL RepeatableRead.
final boolean hasPK = entity.hasPrimaryKeyValue();
if (hasPK || !uniqueDrivenProperties.isEmpty()) { // basically true here but independent
final CB cb = newConditionBean();
if (hasPK) {
cb.acceptPrimaryKeyMap(asDBMeta().extractPrimaryKeyMap(entity));
} else {
for (String uq : uniqueDrivenProperties) {
cb.localCQ().invokeQueryEqual(uq, asDBMeta().findColumnInfo(uq).read(entity));
}
}
if (readCount(cb) == 0) {
return true; // the PK or unique key is not found so insert only
}
}
}
return false; // update statement first
}
// -----------------------------------------------------
// Create or Modify
// ----------------
/** {@inheritDoc} */
public void createOrModify(Entity entity, InsertOption extends ConditionBean> insertOption,
UpdateOption extends ConditionBean> updateOption) {
doCreateOrModify(entity, insertOption, updateOption);
}
protected void doCreateOrModify(Entity entity, InsertOption extends ConditionBean> insertOption,
UpdateOption extends ConditionBean> updateOption) {
doInsertOrUpdate(downcast(entity), downcast(insertOption), downcast(updateOption));
}
/** {@inheritDoc} */
public void createOrModifyNonstrict(Entity entity, InsertOption extends ConditionBean> insertOption,
UpdateOption extends ConditionBean> updateOption) {
doCreateOrModifyNonstrict(entity, insertOption, updateOption);
}
protected void doCreateOrModifyNonstrict(Entity entity, InsertOption extends ConditionBean> insertOption,
UpdateOption extends ConditionBean> updateOption) {
if (asDBMeta().hasOptimisticLock()) {
doInsertOrUpdateNonstrict(downcast(entity), downcast(insertOption), downcast(updateOption));
} else {
doInsertOrUpdate(downcast(entity), downcast(insertOption), downcast(updateOption));
}
}
// -----------------------------------------------------
// Delete
// ------
protected void doDelete(ENTITY entity, DeleteOption option) {
prepareEntityDelete(entity, option);
helpDeleteInternally(entity, option);
}
protected void doDeleteNonstrict(ENTITY entity, DeleteOption option) {
prepareEntityDelete(entity, option);
helpDeleteNonstrictInternally(entity, option);
}
protected void prepareEntityDelete(ENTITY entity, DeleteOption option) {
assertEntityNotNull(entity);
prepareDeleteOption(option);
prepareEntityDeleteOption(entity, option);
}
protected void prepareDeleteOption(DeleteOption option) { // all delete commands
if (option != null) {
assertDeleteOptionStatus(option);
}
}
protected void prepareEntityDeleteOption(ENTITY entity, DeleteOption option) { // only for entity delete
if (option == null) {
return;
}
if (option.hasUniqueByUniqueInfo()) {
reflectUniqueDriven(entity, option.getUniqueByUniqueInfo());
}
}
protected void helpDeleteInternally(RESULT entity, DeleteOption extends ConditionBean> option) {
assertEntityNotNull(entity);
assertEntityHasOptimisticLockValue(entity);
final int deletedCount = delegateDelete(entity, option);
if (deletedCount == 0) {
throwUpdateEntityAlreadyDeletedException(entity);
} else if (deletedCount > 1) {
throwUpdateEntityDuplicatedException(entity, deletedCount);
}
}
protected void helpDeleteNonstrictInternally(RESULT entity, DeleteOption extends ConditionBean> option) {
assertEntityNotNull(entity);
final int deletedCount = delegateDeleteNonstrict(entity, option);
if (deletedCount == 0) {
throwUpdateEntityAlreadyDeletedException(entity);
} else if (deletedCount > 1) {
throwUpdateEntityDuplicatedException(entity, deletedCount);
}
}
protected void helpDeleteNonstrictIgnoreDeletedInternally(RESULT entity,
DeleteOption extends ConditionBean> option) {
assertEntityNotNull(entity);
final int deletedCount = delegateDeleteNonstrict(entity, option);
if (deletedCount == 0) {
return;
} else if (deletedCount > 1) {
throwUpdateEntityDuplicatedException(entity, deletedCount);
}
}
protected void assertDeleteOptionStatus(DeleteOption extends ConditionBean> option) {
}
protected DeleteOption createDeleteOption(WritableOptionCall> opCall) {
assertDeleteOpCallNotNull(opCall);
final DeleteOption op = newDeleteOption();
opCall.callback(op);
return op;
}
protected DeleteOption newDeleteOption() {
return new DeleteOption();
}
protected void assertDeleteOpCallNotNull(WritableOptionCall> opCall) { // for varyingDelete()
assertObjectNotNull("opLambda (for delete)", opCall);
}
// -----------------------------------------------------
// Remove
// ------
/** {@inheritDoc} */
public void remove(Entity entity, DeleteOption extends ConditionBean> option) {
doRemove(entity, option);
}
protected void doRemove(Entity entity, DeleteOption extends ConditionBean> option) {
doDelete(downcast(entity), downcast(option));
}
/** {@inheritDoc} */
public void removeNonstrict(Entity entity, DeleteOption extends ConditionBean> option) {
doRemoveNonstrict(entity, option);
}
protected void doRemoveNonstrict(Entity entity, DeleteOption extends ConditionBean> option) {
if (asDBMeta().hasOptimisticLock()) {
doDeleteNonstrict(downcast(entity), downcast(option));
} else {
doDelete(downcast(entity), downcast(option));
}
}
// ===================================================================================
// Batch Update
// ============
// -----------------------------------------------------
// Batch Insert
// ------------
protected int[] doBatchInsert(List entityList, InsertOption option) {
assertEntityListNotNull(entityList);
final InsertOption rlop;
if (option != null) {
rlop = option;
} else {
rlop = createPlainInsertOption();
}
prepareBatchInsertOption(entityList, rlop); // required
return delegateBatchInsert(entityList, rlop);
}
protected void prepareBatchInsertOption(List entityList, InsertOption option) { // might be overridden to set option
if (isBatchInsertColumnModifiedPropertiesFragmentedDisallowed()) {
option.xdisallowInsertColumnModifiedPropertiesFragmented(); // default is allowed so use 'disallow' as option
}
if (isCompatibleBatchInsertDefaultEveryColumn()) {
option.xtoBeCompatibleBatchInsertDefaultEveryColumn(); // old style (basically no more use)
}
option.xacceptInsertColumnModifiedPropertiesIfNeeds(entityList);
prepareInsertOption(option);
}
protected boolean isBatchInsertColumnModifiedPropertiesFragmentedDisallowed() {
return false; // might be overridden by generator option
}
protected boolean isCompatibleBatchInsertDefaultEveryColumn() {
return false; // might be overridden by generator option
}
// -----------------------------------------------------
// Lump Create
// -----------
/** {@inheritDoc} */
public int[] lumpCreate(List extends Entity> entityList, InsertOption extends ConditionBean> option) {
@SuppressWarnings("unchecked")
final List castList = (List) entityList;
return doLumpCreate(castList, option);
}
protected int[] doLumpCreate(List entityList, InsertOption extends ConditionBean> option) {
return doBatchInsert(downcast(entityList), downcast(option));
}
// -----------------------------------------------------
// Batch Update
// ------------
protected int[] doBatchUpdate(List entityList, UpdateOption option) {
assertEntityListNotNull(entityList);
final UpdateOption rlop = option != null ? option : createPlainUpdateOption();
prepareBatchUpdateOption(entityList, rlop); // required
return delegateBatchUpdate(entityList, rlop);
}
protected int[] doBatchUpdateNonstrict(List entityList, UpdateOption option) {
assertEntityListNotNull(entityList);
final UpdateOption rlop = option != null ? option : createPlainUpdateOption();
prepareBatchUpdateOption(entityList, rlop);
return delegateBatchUpdateNonstrict(entityList, rlop);
}
protected UpdateOption createSpecifiedUpdateOption(SpecifyQuery updateColumnSpec) {
assertUpdateColumnSpecificationNotNull(updateColumnSpec);
final UpdateOption option = createPlainUpdateOption();
option.specify(updateColumnSpec);
return option;
}
protected void assertUpdateColumnSpecificationNotNull(SpecifyQuery extends ConditionBean> updateColumnSpec) {
assertObjectNotNull("updateColumnSpec", updateColumnSpec);
}
protected void prepareBatchUpdateOption(List entityList, UpdateOption option) {
if (isBatchUpdateColumnModifiedPropertiesFragmentedAllowed()) {
option.xallowUpdateColumnModifiedPropertiesFragmented(); // default is disallowed so use 'allow' as option
}
if (isCompatibleBatchUpdateDefaultEveryColumn()) {
option.xtoBeCompatibleBatchUpdateDefaultEveryColumn(); // old style (basically no more use)
}
option.xacceptUpdateColumnModifiedPropertiesIfNeeds(entityList);
prepareUpdateOption(option);
}
protected boolean isBatchUpdateColumnModifiedPropertiesFragmentedAllowed() {
return false; // might be overridden by generator option
}
protected boolean isCompatibleBatchUpdateDefaultEveryColumn() {
return false; // might be overridden by generator option
}
// -----------------------------------------------------
// Lump Modify
// -----------
/** {@inheritDoc} */
public int[] lumpModify(List extends Entity> entityList, UpdateOption extends ConditionBean> option) {
@SuppressWarnings("unchecked")
final List castList = (List) entityList;
return doLumpModify(castList, option);
}
protected int[] doLumpModify(List entityList, UpdateOption extends ConditionBean> option) {
return doBatchUpdate(downcast(entityList), downcast(option));
}
/** {@inheritDoc} */
public int[] lumpModifyNonstrict(List extends Entity> entityList, UpdateOption extends ConditionBean> option) {
@SuppressWarnings("unchecked")
final List castList = (List) entityList;
return doLumpModifyNonstrict(castList, option);
}
protected int[] doLumpModifyNonstrict(List entityList, UpdateOption extends ConditionBean> option) {
if (asDBMeta().hasOptimisticLock()) {
return doBatchUpdateNonstrict(downcast(entityList), downcast(option));
} else {
return doBatchUpdate(downcast(entityList), downcast(option));
}
}
// -----------------------------------------------------
// Batch Delete
// ------------
protected int[] doBatchDelete(List entityList, DeleteOption option) {
assertEntityListNotNull(entityList);
prepareDeleteOption(option);
return delegateBatchDelete(entityList, option);
}
protected int[] doBatchDeleteNonstrict(List entityList, DeleteOption option) {
assertEntityListNotNull(entityList);
prepareDeleteOption(option);
return delegateBatchDeleteNonstrict(entityList, option);
}
// -----------------------------------------------------
// Lump Remove
// -----------
/** {@inheritDoc} */
public int[] lumpRemove(List extends Entity> entityList, DeleteOption extends ConditionBean> option) {
@SuppressWarnings("unchecked")
final List castList = (List) entityList;
return doLumpRemove(castList, option);
}
protected int[] doLumpRemove(List entityList, DeleteOption extends ConditionBean> option) {
return doBatchDelete(downcast(entityList), downcast(option));
}
/** {@inheritDoc} */
public int[] lumpRemoveNonstrict(List extends Entity> entityList, DeleteOption extends ConditionBean> option) {
@SuppressWarnings("unchecked")
final List castList = (List) entityList;
return doLumpRemoveNonstrict(castList, option);
}
protected int[] doLumpRemoveNonstrict(List entityList, DeleteOption extends ConditionBean> option) {
if (asDBMeta().hasOptimisticLock()) {
return doBatchDeleteNonstrict(downcast(entityList), downcast(option));
} else {
return doBatchDelete(downcast(entityList), downcast(option));
}
}
// =====================================================================================
// Query Update
// ============
// -----------------------------------------------------
// Query Insert
// ------------
protected int doQueryInsert(QueryInsertSetupper setupper, InsertOption option) {
assertObjectNotNull("setupper", setupper);
prepareInsertOption(option);
final ENTITY et = newEntity();
final CB cb = createCBForQueryInsert();
return delegateQueryInsert(et, cb, setupper.setup(et, cb), option);
}
protected CB createCBForQueryInsert() {
final CB cb = newConditionBean();
cb.xsetupForQueryInsert();
return cb;
}
// -----------------------------------------------------
// Range Create
// ------------
/** {@inheritDoc} */
public int rangeCreate(QueryInsertSetupper extends Entity, ? extends ConditionBean> setupper,
InsertOption extends ConditionBean> option) {
return doRangeCreate(setupper, option);
}
protected int doRangeCreate(QueryInsertSetupper extends Entity, ? extends ConditionBean> setupper,
InsertOption extends ConditionBean> option) {
return doQueryInsert(downcast(setupper), downcast(option));
}
// -----------------------------------------------------
// Query Update
// ------------
protected int doQueryUpdate(ENTITY entity, CB cb, UpdateOption option) {
assertEntityNotNull(entity);
assertCBStateValid(cb);
prepareUpdateOption(option);
return checkCountBeforeQueryUpdateIfNeeds(cb) ? delegateQueryUpdate(entity, cb, option) : 0;
}
/**
* Check record count before QueryUpdate if it needs. (against MySQL's deadlock of next-key lock)
* @param cb The condition-bean for QueryUpdate. (NotNull)
* @return true if record count exists or no check.
*/
protected boolean checkCountBeforeQueryUpdateIfNeeds(ConditionBean cb) {
final boolean countExists;
if (cb.isQueryUpdateCountPreCheck()) {
countExists = readCount(cb) > 0;
} else {
countExists = true; // means no check
}
return countExists;
}
// -----------------------------------------------------
// Range Modify
// ------------
/** {@inheritDoc} */
public int rangeModify(Entity entity, ConditionBean cb, UpdateOption extends ConditionBean> option) {
return doRangeModify(entity, cb, option);
}
protected int doRangeModify(Entity entity, ConditionBean cb, UpdateOption extends ConditionBean> option) {
return doQueryUpdate(downcast(entity), downcast(cb), downcast(option));
}
// -----------------------------------------------------
// Query Delete
// ------------
protected int doQueryDelete(CB cb, DeleteOption option) {
assertCBStateValid(cb);
prepareDeleteOption(option);
return checkCountBeforeQueryUpdateIfNeeds(cb) ? delegateQueryDelete(cb, option) : 0;
}
// -----------------------------------------------------
// Range Remove
// ------------
/** {@inheritDoc} */
public int rangeRemove(ConditionBean cb, DeleteOption extends ConditionBean> option) {
return doRangeRemove(cb, option);
}
protected int doRangeRemove(ConditionBean cb, DeleteOption extends ConditionBean> option) {
return doQueryDelete(downcast(cb), downcast(option));
}
// ===================================================================================
// Delegate to Command
// ===================
// -----------------------------------------------------
// Entity Update
// -------------
protected int delegateInsert(Entity entity, InsertOption extends ConditionBean> option) {
final OptionalThing> optOption = createOptionalInsertOption(option);
adjustEntityBeforeInsert(entity, optOption);
final InsertEntityCommand command = createInsertEntityCommand(entity, option);
RuntimeException cause = null;
try {
hookBeforeInsert(command, entity, emptyOpt(), optOption);
return invoke(command);
} catch (RuntimeException e) {
cause = e;
throw e;
} finally {
hookFinallyInsert(command, entity, emptyOpt(), optOption, createOptionalCause(cause));
}
}
protected OptionalThing createOptionalCause(RuntimeException cause) {
return OptionalThing.ofNullable(cause, () -> {
throw new IllegalStateException("Not found the cause exception.");
});
}
protected OptionalThing emptyOpt() {
return OptionalThing.empty();
}
protected int delegateUpdate(Entity entity, UpdateOption extends ConditionBean> option) {
final OptionalThing> optOption = createOptionalUpdateOption(option);
adjustEntityBeforeUpdate(entity, optOption);
if (asDBMeta().hasOptimisticLock()) {
final UpdateEntityCommand command = createUpdateEntityCommand(entity, option);
RuntimeException cause = null;
try {
hookBeforeUpdate(command, entity, emptyOpt(), optOption);
return invoke(command);
} catch (RuntimeException e) {
cause = e;
throw e;
} finally {
hookFinallyUpdate(command, entity, emptyOpt(), optOption, createOptionalCause(cause));
}
} else {
return delegateUpdateNonstrict(entity, option);
}
}
protected int delegateUpdateNonstrict(Entity entity, UpdateOption extends ConditionBean> option) {
final OptionalThing> optOption = createOptionalUpdateOption(option);
adjustEntityBeforeUpdate(entity, optOption);
final UpdateNonstrictEntityCommand command = createUpdateNonstrictEntityCommand(entity, option);
RuntimeException cause = null;
try {
hookBeforeUpdate(command, entity, emptyOpt(), optOption);
return invoke(command);
} catch (RuntimeException e) {
cause = e;
throw e;
} finally {
hookFinallyUpdate(command, entity, emptyOpt(), optOption, createOptionalCause(cause));
}
}
protected OptionalThing> createOptionalUpdateOption(
UpdateOption extends ConditionBean> option) {
return OptionalThing.ofNullable(option, () -> {
throw new IllegalStateException("Not found the update option.");
});
}
protected int delegateDelete(Entity entity, DeleteOption extends ConditionBean> option) {
final OptionalThing> optOption = createOptionalDeleteOption(option);
adjustEntityBeforeDelete(entity, optOption);
if (asDBMeta().hasOptimisticLock()) {
final DeleteEntityCommand command = createDeleteEntityCommand(entity, option);
final OptionalThing
© 2015 - 2025 Weber Informatics LLC | Privacy Policy