Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.landawn.abacus.core.DBAccessImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2015, Haiyang Li.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.landawn.abacus.core;
import static com.landawn.abacus.core.EntityManagerUtil.checkConflictOptions;
import static com.landawn.abacus.core.EntityManagerUtil.checkEntityId;
import static com.landawn.abacus.core.EntityManagerUtil.checkPropsList;
import static com.landawn.abacus.core.EntityManagerUtil.checkResultHandle;
import static com.landawn.abacus.core.EntityManagerUtil.checkSelectPropNamesForGet;
import static com.landawn.abacus.core.EntityManagerUtil.entityId2Condition;
import static com.landawn.abacus.core.EntityManagerUtil.getBatchSize;
import static com.landawn.abacus.core.EntityManagerUtil.getCacheCondition;
import static com.landawn.abacus.core.EntityManagerUtil.getCacheRange;
import static com.landawn.abacus.core.EntityManagerUtil.getCount;
import static com.landawn.abacus.core.EntityManagerUtil.getHandleLiveTime;
import static com.landawn.abacus.core.EntityManagerUtil.getHandleMaxIdleTime;
import static com.landawn.abacus.core.EntityManagerUtil.getOffset;
import static com.landawn.abacus.core.EntityManagerUtil.getQueryCacheLiveTime;
import static com.landawn.abacus.core.EntityManagerUtil.getQueryCacheMaxIdleTime;
import static com.landawn.abacus.core.EntityManagerUtil.getUncachePropNames;
import static com.landawn.abacus.core.EntityManagerUtil.hasOffsetCount;
import static com.landawn.abacus.core.EntityManagerUtil.isCacheResult;
import static com.landawn.abacus.core.EntityManagerUtil.isGetByResultHandle;
import static com.landawn.abacus.core.EntityManagerUtil.isGetFromCache;
import static com.landawn.abacus.core.EntityManagerUtil.isInTransaction;
import static com.landawn.abacus.core.EntityManagerUtil.isNullOrEmptyIdValue;
import static com.landawn.abacus.core.EntityManagerUtil.isRefreshCache;
import static com.landawn.abacus.core.EntityManagerUtil.notInTransaction;
import static com.landawn.abacus.core.EntityManagerUtil.parseInsertPropsList;
import static com.landawn.abacus.core.EntityManagerUtil.parseUpdateProps;
import static com.landawn.abacus.core.EntityManagerUtil.requiresAutoGeneratedKeys;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.landawn.abacus.DataSet;
import com.landawn.abacus.EntityId;
import com.landawn.abacus.IsolationLevel;
import com.landawn.abacus.Transaction.Action;
import com.landawn.abacus.cache.Cache;
import com.landawn.abacus.cache.CacheFactory;
import com.landawn.abacus.cache.DataGrid;
import com.landawn.abacus.cache.QueryCache;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.core.AbacusConfiguration.EntityManagerConfiguration;
import com.landawn.abacus.core.AbacusConfiguration.EntityManagerConfiguration.QueryCacheConfiguration;
import com.landawn.abacus.core.command.Command;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.InvalidResultHandleException;
import com.landawn.abacus.idGenerator.IdGenerator;
import com.landawn.abacus.lock.RefReentrantReadWriteLock;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.metadata.EntityDefinition;
import com.landawn.abacus.metadata.EntityDefinitionFactory;
import com.landawn.abacus.metadata.Property;
import com.landawn.abacus.pool.KeyedObjectPool;
import com.landawn.abacus.pool.PoolFactory;
import com.landawn.abacus.util.ExceptionUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Options;
import com.landawn.abacus.util.Options.Query;
import com.landawn.abacus.util.Properties;
import com.landawn.abacus.util.SQLParser;
import com.landawn.abacus.util.WD;
import com.landawn.abacus.util.u.Holder;
import com.landawn.abacus.util.u.Optional;
// TODO: Auto-generated Javadoc
/**
*
* @author Haiyang Li
* @since 0.8
*/
class DBAccessImpl implements com.landawn.abacus.DBAccess {
private static final Logger logger = LoggerFactory.getLogger(DBAccessImpl.class);
private final KeyedObjectPool handleResultPool = PoolFactory.createKeyedObjectPool(3000, 1000);
private final RefReentrantReadWriteLock queryCacheRefLock = new RefReentrantReadWriteLock();
private final EntityManagerConfiguration entityManagerConfig;
private final EntityDefinitionFactory entityDefFactory;
private final Executant executant;
private final Cache> dataCridCache;
private final QueryCachePool queryCachePool;
private final boolean isAutoRefreshQueryCache;
@SuppressWarnings({ "unchecked", "rawtypes" })
protected DBAccessImpl(EntityManagerConfiguration entityManagerConfig, EntityDefinitionFactory entityDefFactory, Executant executant) {
this.entityManagerConfig = entityManagerConfig;
this.entityDefFactory = entityDefFactory;
this.executant = executant;
for (EntityDefinition entityDef : entityDefFactory.getDefinitionList()) {
for (IdGenerator idGenerator : entityDef.getIdGeneratorList()) {
idGenerator.initialize(executant);
}
}
final QueryCacheConfiguration qcc = entityManagerConfig.getQueryCacheConfiguration();
dataCridCache = ((qcc == null) || (qcc.getProvider() == null)) ? null : (Cache) CacheFactory.createCache(qcc.getProvider());
queryCachePool = createQueryCachePool(qcc);
isAutoRefreshQueryCache = (qcc == null) ? QueryCacheConfiguration.DEFAULT_AUTO_REFRESH : qcc.isAutoRefresh();
}
/**
* Creates the query cache pool.
*
* @param qcc
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private QueryCachePool createQueryCachePool(QueryCacheConfiguration qcc) {
if (qcc == null) {
return new QueryCachePool(QueryCacheConfiguration.DEFAULT_CAPACITY, QueryCacheConfiguration.DEFAULT_EVICT_DELAY, null);
} else {
return new QueryCachePool(qcc.getCapacity(), qcc.getEvictDelay(), qcc);
}
}
/**
*
* @param
* @param entityId
* @return
*/
@Override
public Optional get(EntityId entityId) {
return Optional.ofNullable((T) gett(entityId));
}
/**
*
* @param
* @param entityId
* @param selectPropNames
* @return
*/
@Override
public Optional get(EntityId entityId, Collection selectPropNames) {
return Optional.ofNullable((T) gett(entityId, selectPropNames));
}
/**
*
* @param
* @param entityId
* @param selectPropNames
* @param options
* @return
*/
@Override
public Optional get(EntityId entityId, Collection selectPropNames, Map options) {
return Optional.ofNullable((T) gett(entityId, selectPropNames, options));
}
/**
* Gets the t.
*
* @param
* @param entityId
* @return
*/
@Override
public T gett(EntityId entityId) {
return gett(entityId, null, null);
}
/**
* Gets the t.
*
* @param
* @param entityId
* @param selectPropNames
* @return
*/
@Override
public T gett(EntityId entityId, Collection selectPropNames) {
return gett(entityId, selectPropNames, null);
}
/**
* Gets the t.
*
* @param
* @param entityId
* @param selectPropNames
* @param options
* @return
*/
@SuppressWarnings("unchecked")
@Override
public T gett(EntityId entityId, Collection selectPropNames, Map options) {
checkEntityId(getEntityDefinitionFactory(), entityId);
checkOffsetCountForGet(options);
final String entityName = entityId.entityName();
List entities = (List) list(entityName, selectPropNames, entityId2Condition(entityId), options);
if (entities.size() > 1) {
throw new DuplicatedResultException(
"More than one records are found by one entity Id. [entityId]: " + entityId + ". [entities]: " + N.toString(entities));
}
return (entities.size() > 0) ? entities.get(0) : null;
}
/**
* Check offset count for get.
*
* @param options
*/
private void checkOffsetCountForGet(Map options) {
if (hasOffsetCount(options)) {
throw new IllegalArgumentException(
"Can't set offset[" + options.get(Query.OFFSET) + "] or count[" + options.get(Query.COUNT) + "] options when get entity by EntityId(s). ");
}
}
/**
*
* @param
* @param entityName
* @param selectPropNames
* @param condition
* @return
*/
@Override
public List list(String entityName, Collection selectPropNames, Condition condition) {
return list(entityName, selectPropNames, condition, null);
}
/**
*
* @param
* @param entityName
* @param selectPropNames
* @param condition
* @param options
* @return
*/
@SuppressWarnings("unchecked")
@Override
public List list(String entityName, Collection selectPropNames, Condition condition, Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
checkSelectPropNamesForGet(entityDef, selectPropNames);
DataSet dataSet = query(entityName, selectPropNames, condition, null, true, options);
return (List) dataSet2Entities(entityDef, dataSet);
}
/**
*
* @param entityName
* @param props
* @param options
* @return
*/
@Override
public EntityId add(String entityName, Map props, Map options) {
return addAll(entityName, ParametersUtil.asList(props), options).get(0);
}
/**
* Adds the all.
*
* @param entityName
* @param propsList
* @param options
* @return
*/
@Override
public List addAll(String entityName, List> propsList, Map options) {
return addAll(entityName, propsList, false, options);
}
/**
*
* @param entityName
* @param propsList
* @param isPropChecked
* @param options
* @return
*/
List addAll(String entityName, List> propsList, boolean isPropChecked, Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
checkPropsList(propsList);
int batchSize = getBatchSize(options);
int size = propsList.size();
if (size <= batchSize) {
return addEntities(entityDef, propsList, isPropChecked, options);
} else {
List entityIds = new ArrayList<>(size);
String transactionId = null;
if (notInTransaction(options)) {
transactionId = startDefaultTransactionForUpdate(options);
options = ParametersUtil.copy(options);
options.put(Options.TRANSACTION_ID, transactionId);
}
boolean noException = false;
incrementRefCount();
try {
for (int from = 0, to = Math.min(from + batchSize, size); from < size; from = to, to = Math.min(from + batchSize, size)) {
entityIds.addAll(addEntities(entityDef, propsList.subList(from, to), isPropChecked, options));
}
noException = true;
} finally {
decrementRefCount();
if (transactionId != null) {
if (noException) {
endTransaction(transactionId, Action.COMMIT, null);
} else {
try {
endTransaction(transactionId, Action.ROLLBACK, null);
} catch (Exception e) {
// ignore
logger.error("Failed to roll back with transaction id: " + transactionId + ". " + ExceptionUtil.getMessage(e), e);
}
}
}
}
return entityIds;
}
}
/**
* Check entity name.
*
* @param entityName
* @return
*/
private EntityDefinition checkEntityName(String entityName) {
return EntityManagerUtil.checkEntityName(entityDefFactory, entityName);
}
/**
* Adds the entities.
*
* @param entityDef
* @param propsList
* @param isPropChecked
* @param options
* @return
*/
private List addEntities(EntityDefinition entityDef, List> propsList, boolean isPropChecked, Map options) {
propsList = isPropChecked ? propsList : checkInsertPropsList(entityDef, propsList);
boolean isAutoGeneratedKeys = requiresAutoGeneratedKeys(entityDef, propsList);
List entityIds = null;
SQLResult result = null;
incrementRefCount();
try {
Command command = executant.getInterpreter().interpretAdd(entityDef, propsList, options);
result = executeUpdate(command, isAutoGeneratedKeys, options);
List autoGeneratedkeys = null;
if (isAutoGeneratedKeys) {
autoGeneratedkeys = result.getGeneratedKeys();
// SQL Server/DB2 don't return the auto-generated ids for batch
// insertion.
// if (autoGeneratedkeys.size() != propsList.size()) {
// throw new UncheckedSQLException(
// "Succeed to add entity. but the size of auto-generated ids isn't equal to the size of properties list. The auto-generated ids are: "
// + N.toString(autoGeneratedkeys));
// }
}
entityIds = createEntityId(entityDef, propsList, autoGeneratedkeys);
} finally {
decrementRefCount();
// [TODO] no lock/synchronize? query may be executed before the
// update and put to cache pool later.
if (N.notNullOrEmpty(entityIds) && isAutoRefreshQueryCache && (queryCachePool.size() > 0)) {
Command updateCmd = executant.getInterpreter().interpretUpdate(entityDef, propsList.get(0), entityId2Condition(entityIds), options);
queryCachePool.updateCache(updateCmd, options);
}
}
return entityIds;
}
/**
* Creates the entity id.
*
* @param entityDef
* @param propsList
* @param autoGeneratedkeys
* @return
*/
@SuppressWarnings("deprecation")
private List createEntityId(EntityDefinition entityDef, List> propsList, List> autoGeneratedkeys) {
int size = propsList.size();
final List entityIds = new ArrayList<>(size);
final List idPropList = entityDef.getIdPropertyList();
Seid entityId = null;
Object idPropValue = null;
for (int index = 0, i = 0; i < size; i++) {
entityId = Seid.of(entityDef.getName());
for (Property idProp : idPropList) {
idPropValue = propsList.get(i).get(idProp.getName());
if (idPropValue == null) {
idPropValue = propsList.get(i).get(idProp.getName());
}
if (isNullOrEmptyIdValue(idPropValue) && N.notNullOrEmpty(autoGeneratedkeys)) {
idPropValue = autoGeneratedkeys.get(index++);
propsList.get(i).put(idProp.getName(), idPropValue);
}
entityId.set(idProp.getName(), N.convert(idPropValue, idProp.getType().clazz()));
}
entityIds.add(entityId);
}
return entityIds;
}
// @Override
// public List add(String entityName, List propNamesToInsert, Condition condition, Map options) {
// EntityDefinition entityDef = checkEntityName(entityName);
//
// if ((condition == null) || !(condition instanceof SubQuery)) {
// throw new IllegalArgumentException("The specified condition must be a SubQuery condition. but it's: " + N.toString(condition));
// }
//
// DataSet resultList = null;
// Command command = (Command) executor.getInterpreter(null).interpretCondition(entityDef, condition);
// SQLResult queryResult = executeQuery(entityName, command, options);
//
// try {
// resultList = getResultListBySQLResult(queryResult, null, options);
// } finally {
// queryResult.close();
// }
//
// if (propNamesToInsert != null) {
// if (resultList.getColumnNameList().size() != propNamesToInsert.size()) {
// throw new IllegalArgumentException("The DataSet generated by Sub Query doesn't include all the necessary propNamesToInsert: "
// + propNamesToInsert.toString());
// }
//
// for (int i = 0; i < propNamesToInsert.size(); i++) {
// resultList.changeColumnName(i, propNamesToInsert.get(i));
// }
// }
//
// List> propsList = resultList2PropsList(resultList);
//
// return add(entityName, propsList, options);
// }
/**
* Update.
* @param props
* @param entityId
* @return
*/
@Override
public int update(Map props, EntityId entityId) {
return update(props, entityId, null);
}
/**
* Update.
* @param props
* @param entityId
* @param options
* @return
*/
@Override
public int update(Map props, EntityId entityId, Map options) {
checkEntityId(getEntityDefinitionFactory(), entityId);
final String entityName = entityId.entityName();
return update(entityName, props, entityId2Condition(entityId), options);
}
/**
* Update all.
* @param props
* @param entityIds
* @return
*/
@Override
public int updateAll(Map props, List extends EntityId> entityIds) {
return updateAll(props, entityIds, null);
}
/**
* Update all.
* @param props
* @param entityIds
* @param options
* @return
*/
@Override
public int updateAll(Map props, List extends EntityId> entityIds, Map options) {
checkEntityId(getEntityDefinitionFactory(), entityIds);
final String entityName = entityIds.get(0).entityName();
return update(entityName, props, entityId2Condition(entityIds), options);
}
/**
*
* @param entityName
* @param props
* @param condition
* @param options
* @return
*/
@Override
public int update(String entityName, Map props, Condition condition, Map options) {
return update(entityName, props, condition, false, options);
}
/**
*
* @param entityName
* @param props
* @param condition
* @param isPropChecked
* @param options
* @return
*/
int update(String entityName, Map props, Condition condition, boolean isPropChecked, Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
if (N.isNullOrEmpty(props)) {
throw new IllegalArgumentException("The parameter props can't be null or empty");
}
props = isPropChecked ? props : checkUpateProps(entityDef, props);
final Command command = executant.getInterpreter().interpretUpdate(entityDef, props, condition, options);
int result = 0;
incrementRefCount();
try {
if (isAutoRefreshQueryCache && (queryCachePool.size() > 0)) {
queryCachePool.updateCache(command, options);
}
result = executeUpdate(command, false, options).getUpateCount();
} finally {
decrementRefCount();
if ((result > 0) && isAutoRefreshQueryCache && (queryCachePool.size() > 0)) {
queryCachePool.updateCache(command, options);
}
}
return result;
}
/**
*
* @param entityId
* @return
*/
@Override
public int delete(EntityId entityId) {
return delete(entityId, null);
}
/**
*
* @param entityId
* @param options
* @return
*/
@Override
public int delete(EntityId entityId, Map options) {
checkEntityId(getEntityDefinitionFactory(), entityId);
final String entityName = entityId.entityName();
return delete(entityName, entityId2Condition(entityId), options);
}
/**
*
* @param entityIds
* @return
*/
@Override
public int deleteAll(List extends EntityId> entityIds) {
return deleteAll(entityIds, null);
}
/**
*
* @param entityIds
* @param options
* @return
*/
@Override
public int deleteAll(List extends EntityId> entityIds, Map options) {
checkEntityId(getEntityDefinitionFactory(), entityIds);
final String entityName = entityIds.get(0).entityName();
return delete(entityName, entityId2Condition(entityIds), options);
}
/**
*
* @param entityName
* @param condition
* @param options
* @return
*/
@Override
public int delete(String entityName, Condition condition, Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
final Command command = executant.getInterpreter().interpretDelete(entityDef, condition, options);
int result = 0;
incrementRefCount();
try {
if (isAutoRefreshQueryCache && (queryCachePool.size() > 0)) {
queryCachePool.updateCache(command, options);
}
result = executeUpdate(command, false, options).getUpateCount();
} finally {
decrementRefCount();
}
return result;
}
/**
*
* @param entityName
* @param selectPropNames
* @param condition
* @return
*/
@Override
public DataSet query(String entityName, Collection selectPropNames, Condition condition) {
return query(entityName, selectPropNames, condition, null, null);
}
/**
*
* @param entityName
* @param selectPropNames
* @param condition
* @param resultHandle
* @param options
* @return
*/
@Override
public DataSet query(String entityName, Collection selectPropNames, Condition condition, Holder resultHandle, Map options) {
return query(entityName, selectPropNames, condition, resultHandle, false, options);
}
/**
*
* @param entityName
* @param selectPropNames
* @param condition
* @param resultHandle
* @param isGet
* @param options
* @return
*/
DataSet query(String entityName, Collection selectPropNames, Condition condition, Holder resultHandle, boolean isGet,
Map options) {
EntityDefinition entityDef = checkEntityName(entityName);
selectPropNames = checkSelectPropNames(entityDef, selectPropNames, resultHandle, isGet);
checkConflictOptions(options);
boolean isRefreshCache = isRefreshCache(options);
boolean isCacheResult = isCacheResult(options);
boolean isGetFromCache = isGetFromCache(options);
boolean isInTransaction = isInTransaction(options);
// May cache dirty data if the update in transaction which is rolled backed.
if ((isCacheResult || isGetFromCache) && isInTransaction) {
throw new IllegalArgumentException("Can't cache or get result from cache in transaction. ");
}
final String cacheKey = ((isCacheResult || isGetFromCache || isRefreshCache) && (resultHandle == null)) ? createQueryCacheKey(entityDef, condition)
: null;
boolean needCloseResult = true;
SQLResult queryResult = null;
QueryCache queryCache = null;
DataSet cachedResult = null;
if (resultHandle == null) {
if (isRefreshCache) {
queryCachePool.remove(cacheKey);
} else if (isGetFromCache) {
queryCache = queryCachePool.get(cacheKey);
cachedResult = (queryCache == null) ? null : getResultFromCache(queryCache, selectPropNames, options);
}
if (isCacheResult && (queryCache == null)) {
queryCache = createQueryCache(cacheKey, options);
}
} else {
if (isGetByResultHandle(resultHandle)) {
HandleResult handleResult = getHandleResult(resultHandle.value());
queryResult = handleResult.getSQLResult();
if (isRefreshCache) {
handleResult.refreshQueryCache();
} else if (isGetFromCache) {
queryCache = handleResult.getQueryCache();
cachedResult = (queryCache == null) ? null : getResultFromCache(queryCache, selectPropNames, options);
}
if (isCacheResult && (queryCache == null)) {
queryCache = createQueryCache(options);
}
} else {
queryResult = getSQLResultWithHandleBySearch(entityDef, selectPropNames, condition, options);
}
needCloseResult = false;
}
DataSet dataSet = null;
boolean isAllResultCached = false;
try {
// create returned result.
if (cachedResult == null) {
if (queryResult == null) {
queryResult = getSQLResultBySearch(entityDef, selectPropNames, condition, options);
}
dataSet = getResultListBySQLResult(queryResult, selectPropNames, options);
} else {
if (cachedResult.columnNameList().containsAll(selectPropNames)) {
dataSet = cachedResult;
isAllResultCached = true;
} else {
List uncachePropNames = new ArrayList<>(selectPropNames);
uncachePropNames.removeAll(cachedResult.columnNameList());
if (queryResult == null) {
queryResult = getSQLResultBySearch(entityDef, uncachePropNames, condition, options);
}
DataSet uncachedResult = getResultListBySQLResult(queryResult, uncachePropNames, options);
dataSet = combineResultList(uncachedResult, cachedResult);
}
}
if (isCacheResult && (queryCache != null) && (!isAllResultCached || options.containsKey(Options.Cache.CACHE_RESULT_RANGE))) {
if (queryResult == null) {
queryResult = getSQLResultBySearch(entityDef, selectPropNames, condition, options);
}
needCloseResult = cacheResult(queryCache, cacheKey, selectPropNames, queryResult, needCloseResult, options);
}
} finally {
if (needCloseResult && (queryResult != null)) {
queryResult.close();
}
}
// Keep query result.
saveHandleResult(resultHandle, entityDef, selectPropNames, queryResult, queryCache, options);
return dataSet;
}
/**
*
* @param queryCache
* @param cacheKey
* @param selectPropNames
* @param queryResult
* @param closeResult
* @param options
* @return true, if successful
*/
@SuppressWarnings("unchecked")
private boolean cacheResult(QueryCache queryCache, String cacheKey, final Collection selectPropNames, final SQLResult queryResult,
boolean closeResult, final Map options) {
// cache key is null when resultHandle is set.
if ((cacheKey == null) || (queryCache == queryCachePool.get(cacheKey))) {
Collection cachePropNames = selectPropNames;
Object uncachedPropNames = getUncachePropNames(options);
if (uncachedPropNames != null) {
cachePropNames = new ArrayList<>(selectPropNames);
if (uncachedPropNames instanceof Collection) {
cachePropNames.removeAll((Collection) uncachedPropNames);
} else {
cachePropNames.remove(uncachedPropNames);
}
}
Options.Cache.Condition cacheCond = getCacheCondition(entityManagerConfig.getQueryCacheConfiguration(), options);
Options.Cache.Range range = getCacheRange(options);
if (Query.CACHE_RESULT_SYNC.equals(options.get(Query.CACHE_RESULT))) {
queryCache.cacheResult(queryResult, cachePropNames, cacheCond, range);
} else if (Query.CACHE_RESULT_ASYNC.equals(options.get(Query.CACHE_RESULT))) {
queryCache.asyncCacheResult(queryResult, cachePropNames, cacheCond, range, closeResult);
closeResult = false;
} else {
throw new IllegalArgumentException(
"Invalid 'CACHE_RESULT' option '" + options.get(Query.CACHE_RESULT) + "'. It must be 'CACHE_RESULT_SYN' or 'CACHE_RESULT_ASY'. ");
}
}
return closeResult;
}
// @Override
// public ResultSet queryBy(String entityName, String sql, Object parameters, Handle resultHandle, Map options) {
// EntityDefinition entityDef = checkEntityName(entityName);
//
// if (isCacheResult(options) || isGetFromCache(options) || isRefreshCache(options)) {
// throw new IllegalArgumentException("'queryBy' doesn't support 'CACHE_RESULT', 'FROM_CACHE' and 'REFRESH_CACHE' option. ");
// }
//
// checkConflictOptions(options);
//
// NamedSQL namedSQL = getNamedQuery(sql);
// Map attrs = namedSQL.getAttribes();
//
// if (!N.isNullOrEmpty(attrs)) {
// if ((attrs.get(SQLMapper.BATCH_SIZE) != null) && ((options == null) || (options.get(Options.BATCH_SIZE) == null))) {
// options = InternalParametersUtil.copy(options);
// options.put(Options.BATCH_SIZE, Numbers.toInt(attrs.get(SQLMapper.BATCH_SIZE)));
// }
//
// if ((attrs.get(SQLMapper.FETCH_SIZE) != null) && ((options == null) || (options.get(Options.Jdbc.FETCH_SIZE) == null))) {
// options = InternalParametersUtil.copy(options);
// options.put(Options.Jdbc.FETCH_SIZE, Numbers.toInt(attrs.get(SQLMapper.FETCH_SIZE)));
// }
//
// if ((attrs.get(SQLMapper.RESULT_SET_TYPE) != null) && ((options == null) || (options.get(Options.Jdbc.RESULT_SET_TYPE) == null))) {
// options = InternalParametersUtil.copy(options);
// options.put(Options.Jdbc.RESULT_SET_TYPE, SQLMapper.RESULT_SET_TYPE_MAP.get(attrs.get(SQLMapper.RESULT_SET_TYPE)));
// }
// }
//
// sql = namedSQL.getPureSQL();
//
// List parameterList = getNamedParameters(namedSQL, parameters);
// boolean needCloseResult = true;
// ResultSet resultList = null;
// SQLResult queryResult = null;
//
// try {
// if (resultHandle == null) {
// Command command = executor.getInterpreter(null).interpretQueryBy(entityDef, sql, parameterList, options);
// queryResult = executeQuery(entityName, command, options);
// } else {
// // get result from handle or create result handle.
// if (isGetByResultHandle(resultHandle, options)) {
// queryResult = getHandleResult(resultHandle).getSQLResult();
// } else {
// Command command = executor.getInterpreter(null).interpretQueryBy(entityDef, sql, parameterList, options);
// queryResult = getExecutant().executeQueryWithHandle(command, options);
// }
//
// needCloseResult = false;
// }
//
// resultList = getResultListBySQLResult(queryResult, null, options);
// } finally {
// if (needCloseResult && (queryResult != null)) {
// queryResult.close();
// }
// }
//
// // Keep query result.
// saveHandleResult(resultHandle, entityDef, null, queryResult, null, options);
//
// return resultList;
// }
/**
* Gets the result by handle.
*
* @param resultHandle
* @param selectPropNames
* @param options
* @return
*/
@Override
public DataSet getResultByHandle(String resultHandle, Collection selectPropNames, Map options) {
final HandleResult handleResult = getHandleResult(resultHandle);
final EntityDefinition entityDef = handleResult.getEntityDef();
return query(entityDef.getName(), selectPropNames, null, Holder.of(resultHandle), options);
}
/**
* Release result handle.
*
* @param resultHandle
*/
@Override
public void releaseResultHandle(String resultHandle) {
checkResultHandle(resultHandle);
handleResultPool.remove(resultHandle);
}
/**
*
* @param isolationLevel
* @param options
* @return
*/
@Override
public String beginTransaction(IsolationLevel isolationLevel, Map options) {
if (isolationLevel == null) {
throw new IllegalArgumentException("The parameter isolationLevel can't be null");
}
final String result = executant.beginTransaction(isolationLevel, options);
incrementRefCount();
return result;
}
/**
* Start default transaction for update.
*
* @param options
* @return
*/
String startDefaultTransactionForUpdate(Map options) {
return beginTransaction(IsolationLevel.DEFAULT, options);
}
/**
*
* @param transactionId
* @param transactionAction
* @param options
*/
@Override
public void endTransaction(String transactionId, Action transactionAction, Map options) {
N.checkArgNotNullOrEmpty(transactionId, "TransactionId");
if (transactionAction == null) {
throw new IllegalArgumentException("The parameter transactionAction can't be null");
}
try {
executant.endTransaction(transactionId, transactionAction, options);
} finally {
decrementRefCount();
}
}
/**
* Gets the entity definition factory.
*
* @return
*/
@Override
public EntityDefinitionFactory getEntityDefinitionFactory() {
return entityDefFactory;
}
/**
* Gets the executant.
*
* @return
*/
Executant getExecutant() {
return executant;
}
/**
*
* @param command
* @param isAutoGeneratedKeys
* @param options
* @return
*/
private SQLResult executeUpdate(Command command, boolean isAutoGeneratedKeys, Map options) {
return executant.executeUpdate(command, isAutoGeneratedKeys, options);
}
/**
*
* @param command
* @param options
* @return
*/
SQLResult executeQuery(Command command, Map options) {
return executant.executeQuery(command, options);
}
/**
* Gets the result list by SQL result.
*
* @param queryResult
* @param selectPropNames
* @param options
* @return
*/
DataSet getResultListBySQLResult(SQLResult queryResult, Collection selectPropNames, Map options) {
synchronized (queryResult) {
return queryResult.getResultSet(selectPropNames, options);
}
}
/**
* Gets the handle result.
*
* @param resultHandle
* @return
*/
HandleResult getHandleResult(String resultHandle) {
checkResultHandle(resultHandle);
final HandleResult queryResult = handleResultPool.get(resultHandle);
if (queryResult == null) {
throw new
InvalidResultHandleException("Invalid result handle: " + resultHandle);
}
return queryResult;
}
/**
* Increment ref count.
*/
private void incrementRefCount() {
if (isAutoRefreshQueryCache) {
queryCacheRefLock.writeLock().lock();
try {
queryCacheRefLock.incrementRefCount();
} finally {
queryCacheRefLock.writeLock().unlock();
}
}
}
/**
* Decrement ref count.
*/
private void decrementRefCount() {
if (isAutoRefreshQueryCache) {
queryCacheRefLock.decrementRefCount();
}
}
/**
* Gets the SQL result by search.
*
* @param entityDef
* @param selectPropNames
* @param condition
* @param options
* @return
*/
private SQLResult getSQLResultBySearch(EntityDefinition entityDef, Collection selectPropNames, Condition condition, Map options) {
final Command command = executant.getInterpreter().interpretQuery(entityDef, selectPropNames, condition, options);
return executeQuery(command, options);
}
/**
* Gets the SQL result with handle by search.
*
* @param entityDef
* @param selectPropNames
* @param condition
* @param options
* @return
*/
private SQLResult getSQLResultWithHandleBySearch(EntityDefinition entityDef, Collection selectPropNames, Condition condition,
Map options) {
final Command command = executant.getInterpreter().interpretQuery(entityDef, selectPropNames, condition, options);
return executant.executeQueryWithHandle(command, options);
}
/**
* Gets the cached prop names.
*
* @param queryCache
* @param selectPropNames
* @param options
* @return
*/
private List getCachedPropNames(QueryCache queryCache, Collection selectPropNames, Map options) {
if (queryCache.size() < 0) {
return null;
}
int offset = getOffset(options);
int count = getCount(options);
int fromIndex = (offset > queryCache.size()) ? queryCache.size() : offset;
int endIndex = (count > (queryCache.size() - offset)) ? queryCache.size() : (count + offset);
List cachedPropNames = new ArrayList<>();
Options.Cache.Range range = Options.Cache.range(fromIndex, endIndex);
for (String propName : selectPropNames) {
if (queryCache.isCachedResult(propName, range)) {
cachedPropNames.add(propName);
}
}
return cachedPropNames;
}
/**
* Gets the result from cache.
*
* @param queryCache
* @param selectPropNames
* @param options
* @return
*/
private DataSet getResultFromCache(QueryCache queryCache, Collection selectPropNames, Map options) {
if (queryCache != null) {
queryCache.lock();
try {
List cachedPropNames = getCachedPropNames(queryCache, selectPropNames, options);
if (N.notNullOrEmpty(cachedPropNames)) {
int offset = getOffset(options);
int count = getCount(options);
int fromIndex = (offset > queryCache.size()) ? queryCache.size() : offset;
int endIndex = (count > (queryCache.size() - offset)) ? queryCache.size() : (count + offset);
Object[][] columnArray = queryCache.getResult(cachedPropNames, fromIndex, endIndex);
// The cache may be cleared because of SoftReference after
// getCachedPropNames.
// Need to check it again
if (columnArray.length > 0) {
List> columnList = new ArrayList<>(columnArray.length);
for (int i = 0; i < columnArray.length; i++) {
columnList.add(N.asList(columnArray[i]));
}
final Properties properties = new Properties<>();
properties.put(RowDataSet.CACHED_PROP_NAMES, cachedPropNames);
return new RowDataSet(new ArrayList<>(cachedPropNames), columnList, properties);
}
}
} finally {
queryCache.unlock();
}
}
return null;
}
/**
* Combine result list.
*
* @param sourceResult
* @param targetResult
* @return
*/
private DataSet combineResultList(DataSet sourceResult, DataSet targetResult) {
// the cache result has 'cache properties' cmd,
// the search result doesn't has this cmd.
if (targetResult == null) {
return sourceResult;
} else {
int propNum = sourceResult.columnNameList().size();
for (int i = 0; i < propNum; i++) {
targetResult.addColumn(sourceResult.getColumnName(i), sourceResult.getColumn(i));
}
sourceResult.clear();
}
return targetResult;
}
/**
* Data set 2 entities.
*
* @param
* @param entityDef
* @param result
* @return
*/
private List dataSet2Entities(EntityDefinition entityDef, DataSet result) {
return (List) DataSetUtil.row2Entity(entityDef.getTypeClass(), (RowDataSet) result, entityDef, 0, result.size());
}
/**
* Check select prop names.
*
* @param entityDef
* @param selectPropNames
* @param resultHandle
* @param isGet
* @return
*/
private Collection checkSelectPropNames(EntityDefinition entityDef, Collection selectPropNames, Holder resultHandle,
boolean isGet) {
if (N.isNullOrEmpty(selectPropNames)) {
if ((resultHandle != null) && N.notNullOrEmpty(resultHandle.value())) {
return getHandleResult(resultHandle.value()).getSelectPropNames();
} else {
return entityDef.getDefaultLoadPropertyNameList();
}
}
final List selectPropNameList = new ArrayList<>(selectPropNames.size());
for (String propName : selectPropNames) {
Property prop = entityDef.getProperty(propName);
if (prop != null && prop.getEntityDefinition() == entityDef) {
selectPropNameList.add(prop.getName());
} else {
selectPropNameList.add(propName);
}
}
if (isGet && N.notNullOrEmpty(entityDef.getIdPropertyList())) {
for (Property idProp : entityDef.getIdPropertyList()) {
if (!selectPropNameList.contains(idProp.getName())) {
selectPropNameList.add(idProp.getName());
}
}
}
return selectPropNameList;
}
/**
* Check insert props list.
*
* @param entityDef
* @param propsList
* @return
*/
private List> checkInsertPropsList(EntityDefinition entityDef, List> propsList) {
final InsertPropsListView insertPropsListView = parseInsertPropsList(entityDef, propsList, false);
return insertPropsListView.writePropsList;
}
/**
* Check upate props.
*
* @param entityDef
* @param props
* @return
*/
private Map checkUpateProps(EntityDefinition entityDef, Map props) {
final UpdatePropsView updatePropsView = parseUpdateProps(entityDef, props, false);
return updatePropsView.updateProps;
}
/**
* Save handle result.
*
* @param resultHandle
* @param entityDef
* @param selectPropNames
* @param queryResult
* @param queryCache
* @param options
*/
private void saveHandleResult(Holder resultHandle, EntityDefinition entityDef, Collection selectPropNames, SQLResult queryResult,
QueryCache queryCache, Map options) {
if ((resultHandle != null)) {
if (resultHandle.value() == null) {
resultHandle.setValue(N.uuid());
long liveTime = getHandleLiveTime(options);
long maxIdleTime = getHandleMaxIdleTime(options);
HandleResult handleResult = new HandleResult(entityDef, new ArrayList<>(selectPropNames), queryResult, queryCache, liveTime, maxIdleTime);
if (!handleResultPool.put(resultHandle.value(), handleResult)) {
handleResult.close();
throw new RuntimeException(
"Failed to save the handled result because the pool is full. The max pool capacity is: " + handleResultPool.getCapacity());
}
} else {
if ((queryCache != null) && (handleResultPool.get(resultHandle.value()).getQueryCache() == null)) {
handleResultPool.get(resultHandle.value()).setQueryCache(queryCache);
}
}
}
}
/**
* Creates the query cache key.
*
* @param entityDef
* @param condition
* @return
*/
private String createQueryCacheKey(EntityDefinition entityDef, Condition condition) {
final String cacheKey = condition == null ? entityDef.getName() : condition.toString();
if (N.notNullOrEmpty(cacheKey)) {
if ((SQLParser.indexWord(cacheKey, WD.LIMIT, 0, false) >= 0) || (SQLParser.indexWord(cacheKey, WD.OFFSET, 0, false) >= 0)) {
throw new IllegalArgumentException("'LIMIT' or 'OFFSET' are supported for cached related operation");
}
}
return cacheKey;
}
/**
* Creates the query cache.
*
* @param cacheKey
* @param options
* @return
*/
private QueryCache createQueryCache(String cacheKey, final Map options) {
queryCacheRefLock.readLock().lock();
try {
QueryCache queryCache = queryCachePool.get(cacheKey);
if ((queryCache == null) && (queryCacheRefLock.getRefCount() == 0)) {
queryCache = createQueryCache(options);
queryCachePool.put(cacheKey, queryCache);
}
return queryCache;
} finally {
queryCacheRefLock.readLock().unlock();
}
}
/**
* Creates the query cache.
*
* @param options
* @return
*/
private QueryCache createQueryCache(Map options) {
final long liveTime = getQueryCacheLiveTime(entityManagerConfig.getQueryCacheConfiguration(), options);
final long maxIdleTime = getQueryCacheMaxIdleTime(entityManagerConfig.getQueryCacheConfiguration(), options);
return new SQLQueryCache(liveTime, maxIdleTime, dataCridCache);
}
}