
com.landawn.abacus.core.EntityManagerEx Maven / Gradle / Ivy
The newest version!
/*
* 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 java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.landawn.abacus.DataSet;
import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.EntityId;
import com.landawn.abacus.EntityManager;
import com.landawn.abacus.IsolationLevel;
import com.landawn.abacus.LockMode;
import com.landawn.abacus.Transaction.Action;
import com.landawn.abacus.annotation.Internal;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.metadata.EntityDefinition;
import com.landawn.abacus.metadata.EntityDefinitionFactory;
import com.landawn.abacus.metadata.Property;
import com.landawn.abacus.util.AsyncExecutor;
import com.landawn.abacus.util.ClassUtil;
import com.landawn.abacus.util.IOUtil;
import com.landawn.abacus.util.N;
import com.landawn.abacus.util.Objectory;
import com.landawn.abacus.util.Options;
import com.landawn.abacus.util.Options.Query;
import com.landawn.abacus.util.SQLBuilder.NSC;
import com.landawn.abacus.util.StringUtil.Strings;
import com.landawn.abacus.util.u.Holder;
import com.landawn.abacus.util.u.Nullable;
import com.landawn.abacus.util.u.Optional;
import com.landawn.abacus.util.u.OptionalBoolean;
import com.landawn.abacus.util.u.OptionalByte;
import com.landawn.abacus.util.u.OptionalChar;
import com.landawn.abacus.util.u.OptionalDouble;
import com.landawn.abacus.util.u.OptionalFloat;
import com.landawn.abacus.util.u.OptionalInt;
import com.landawn.abacus.util.u.OptionalLong;
import com.landawn.abacus.util.u.OptionalShort;
import com.landawn.abacus.util.function.Runnable;
import com.landawn.abacus.util.function.ToBooleanFunction;
import com.landawn.abacus.util.function.ToByteFunction;
import com.landawn.abacus.util.function.ToCharFunction;
import com.landawn.abacus.util.function.ToDoubleFunction;
import com.landawn.abacus.util.function.ToFloatFunction;
import com.landawn.abacus.util.function.ToIntFunction;
import com.landawn.abacus.util.function.ToLongFunction;
import com.landawn.abacus.util.function.ToShortFunction;
// TODO: Auto-generated Javadoc
/**
*
* @author Haiyang Li
* @param the entity type
* @since 0.8
*/
public final class EntityManagerEx implements EntityManager {
static final AsyncExecutor DEFAULT_ASYNC_EXECUTOR = new AsyncExecutor(Math.max(64, Math.min(IOUtil.CPU_CORES * 8, IOUtil.MAX_MEMORY_IN_MB / 1024) * 32),
Math.max(256, (IOUtil.MAX_MEMORY_IN_MB / 1024) * 64), 180L, TimeUnit.SECONDS);
private final AsyncExecutor asyncExecutor;
private final EntityManager entityManager;
private final boolean isVersionSupported;
EntityManagerEx(final EntityManager entityManager) {
this(entityManager, DEFAULT_ASYNC_EXECUTOR);
}
@SuppressWarnings("deprecation")
EntityManagerEx(final EntityManager entityManager, final AsyncExecutor asyncExecutor) {
this.entityManager = entityManager;
this.asyncExecutor = asyncExecutor;
boolean temp = true;
try {
Seid entityId = null;
for (EntityDefinition entityDef : getEntityDefinitionFactory().getDefinitionList()) {
if (N.notNullOrEmpty(entityDef.getIdPropertyList())) {
entityId = Seid.of(entityDef.getName());
for (Property idProp : entityDef.getIdPropertyList()) {
entityId.set(idProp.getName(), idProp.getType().defaultValue());
}
break;
}
}
getRecordVersion(entityId, null);
temp = true;
} catch (UnsupportedOperationException e) {
temp = false;
} catch (Exception e) {
// ignore.
}
this.isVersionSupported = temp;
}
/**
*
* @param entityName
* @param id
* @return true, if successful
*/
public boolean exists(final String entityName, final long id) {
return exists(createEntityId(entityName, id));
}
/**
*
* @param entityName
* @param id
* @return true, if successful
*/
public boolean exists(final String entityName, final String id) {
return exists(createEntityId(entityName, id));
}
/**
*
* @param entityId
* @return true, if successful
*/
public boolean exists(final EntityId entityId) {
return exists(entityId, null);
}
/**
*
* @param entityId
* @param options
* @return true, if successful
*/
public boolean exists(final EntityId entityId, final Map options) {
return gett(entityId, N.asList(entityId.keySet().iterator().next()), options) != null;
}
/**
*
* @param entityName
* @param cond
* @return true, if successful
*/
public boolean exists(final String entityName, final Condition cond) {
return exists(entityName, cond, null);
}
/**
*
* @param entityName
* @param cond
* @param options
* @return true, if successful
*/
public boolean exists(final String entityName, final Condition cond, final Map options) {
Map newOptions = setSingleResultOption(options);
return query(entityName, NSC._1_list, cond, newOptions).size() > 0;
}
/**
*
* @param entityName
* @param cond
* @return
*/
public int count(final String entityName, final Condition cond) {
return count(entityName, cond, null);
}
/**
*
* @param entityName
* @param cond
* @param options
* @return
*/
public int count(final String entityName, final Condition cond, final Map options) {
Map newOptions = setSingleResultOption(options);
return queryForSingleResult(int.class, entityName, NSC.COUNT_ALL, cond, newOptions).orElse(0);
}
/**
* Sets the single result option.
*
* @param options
* @return
*/
protected Map setSingleResultOption(final Map options) {
Map newOptions = EntityManagerUtil.copyOptions(options);
newOptions.put(Options.Query.COUNT, 1);
return newOptions;
}
/**
* Sets the unique result option.
*
* @param options
* @return
*/
protected Map setUniqueResultOption(final Map options) {
Map newOptions = EntityManagerUtil.copyOptions(options);
newOptions.put(Options.Query.COUNT, 2);
return newOptions;
}
/**
* Query for boolean.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalBoolean queryForBoolean(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Boolean.class, entityName, propName, cond).mapToBoolean(ToBooleanFunction.UNBOX);
}
/**
* Query for char.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalChar queryForChar(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Character.class, entityName, propName, cond).mapToChar(ToCharFunction.UNBOX);
}
/**
* Query for byte.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalByte queryForByte(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Byte.class, entityName, propName, cond).mapToByte(ToByteFunction.UNBOX);
}
/**
* Query for short.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalShort queryForShort(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Short.class, entityName, propName, cond).mapToShort(ToShortFunction.UNBOX);
}
/**
* Query for int.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalInt queryForInt(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Integer.class, entityName, propName, cond).mapToInt(ToIntFunction.UNBOX);
}
/**
* Query for long.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalLong queryForLong(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Long.class, entityName, propName, cond).mapToLong(ToLongFunction.UNBOX);
}
/**
* Query for float.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalFloat queryForFloat(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Float.class, entityName, propName, cond).mapToFloat(ToFloatFunction.UNBOX);
}
/**
* Query for double.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public OptionalDouble queryForDouble(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(Double.class, entityName, propName, cond).mapToDouble(ToDoubleFunction.UNBOX);
}
/**
* Query for string.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public Nullable queryForString(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(String.class, entityName, propName, cond);
}
/**
* Query for date.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public Nullable queryForDate(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(java.sql.Date.class, entityName, propName, cond);
}
/**
* Query for time.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public Nullable queryForTime(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(java.sql.Time.class, entityName, propName, cond);
}
/**
* Query for timestamp.
*
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(String, String, Condition).
*/
public Nullable queryForTimestamp(final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(java.sql.Timestamp.class, entityName, propName, cond);
}
/**
* Query for single result.
*
* @param the value type
* @param targetClass
* @param entityName
* @param propName
* @param cond
* @return
* @see SQLExecutor#queryForSingleResult(Class, String, String, Condition, Map).
*/
public Nullable queryForSingleResult(final Class targetClass, final String entityName, final String propName, final Condition cond) {
return queryForSingleResult(targetClass, entityName, propName, cond, null);
}
/**
* Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
*
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the value type
* @param targetClass set result type to avoid the NullPointerException if result is null and T is primitive type
* "int, long. short ... char, boolean..".
* @param entityName
* @param propName
* @param cond
* @param options
* @return
*/
@SuppressWarnings("unchecked")
public Nullable queryForSingleResult(final Class targetClass, final String entityName, final String propName, final Condition cond,
final Map options) {
// For performance improvement with EntityId when entity cache is enable.
if (cond instanceof EntityId && checkEntityName(entityName).getProperty(propName) != null) {
final Object entity = gett((EntityId) cond, N.asList(propName), options);
return entity == null ? Nullable. empty() : Nullable.of(N.convert(ClassUtil.getPropValue(entity, propName), targetClass));
} else {
final Map newOptions = setSingleResultOption(options);
final DataSet dataSet = this.query(entityName, N.asList(propName), cond, newOptions);
return N.isNullOrEmpty(dataSet) ? Nullable. empty() : Nullable.of(N.convert(dataSet.get(0), targetClass));
}
}
/**
* Query for single non null.
*
* @param the value type
* @param targetClass
* @param entityName
* @param propName
* @param cond
* @return
*/
public Optional queryForSingleNonNull(final Class targetClass, final String entityName, final String propName, final Condition cond) {
return queryForSingleNonNull(targetClass, entityName, propName, cond, null);
}
/**
* Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
*
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the value type
* @param targetClass set result type to avoid the NullPointerException if result is null and T is primitive type
* "int, long. short ... char, boolean..".
* @param entityName
* @param propName
* @param cond
* @param options
* @return
*/
@SuppressWarnings("unchecked")
public Optional queryForSingleNonNull(final Class targetClass, final String entityName, final String propName, final Condition cond,
final Map options) {
// For performance improvement with EntityId when entity cache is enable.
if (cond instanceof EntityId && checkEntityName(entityName).getProperty(propName) != null) {
final Object entity = gett((EntityId) cond, N.asList(propName), options);
return entity == null ? Optional. empty() : Optional.of(N.convert(ClassUtil.getPropValue(entity, propName), targetClass));
} else {
final Map newOptions = setSingleResultOption(options);
final DataSet dataSet = this.query(entityName, N.asList(propName), cond, newOptions);
return N.isNullOrEmpty(dataSet) ? Optional. empty() : Optional.of(N.convert(dataSet.get(0), targetClass));
}
}
/**
* Query for unique result.
*
* @param the value type
* @param targetClass
* @param entityName
* @param propName
* @param cond
* @return
* @throws DuplicatedResultException if more than one record found.
* @see SQLExecutor#queryForUniqueResult(Class, String, String, Condition, Map).
*/
public Nullable queryForUniqueResult(final Class targetClass, final String entityName, final String propName, final Condition cond)
throws DuplicatedResultException {
return queryForUniqueResult(targetClass, entityName, propName, cond, null);
}
/**
* Returns a {@code Nullable} describing the value in the first row/column if it exists, otherwise return an empty {@code Nullable}.
* And throws {@code DuplicatedResultException} if more than one record found.
*
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the value type
* @param targetClass set result type to avoid the NullPointerException if result is null and T is primitive type
* "int, long. short ... char, boolean..".
* @param entityName
* @param propName
* @param cond
* @param options
* @return
* @throws DuplicatedResultException if more than one record found.
*/
@SuppressWarnings("unchecked")
public Nullable queryForUniqueResult(final Class targetClass, final String entityName, final String propName, final Condition cond,
final Map options) throws DuplicatedResultException {
// For performance improvement with EntityId when entity cache is enable.
if (cond instanceof EntityId && checkEntityName(entityName).getProperty(propName) != null) {
final Object entity = gett((EntityId) cond, N.asList(propName), options);
return entity == null ? Nullable. empty() : Nullable.of(N.convert(ClassUtil.getPropValue(entity, propName), targetClass));
} else {
final Map newOptions = setUniqueResultOption(options);
final DataSet ds = this.query(entityName, N.asList(propName), cond, newOptions);
if (ds.isEmpty()) {
return Nullable.empty();
} else if (ds.size() == 1) {
return Nullable.of(N.convert(ds.get(0), targetClass));
} else {
throw new DuplicatedResultException("At least two results found: " + Strings.concat(ds.get(0, 0), ", ", ds.get(1, 0)));
}
}
}
/**
* Query for unique non null.
*
* @param the value type
* @param targetClass
* @param entityName
* @param propName
* @param cond
* @return
* @throws DuplicatedResultException if more than one record found.
*/
public Optional queryForUniqueNonNull(final Class targetClass, final String entityName, final String propName, final Condition cond)
throws DuplicatedResultException {
return queryForUniqueNonNull(targetClass, entityName, propName, cond, null);
}
/**
* Returns an {@code Optional} describing the value in the first row/column if it exists, otherwise return an empty {@code Optional}.
* And throws {@code DuplicatedResultException} if more than one record found.
*
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the value type
* @param targetClass set result type to avoid the NullPointerException if result is null and T is primitive type
* "int, long. short ... char, boolean..".
* @param entityName
* @param propName
* @param cond
* @param options
* @return
* @throws DuplicatedResultException if more than one record found.
*/
@SuppressWarnings("unchecked")
public Optional queryForUniqueNonNull(final Class targetClass, final String entityName, final String propName, final Condition cond,
final Map options) throws DuplicatedResultException {
// For performance improvement with EntityId when entity cache is enable.
if (cond instanceof EntityId && checkEntityName(entityName).getProperty(propName) != null) {
final Object entity = gett((EntityId) cond, N.asList(propName), options);
return entity == null ? Optional. empty() : Optional.of(N.convert(ClassUtil.getPropValue(entity, propName), targetClass));
} else {
final Map newOptions = setUniqueResultOption(options);
final DataSet ds = this.query(entityName, N.asList(propName), cond, newOptions);
if (ds.isEmpty()) {
return Optional.empty();
} else if (ds.size() == 1) {
return Optional.of(N.convert(ds.get(0), targetClass));
} else {
throw new DuplicatedResultException("At least two results found: " + Strings.concat(ds.get(0, 0), ", ", ds.get(1, 0)));
}
}
}
/**
*
* @param entityName
* @param selectPropNames
* @param cond
* @return
*/
@Override
public DataSet query(final String entityName, final Collection selectPropNames, final Condition cond) {
return query(entityName, selectPropNames, cond, null);
}
/**
*
* @param entityName
* @param selectPropNames
* @param cond
* @param options
* @return
*/
public DataSet query(final String entityName, final Collection selectPropNames, final Condition cond, final Map options) {
return entityManager.query(entityName, selectPropNames, cond, null, options);
}
/**
*
* @param entityName
* @param selectPropNames
* @param cond
* @param resultHandle
* @param options
* @return
*/
@Override
public DataSet query(final String entityName, final Collection selectPropNames, final Condition cond, final Holder resultHandle,
final Map options) {
return entityManager.query(entityName, selectPropNames, cond, resultHandle, options);
}
// /**
// * Query the same property list with the same condition from multiple entities(main entity and history entities).
// *
// * @param entityNameList
// * @param selectPropNames
// * the property name should not contains any entity name.
// * @param cond
// * the property name in the condition should not contains any entity name.
// * @param options
// * @return the merged result set.
// */
// public DataSet query(List entityNameList, Collection selectPropNames, Condition cond, Map options) {
// DataSet resultSet = null;
//
// for (String entityName : entityNameList) {
// if (resultSet == null) {
// resultSet = entityManager.query(entityName, selectPropNames, cond, null, options);
// } else {
// resultSet.merge(entityManager.query(entityName, selectPropNames, cond, null, options));
// }
// }
//
// return resultSet;
// }
/**
* Returns the merged ResultSet acquired by querying with the specified entity and its slices if it has.
* Mostly it's designed for partition to query different partitioning tables in the specified data sources.
*
* By default it's queried in parallel. but it can be set to sequential query by set Query.QUERY_IN_PARALLEL=false
in options
*
* @param entityName
* @param selectPropNames
* @param cond
* @param options Multiple data sources can be specified by query options: Query.QUERY_WITH_DATA_SOURCES
* @return
*/
public DataSet queryAll(final String entityName, final Collection selectPropNames, final Condition cond, final Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
if (options != null && options.containsKey(Query.OFFSET)) {
throw new IllegalArgumentException("offset can't be set for partitioning query");
}
final AtomicInteger counter = new AtomicInteger(
(options != null && options.containsKey(Query.COUNT)) ? (Integer) options.get(Query.COUNT) : Integer.MAX_VALUE);
return queryAll(entityDef, selectPropNames, cond, options, counter);
}
//
// public Map queryForMap(String entityName, Collection selectPropNames, Condition cond) {
// return queryForMap(entityName, selectPropNames, cond, null);
// }
//
// /**
// * Just fetch the result in the 1st row. {@code null} is returned if no result is found. Remember to add
// * {@code limit} condition if big result will be returned by the query.
// *
// * @param entityName
// * @param selectPropNames
// * @param cond
// * @param options
// * @return
// */
// @SuppressWarnings("unchecked")
// public Map queryForMap(String entityName, Collection selectPropNames, Condition cond, Map options) {
// Map newOptions = setSingleResultOption(options);
// DataSet dataSet = query(entityName, selectPropNames, cond, newOptions);
//
// return (dataSet.size() == 0) ? null : (Map) dataSet.getRow(Map.class, 0);
// }
/**
*
* @param the target entity type
* @param entityClass
* @param selectPropNames
* @param cond
* @return
*/
//
public Optional findFirst(final Class entityClass, final Collection selectPropNames, final Condition cond) {
return findFirst(entityClass, selectPropNames, cond, null);
}
/**
* Just fetch the result in the 1st row. {@code null} is returned if no result is found. This method will try to
* convert the column value to the type of mapping entity property if the mapping entity property is not assignable
* from column value.
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the target entity type
* @param entityClass
* @param selectPropNames
* @param cond
* @param options
* @return
*/
public Optional findFirst(final Class entityClass, final Collection selectPropNames, final Condition cond,
final Map options) {
final Map newOptions = setSingleResultOption(options);
final List entities = list(ClassUtil.getSimpleClassName(entityClass), selectPropNames, cond, newOptions);
return N.isNullOrEmpty(entities) ? (Optional) Optional.empty() : Optional.of(entities.get(0));
}
/**
*
* @param the target entity type
* @param entityName
* @param selectPropNames
* @param cond
* @return
*/
public Optional findFirst(final String entityName, final Collection selectPropNames, final Condition cond) {
return findFirst(entityName, selectPropNames, cond, null);
}
/**
* Just fetch the result in the 1st row. {@code null} is returned if no result is found. This method will try to
* convert the column value to the type of mapping entity property if the mapping entity property is not assignable
* from column value.
*
* Remember to add {@code limit} condition if big result will be returned by the query.
*
* @param the target entity type
* @param entityName
* @param selectPropNames
* @param cond
* @param options
* @return
*/
public Optional findFirst(final String entityName, final Collection selectPropNames, final Condition cond,
final Map options) {
final Map newOptions = setSingleResultOption(options);
final List entities = list(entityName, selectPropNames, cond, newOptions);
return N.isNullOrEmpty(entities) ? (Optional) Optional.empty() : Optional.of(entities.get(0));
}
/**
*
* @param entityDef
* @param selectPropNames
* @param cond
* @param options
* @param counter
* @return
*/
private DataSet queryAll(final EntityDefinition entityDef, final Collection selectPropNames, final Condition cond,
final Map options, final AtomicInteger counter) {
final Collection dataSourceNames = N.isNullOrEmpty(options) ? null : (Collection) options.get(Query.QUERY_WITH_DATA_SOURCES);
final boolean isQueryWithMultiDataSources = N.notNullOrEmpty(dataSourceNames);
final boolean isQueryInParallel = EntityManagerUtil.isQueryInParallel(options);
final List sliceEntityList = entityDef.getSliceEntityList();
if (isQueryWithMultiDataSources == false && N.isNullOrEmpty(sliceEntityList)) {
return query(entityDef.getName(), selectPropNames, cond, options);
}
final List resultSetList = Objectory.createList();
final List exceptionList = Objectory.createList();
final AtomicInteger activeThreadNum = new AtomicInteger(0);
try {
if (isQueryWithMultiDataSources) {
for (String dataSourceName : dataSourceNames) {
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.remove(Query.QUERY_WITH_DATA_SOURCES);
newOptions.put(Query.QUERY_WITH_DATA_SOURCE, dataSourceName);
final Runnable cmd = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
newOptions.put(Query.COUNT, count);
final DataSet resultSet = queryAll(entityDef, selectPropNames, cond, newOptions, counter);
synchronized (resultSetList) {
resultSetList.add(resultSet);
counter.addAndGet(-resultSet.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd);
} else {
cmd.run();
}
}
} else {
final Runnable cmd = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.put(Query.COUNT, count);
final DataSet resultSet = query(entityDef.getName(), selectPropNames, cond, newOptions);
synchronized (resultSetList) {
resultSetList.add(resultSet);
counter.addAndGet(-resultSet.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd);
} else {
cmd.run();
}
for (EntityDefinition e : sliceEntityList) {
final EntityDefinition sliceEntity = e;
final Runnable cmd2 = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.put(Query.COUNT, count);
final DataSet resultSet = query(sliceEntity.getName(), selectPropNames, cond, newOptions);
synchronized (resultSetList) {
resultSetList.add(resultSet);
counter.addAndGet(-resultSet.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd2);
} else {
cmd2.run();
}
}
}
while (activeThreadNum.get() > 0) {
N.sleep(1);
}
if (exceptionList.size() > 0) {
throw exceptionList.get(0);
}
DataSet dataSet = null;
int count = (options != null && options.containsKey(Query.COUNT)) ? (Integer) options.get(Query.COUNT) : Integer.MAX_VALUE;
for (int i = 0, len = resultSetList.size(); i < len; i++) {
if (N.isNullOrEmpty(dataSet)) {
dataSet = resultSetList.get(i);
} else {
dataSet = dataSet.merge(resultSetList.get(i));
}
if (dataSet.size() >= count) {
break;
}
}
if (dataSet.size() > count) {
dataSet = dataSet.copy(dataSet.columnNameList(), 0, count);
}
return dataSet;
} finally {
Objectory.recycle(exceptionList);
Objectory.recycle(resultSetList);
}
}
/**
*
* @param the target entity type
* @param entityName
* @param selectPropNames
* @param cond
* @return
*/
@Override
public List list(final String entityName, final Collection selectPropNames, final Condition cond) {
return list(entityName, selectPropNames, cond, null);
}
/**
*
* @param the target entity type
* @param entityName
* @param selectPropNames
* @param cond
* @param options
* @return
*/
@Override
public List list(final String entityName, final Collection selectPropNames, final Condition cond, final Map options) {
return entityManager.list(entityName, selectPropNames, cond, options);
}
/**
* Returns the merged entity list acquired by querying with the specified entity and its slices if it has.
* Mostly it's designed for partition to query different partitioning tables in the specified data sources.
*
* By default it's queried in parallel. but it can be set to sequential query by set Query.QUERY_IN_PARALLEL=false
in options
*
* @param the target entity type
* @param entityName
* @param selectPropNames
* @param cond
* @param options Multiple data sources can be specified by query options: Query.QUERY_WITH_DATA_SOURCES
* @return
*/
@SuppressWarnings("unchecked")
public List listAll(final String entityName, final Collection selectPropNames, final Condition cond, final Map options) {
final EntityDefinition entityDef = checkEntityName(entityName);
if (options != null && options.containsKey(Query.OFFSET)) {
throw new IllegalArgumentException("offset can't be set for partitioning query");
}
final AtomicInteger counter = new AtomicInteger(
(options != null && options.containsKey(Query.COUNT)) ? (Integer) options.get(Query.COUNT) : Integer.MAX_VALUE);
return listAll(entityDef, selectPropNames, cond, options, counter);
}
/**
*
* @param the target entity type
* @param entityDef
* @param selectPropNames
* @param cond
* @param options
* @param counter
* @return
*/
@SuppressWarnings("unchecked")
private List listAll(final EntityDefinition entityDef, final Collection selectPropNames, final Condition cond,
final Map options, final AtomicInteger counter) {
final Collection dataSourceNames = N.isNullOrEmpty(options) ? null : (Collection) options.get(Query.QUERY_WITH_DATA_SOURCES);
final boolean isQueryWithMultiDataSources = N.notNullOrEmpty(dataSourceNames);
final boolean isQueryInParallel = EntityManagerUtil.isQueryInParallel(options);
final List sliceEntityList = entityDef.getSliceEntityList();
if (isQueryWithMultiDataSources == false && N.isNullOrEmpty(sliceEntityList)) {
return list(entityDef.getName(), selectPropNames, cond, options);
}
final List> resultList = Objectory.createList();
final List exceptionList = Objectory.createList();
final AtomicInteger activeThreadNum = new AtomicInteger(0);
try {
if (isQueryWithMultiDataSources) {
for (String dataSourceName : dataSourceNames) {
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.remove(Query.QUERY_WITH_DATA_SOURCES);
newOptions.put(Query.QUERY_WITH_DATA_SOURCE, dataSourceName);
final Runnable cmd = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
newOptions.put(Query.COUNT, count);
final List entities = listAll(entityDef, selectPropNames, cond, newOptions, counter);
synchronized (resultList) {
resultList.add(entities);
counter.addAndGet(-entities.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd);
} else {
cmd.run();
}
}
} else {
final Runnable cmd = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.put(Query.COUNT, count);
final List entities = list(entityDef.getName(), selectPropNames, cond, newOptions);
synchronized (resultList) {
resultList.add(entities);
counter.addAndGet(-entities.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd);
} else {
cmd.run();
}
for (EntityDefinition e : sliceEntityList) {
final EntityDefinition sliceEntity = e;
final Runnable cmd2 = new Runnable() {
@Override
public void run() {
try {
final int count = counter.get();
if (count <= 0 || exceptionList.size() > 0) {
return;
}
final Map newOptions = options == null ? Options.create() : Options.copy(options);
newOptions.put(Query.COUNT, count);
List entities = list(sliceEntity.getName(), selectPropNames, cond, newOptions);
synchronized (resultList) {
resultList.add(entities);
counter.addAndGet(-entities.size());
}
} catch (RuntimeException e) {
if (isTableNotExistsException(e)) {
// ignore;
} else {
synchronized (exceptionList) {
exceptionList.add(e);
}
}
} finally {
activeThreadNum.decrementAndGet();
}
}
};
activeThreadNum.incrementAndGet();
if (isQueryInParallel) {
asyncExecutor.execute(cmd2);
} else {
cmd2.run();
}
}
}
while (activeThreadNum.get() > 0) {
N.sleep(1);
}
if (exceptionList.size() > 0) {
throw exceptionList.get(0);
}
if (resultList.size() == 1) {
return resultList.get(0);
}
final List entityList = new ArrayList<>();
int count = (options != null && options.containsKey(Query.COUNT)) ? (Integer) options.get(Query.COUNT) : Integer.MAX_VALUE;
for (int i = 0, len = resultList.size(); i < len; i++) {
if (N.notNullOrEmpty(resultList.get(i))) {
for (TT entity : resultList.get(i)) {
entityList.add(entity);
if (entityList.size() >= count) {
break;
}
}
}
if (entityList.size() >= count) {
break;
}
}
return entityList;
} finally {
Objectory.recycle(exceptionList);
Objectory.recycle(resultList);
}
}
/**
*
* @param entityName
* @param props
* @return
*/
public EntityId add(final String entityName, final Map props) {
return add(entityName, props, null);
}
/**
*
* @param entityName
* @param props
* @param options
* @return
*/
@Override
public EntityId add(final String entityName, final Map props, final Map options) {
return entityManager.add(entityName, props, options);
}
/**
* Adds the all.
*
* @param entityName
* @param propsList
* @return
*/
public List addAll(final String entityName, final List