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.util.DaoUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2019, 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.util;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.OptionalLong;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.regex.Pattern;
import com.landawn.abacus.DataSet;
import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.EntityId;
import com.landawn.abacus.cache.Cache;
import com.landawn.abacus.cache.CacheFactory;
import com.landawn.abacus.condition.Condition;
import com.landawn.abacus.condition.ConditionFactory.CF;
import com.landawn.abacus.core.DirtyMarkerUtil;
import com.landawn.abacus.exception.DuplicatedResultException;
import com.landawn.abacus.exception.UncheckedSQLException;
import com.landawn.abacus.logging.Logger;
import com.landawn.abacus.logging.LoggerFactory;
import com.landawn.abacus.parser.KryoParser;
import com.landawn.abacus.parser.ParserFactory;
import com.landawn.abacus.parser.ParserUtil;
import com.landawn.abacus.parser.ParserUtil.EntityInfo;
import com.landawn.abacus.parser.ParserUtil.PropInfo;
import com.landawn.abacus.type.Type;
import com.landawn.abacus.util.Columns.ColumnOne;
import com.landawn.abacus.util.Fn.IntFunctions;
import com.landawn.abacus.util.JdbcUtil.BiResultExtractor;
import com.landawn.abacus.util.JdbcUtil.BiRowFilter;
import com.landawn.abacus.util.JdbcUtil.BiRowMapper;
import com.landawn.abacus.util.JdbcUtil.Dao;
import com.landawn.abacus.util.JdbcUtil.Dao.OP;
import com.landawn.abacus.util.JdbcUtil.Dao.OutParameter;
import com.landawn.abacus.util.JdbcUtil.NamedQuery;
import com.landawn.abacus.util.JdbcUtil.PreparedCallableQuery;
import com.landawn.abacus.util.JdbcUtil.ResultExtractor;
import com.landawn.abacus.util.JdbcUtil.RowFilter;
import com.landawn.abacus.util.SQLBuilder.NAC;
import com.landawn.abacus.util.SQLBuilder.NLC;
import com.landawn.abacus.util.SQLBuilder.NSC;
import com.landawn.abacus.util.SQLBuilder.PAC;
import com.landawn.abacus.util.SQLBuilder.PLC;
import com.landawn.abacus.util.SQLBuilder.PSC;
import com.landawn.abacus.util.SQLBuilder.SP;
import com.landawn.abacus.util.Tuple.Tuple2;
import com.landawn.abacus.util.Tuple.Tuple3;
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.OptionalFloat;
import com.landawn.abacus.util.u.OptionalShort;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.LongFunction;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.Supplier;
import com.landawn.abacus.util.function.TriFunction;
import com.landawn.abacus.util.stream.BaseStream;
import com.landawn.abacus.util.stream.EntryStream;
import com.landawn.abacus.util.stream.IntStream.IntStreamEx;
import com.landawn.abacus.util.stream.Stream;
import com.landawn.abacus.util.stream.Stream.StreamEx;
@SuppressWarnings("deprecation")
final class DaoUtil {
private DaoUtil() {
// singleton for utility class.
}
private static final KryoParser kryoParser = ParserFactory.isKryoAvailable() ? ParserFactory.createKryoParser() : null;
/** The Constant daoPool. */
@SuppressWarnings("rawtypes")
private static final Map daoPool = new ConcurrentHashMap<>();
/** The Constant sqlAnnoMap. */
private static final Map, BiFunction> sqlAnnoMap = new HashMap<>();
static {
sqlAnnoMap.put(Dao.Select.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.Select tmp = (Dao.Select) anno;
int queryTimeout = tmp.queryTimeout();
int fetchSize = tmp.fetchSize();
final boolean isBatch = false;
final int batchSize = -1;
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = tmp.isSingleParameter();
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).getParameterizedSql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
if (attrs.containsKey(SQLMapper.FETCH_SIZE)) {
fetchSize = N.parseInt(attrs.get(SQLMapper.FETCH_SIZE));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.Insert.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.Insert tmp = (Dao.Insert) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = OP.DEFAULT;
final boolean isSingleParameter = tmp.isSingleParameter();
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).getParameterizedSql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.Update.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.Update tmp = (Dao.Update) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = tmp.isSingleParameter();
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).getParameterizedSql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.Delete.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.Delete tmp = (Dao.Delete) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = tmp.isSingleParameter();
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).getParameterizedSql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.NamedSelect.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.NamedSelect tmp = (Dao.NamedSelect) anno;
int queryTimeout = tmp.queryTimeout();
int fetchSize = tmp.fetchSize();
final boolean isBatch = false;
final int batchSize = -1;
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = false;
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).sql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
if (attrs.containsKey(SQLMapper.FETCH_SIZE)) {
fetchSize = N.parseInt(attrs.get(SQLMapper.FETCH_SIZE));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.NamedInsert.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.NamedInsert tmp = (Dao.NamedInsert) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = OP.DEFAULT;
final boolean isSingleParameter = false;
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).sql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
if (attrs.containsKey(SQLMapper.BATCH_SIZE)) {
batchSize = N.parseInt(attrs.get(SQLMapper.BATCH_SIZE));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.NamedUpdate.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.NamedUpdate tmp = (Dao.NamedUpdate) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = false;
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).sql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
if (attrs.containsKey(SQLMapper.BATCH_SIZE)) {
batchSize = N.parseInt(attrs.get(SQLMapper.BATCH_SIZE));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.NamedDelete.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.NamedDelete tmp = (Dao.NamedDelete) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = tmp.isBatch();
int batchSize = tmp.batchSize();
final OP op = tmp.op() == null ? OP.DEFAULT : tmp.op();
final boolean isSingleParameter = false;
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).sql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
if (attrs.containsKey(SQLMapper.BATCH_SIZE)) {
batchSize = N.parseInt(attrs.get(SQLMapper.BATCH_SIZE));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
sqlAnnoMap.put(Dao.Call.class, (Annotation anno, SQLMapper sqlMapper) -> {
final Dao.Call tmp = (Dao.Call) anno;
int queryTimeout = tmp.queryTimeout();
final int fetchSize = -1;
final boolean isBatch = false;
final int batchSize = -1;
final OP op = OP.DEFAULT;
final boolean isSingleParameter = tmp.isSingleParameter();
String sql = StringUtil.trim(tmp.sql());
if (N.isNullOrEmpty(sql)) {
sql = StringUtil.trim(tmp.value());
}
if (N.notNullOrEmpty(tmp.id())) {
final String id = tmp.id();
if (sqlMapper == null) {
throw new IllegalArgumentException("No SQLMapper is defined or passed for id: " + id);
}
sql = sqlMapper.get(id).getParameterizedSql();
if (N.isNullOrEmpty(sql)) {
throw new IllegalArgumentException("No sql is found in SQLMapper by id: " + id);
}
final Map attrs = sqlMapper.getAttrs(id);
if (N.notNullOrEmpty(attrs)) {
if (attrs.containsKey(SQLMapper.TIMEOUT)) {
queryTimeout = N.parseInt(attrs.get(SQLMapper.TIMEOUT));
}
}
}
return new QueryInfo(sql, queryTimeout, fetchSize, isBatch, batchSize, op, isSingleParameter);
});
}
@SuppressWarnings("rawtypes")
private static final Map, Predicate> isValuePresentMap = N.newHashMap(20);
static {
final Map, Predicate>> tmp = N.newHashMap(20);
tmp.put(u.Nullable.class, (u.Nullable> t) -> t.isPresent());
tmp.put(u.Optional.class, (u.Optional> t) -> t.isPresent());
tmp.put(u.OptionalBoolean.class, (u.OptionalBoolean t) -> t.isPresent());
tmp.put(u.OptionalChar.class, (u.OptionalChar t) -> t.isPresent());
tmp.put(u.OptionalByte.class, (u.OptionalByte t) -> t.isPresent());
tmp.put(u.OptionalShort.class, (u.OptionalShort t) -> t.isPresent());
tmp.put(u.OptionalInt.class, (u.OptionalInt t) -> t.isPresent());
tmp.put(u.OptionalLong.class, (u.OptionalLong t) -> t.isPresent());
tmp.put(u.OptionalFloat.class, (u.OptionalFloat t) -> t.isPresent());
tmp.put(u.OptionalDouble.class, (u.OptionalDouble t) -> t.isPresent());
tmp.put(java.util.Optional.class, (java.util.Optional> t) -> t.isPresent());
tmp.put(java.util.OptionalInt.class, (java.util.OptionalInt t) -> t.isPresent());
tmp.put(java.util.OptionalLong.class, (java.util.OptionalLong t) -> t.isPresent());
tmp.put(java.util.OptionalDouble.class, (java.util.OptionalDouble t) -> t.isPresent());
isValuePresentMap.putAll(tmp);
}
private static Set> notCacheableTypes = N.asSet(void.class, Void.class, Iterator.class, java.util.stream.BaseStream.class, BaseStream.class,
EntryStream.class, ExceptionalStream.class);
/**
* Creates the method handle.
*
* @param method
* @return
*/
private static MethodHandle createMethodHandle(final Method method) {
final Class> declaringClass = method.getDeclaringClass();
try {
return MethodHandles.lookup().in(declaringClass).unreflectSpecial(method, declaringClass);
} catch (Exception e) {
try {
final Constructor constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
ClassUtil.setAccessible(constructor, true);
return constructor.newInstance(declaringClass).in(declaringClass).unreflectSpecial(method, declaringClass);
} catch (Exception ex) {
try {
return MethodHandles.lookup()
.findSpecial(declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
declaringClass);
} catch (Exception exx) {
throw new UnsupportedOperationException(exx);
}
}
}
}
/**
* Checks if is list query.
*
* @param method
* @return true, if is list query
*/
private static boolean isListQuery(final Method method, final OP op, final String fullClassMethodName) {
final Class>[] paramTypes = method.getParameterTypes();
final Class> returnType = method.getReturnType();
final int paramLen = paramTypes.length;
if (op == OP.list) {
if (!(List.class.isAssignableFrom(returnType))) {
throw new UnsupportedOperationException(
"The result type of exists OP must be List, can't be: " + returnType + " in method: " + fullClassMethodName);
}
return true;
} else if (op != OP.DEFAULT) {
return false;
}
if (List.class.isAssignableFrom(returnType)) {
// Check if return type is generic List type.
if (method.getGenericReturnType() instanceof ParameterizedType) {
final ParameterizedType parameterizedReturnType = (ParameterizedType) method.getGenericReturnType();
if (paramLen > 0
&& (JdbcUtil.RowMapper.class.isAssignableFrom(paramTypes[paramLen - 1]) || BiRowMapper.class.isAssignableFrom(paramTypes[paramLen - 1]))
&& method.getGenericParameterTypes()[paramLen - 1] instanceof ParameterizedType) {
// if the return type of the method is same as the return type of RowMapper/BiRowMapper parameter, return false;
return !parameterizedReturnType.equals(((ParameterizedType) method.getGenericParameterTypes()[paramLen - 1]).getActualTypeArguments()[0]);
}
}
return !(method.getName().startsWith("get") || method.getName().startsWith("findFirst") || method.getName().startsWith("findOne"));
} else {
return false;
}
}
private static boolean isExistsQuery(final Method method, final OP op, final String fullClassMethodName) {
final String methodName = method.getName();
final Class> returnType = method.getReturnType();
if (op == OP.exists) {
if (!(boolean.class.equals(returnType) || Boolean.class.equals(returnType))) {
throw new UnsupportedOperationException(
"The result type of exists OP must be boolean or Boolean, can't be: " + returnType + " in method: " + fullClassMethodName);
}
return true;
} else if (op != OP.DEFAULT) {
return false;
}
return (boolean.class.equals(returnType) || Boolean.class.equals(returnType))
&& ((methodName.startsWith("exists") && (methodName.length() == 6 || Character.isUpperCase(methodName.charAt(6))))
|| (methodName.startsWith("exist") && (methodName.length() == 5 || Character.isUpperCase(methodName.charAt(5))))
|| (methodName.startsWith("has") && (methodName.length() == 3 || Character.isUpperCase(methodName.charAt(3)))));
}
@SuppressWarnings("rawtypes")
private static Throwables.BiFunction createSingleQueryFunction(final Class> returnType) {
if (OptionalBoolean.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForBoolean();
} else if (OptionalChar.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForChar();
} else if (OptionalByte.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForByte();
} else if (OptionalShort.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForShort();
} else if (OptionalInt.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForInt();
} else if (OptionalLong.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForLong();
} else if (OptionalFloat.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForFloat();
} else if (OptionalDouble.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.queryForDouble();
} else {
return (preparedQuery, args) -> (R) preparedQuery.queryForSingleResult(returnType).orElse(N.defaultValueOf(returnType));
}
}
@SuppressWarnings("rawtypes")
private static Throwables.BiFunction createQueryFunctionByMethod(final Method method,
final boolean hasRowMapperOrExtractor, final boolean hasRowFilter, final OP op, final String fullClassMethodName) {
method.getName();
final Class>[] paramTypes = method.getParameterTypes();
final Class> returnType = method.getReturnType();
final int paramLen = paramTypes.length;
final Class> lastParamType = paramLen == 0 ? null : paramTypes[paramLen - 1];
final boolean isListQuery = isListQuery(method, op, fullClassMethodName);
final boolean isExists = isExistsQuery(method, op, fullClassMethodName);
if ((op == OP.stream && !(Stream.class.isAssignableFrom(returnType) || ExceptionalStream.class.isAssignableFrom(returnType)))
|| (op == OP.query && !(DataSet.class.isAssignableFrom(returnType) || lastParamType == null
|| ResultExtractor.class.isAssignableFrom(lastParamType) || BiResultExtractor.class.isAssignableFrom(lastParamType)))) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
if ((op == OP.get || op == OP.findFirst) && Nullable.class.isAssignableFrom(returnType)) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
// if ((op == OP.queryForSingle || op == OP.queryForUnique)
// && !(Optional.class.isAssignableFrom(returnType) || Nullable.class.isAssignableFrom(returnType))) {
// throw new UnsupportedOperationException(
// "The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
// }
if ((Stream.class.isAssignableFrom(returnType) || ExceptionalStream.class.isAssignableFrom(returnType)) && !(op == OP.stream || op == OP.DEFAULT)) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
if (DataSet.class.isAssignableFrom(returnType) && !(op == OP.query || op == OP.DEFAULT)) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
if (Optional.class.isAssignableFrom(returnType)
&& !(op == OP.get || op == OP.findFirst || op == OP.queryForSingle || op == OP.queryForUnique || op == OP.DEFAULT)) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
if (Nullable.class.isAssignableFrom(returnType) && !(op == OP.queryForSingle || op == OP.queryForUnique || op == OP.DEFAULT)) {
throw new UnsupportedOperationException(
"The return type: " + returnType + " of method: " + fullClassMethodName + " is not supported the specified op: " + op);
}
if (hasRowMapperOrExtractor) {
if (!(op == OP.get || op == OP.findFirst || op == OP.list || op == OP.stream)) {
throw new UnsupportedOperationException("RowMapper/ResultExtractor is not supported by OP: " + op + " in method: " + fullClassMethodName);
}
if (hasRowFilter && (op == OP.get || op == OP.query)) {
throw new UnsupportedOperationException("RowFilter is not supported by OP: " + op + " in method: " + fullClassMethodName);
}
if (JdbcUtil.RowMapper.class.isAssignableFrom(lastParamType)) {
if (isListQuery) {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.list((RowFilter) args[paramLen - 2], (JdbcUtil.RowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.list((JdbcUtil.RowMapper) args[paramLen - 1]);
}
} else if (Optional.class.isAssignableFrom(returnType)) {
if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.get((JdbcUtil.RowMapper) args[paramLen - 1]);
} else {
if (hasRowFilter) {
return (preparedQuery,
args) -> (R) preparedQuery.findFirst((RowFilter) args[paramLen - 2], (JdbcUtil.RowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((JdbcUtil.RowMapper) args[paramLen - 1]);
}
}
} else if (ExceptionalStream.class.isAssignableFrom(returnType)) {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.stream((RowFilter) args[paramLen - 2], (JdbcUtil.RowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.stream((JdbcUtil.RowMapper) args[paramLen - 1]);
}
} else if (Stream.class.isAssignableFrom(returnType)) {
if (hasRowFilter) {
return (preparedQuery,
args) -> (R) preparedQuery.stream((RowFilter) args[paramLen - 2], (JdbcUtil.RowMapper) args[paramLen - 1]).unchecked();
} else {
return (preparedQuery, args) -> (R) preparedQuery.stream((JdbcUtil.RowMapper) args[paramLen - 1]).unchecked();
}
} else {
if (Nullable.class.isAssignableFrom(returnType)) {
throw new UnsupportedOperationException(
"The return type of method: " + method.getName() + " can't be: " + returnType + " when RowMapper/BiRowMapper is specified");
}
if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.gett((JdbcUtil.RowMapper) args[paramLen - 1]);
} else {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((RowFilter) args[paramLen - 2], (JdbcUtil.RowMapper) args[paramLen - 1])
.orElse(N.defaultValueOf(returnType));
} else {
return (preparedQuery,
args) -> (R) preparedQuery.findFirst((JdbcUtil.RowMapper) args[paramLen - 1]).orElse(N.defaultValueOf(returnType));
}
}
}
} else if (BiRowMapper.class.isAssignableFrom(lastParamType)) {
if (isListQuery) {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.list((BiRowFilter) args[paramLen - 2], (BiRowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.list((BiRowMapper) args[paramLen - 1]);
}
} else if (Optional.class.isAssignableFrom(returnType)) {
if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.get((BiRowMapper) args[paramLen - 1]);
} else {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((BiRowFilter) args[paramLen - 2], (BiRowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((BiRowMapper) args[paramLen - 1]);
}
}
} else if (ExceptionalStream.class.isAssignableFrom(returnType)) {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.stream((BiRowFilter) args[paramLen - 2], (BiRowMapper) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.stream((BiRowMapper) args[paramLen - 1]);
}
} else if (Stream.class.isAssignableFrom(returnType)) {
if (hasRowFilter) {
return (preparedQuery,
args) -> (R) preparedQuery.stream((BiRowFilter) args[paramLen - 2], (BiRowMapper) args[paramLen - 1]).unchecked();
} else {
return (preparedQuery, args) -> (R) preparedQuery.stream((BiRowMapper) args[paramLen - 1]).unchecked();
}
} else {
if (Nullable.class.isAssignableFrom(returnType)) {
throw new UnsupportedOperationException(
"The return type of method: " + method.getName() + " can't be: " + returnType + " when RowMapper/BiRowMapper is specified");
}
if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.gett((BiRowMapper) args[paramLen - 1]);
} else {
if (hasRowFilter) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((BiRowFilter) args[paramLen - 2], (BiRowMapper) args[paramLen - 1])
.orElse(N.defaultValueOf(returnType));
} else {
return (preparedQuery, args) -> (R) preparedQuery.findFirst((BiRowMapper) args[paramLen - 1]).orElse(N.defaultValueOf(returnType));
}
}
}
} else {
if (!(op == OP.query || op == OP.DEFAULT)) {
throw new UnsupportedOperationException(
"ResultExtractor/BiResultExtractor is not supported by OP: " + op + " in method: " + fullClassMethodName);
}
if (method.getGenericParameterTypes()[paramLen - 1] instanceof ParameterizedType) {
final java.lang.reflect.Type resultExtractorReturnType = ((ParameterizedType) method.getGenericParameterTypes()[paramLen - 1])
.getActualTypeArguments()[0];
final Class> resultExtractorReturnClass = resultExtractorReturnType instanceof Class ? (Class>) resultExtractorReturnType
: (Class>) ((ParameterizedType) resultExtractorReturnType).getRawType();
if (returnType.isAssignableFrom(resultExtractorReturnClass)) {
throw new UnsupportedOperationException("The return type: " + returnType + " of method: " + method.getName()
+ " is not assignable from the return type of ResultExtractor: " + resultExtractorReturnClass);
}
}
if (ResultExtractor.class.isAssignableFrom(lastParamType)) {
return (preparedQuery, args) -> (R) preparedQuery.query((ResultExtractor) args[paramLen - 1]);
} else {
return (preparedQuery, args) -> (R) preparedQuery.query((BiResultExtractor) args[paramLen - 1]);
}
}
} else if (isExists) {
return (preparedQuery, args) -> (R) (Boolean) preparedQuery.exists();
} else if (isListQuery) {
final ParameterizedType parameterizedReturnType = (ParameterizedType) method.getGenericReturnType();
final Class> eleType = parameterizedReturnType.getActualTypeArguments()[0] instanceof Class
? (Class>) parameterizedReturnType.getActualTypeArguments()[0]
: (Class>) ((ParameterizedType) parameterizedReturnType.getActualTypeArguments()[0]).getRawType();
return (preparedQuery, args) -> (R) preparedQuery.list(eleType);
} else if (DataSet.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.query();
} else if (Optional.class.isAssignableFrom(returnType) || Nullable.class.isAssignableFrom(returnType)) {
final ParameterizedType parameterizedReturnType = (ParameterizedType) method.getGenericReturnType();
final Class> eleType = parameterizedReturnType.getActualTypeArguments()[0] instanceof Class
? (Class>) parameterizedReturnType.getActualTypeArguments()[0]
: (Class>) ((ParameterizedType) parameterizedReturnType.getActualTypeArguments()[0]).getRawType();
if (Nullable.class.isAssignableFrom(returnType)) {
if (op == OP.queryForSingle) {
return (preparedQuery, args) -> (R) preparedQuery.queryForSingleResult(eleType);
} else if (op == OP.queryForUnique) {
return (preparedQuery, args) -> (R) preparedQuery.queryForUniqueResult(eleType);
} else {
return (preparedQuery, args) -> (R) preparedQuery.queryForSingleResult(eleType);
}
} else {
if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.get(BiRowMapper.to(eleType));
} else if (op == OP.findFirst) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst(BiRowMapper.to(eleType));
} else if (op == OP.queryForSingle) {
return (preparedQuery, args) -> (R) preparedQuery.queryForSingleNonNull(eleType);
} else if (op == OP.queryForUnique) {
return (preparedQuery, args) -> (R) preparedQuery.queryForUniqueNonNull(eleType);
} else {
if (ClassUtil.isEntity(eleType) || Map.class.isAssignableFrom(eleType) || List.class.isAssignableFrom(eleType)
|| Object[].class.isAssignableFrom(eleType)) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst(BiRowMapper.to(eleType));
} else {
return (preparedQuery, args) -> (R) preparedQuery.queryForSingleNonNull(eleType);
}
}
}
} else if (ExceptionalStream.class.isAssignableFrom(returnType) || Stream.class.isAssignableFrom(returnType)) {
final ParameterizedType parameterizedReturnType = (ParameterizedType) method.getGenericReturnType();
final Class> eleType = parameterizedReturnType.getActualTypeArguments()[0] instanceof Class
? (Class>) parameterizedReturnType.getActualTypeArguments()[0]
: (Class>) ((ParameterizedType) parameterizedReturnType.getActualTypeArguments()[0]).getRawType();
if (ExceptionalStream.class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.stream(eleType);
} else {
return (preparedQuery, args) -> (R) preparedQuery.stream(eleType).unchecked();
}
} else if (op == OP.get) {
return (preparedQuery, args) -> (R) preparedQuery.gett(BiRowMapper.to(returnType));
} else if (op == OP.findFirst) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst(BiRowMapper.to(returnType)).orNull();
} else if (op == OP.queryForSingle) {
return createSingleQueryFunction(returnType);
} else if (op == OP.queryForUnique) {
return (preparedQuery, args) -> (R) preparedQuery.queryForUniqueResult(returnType).orElse(N.defaultValueOf(returnType));
} else {
if (ClassUtil.isEntity(returnType) || Map.class.isAssignableFrom(returnType) || List.class.isAssignableFrom(returnType)
|| Object[].class.isAssignableFrom(returnType)) {
return (preparedQuery, args) -> (R) preparedQuery.findFirst(BiRowMapper.to(returnType)).orNull();
} else {
return createSingleQueryFunction(returnType);
}
}
}
@SuppressWarnings("rawtypes")
private static JdbcUtil.BiParametersSetter createParametersSetter(Method m, final String fullClassMethodName,
final Class>[] paramTypes, final int paramLen, final boolean isBatch, final boolean isSingleParameter, final boolean isCall,
final boolean isNamedQuery, final ParsedSql namedSql, final int defineParamLen, final int[] stmtParamIndexes, final int stmtParamLen) {
JdbcUtil.BiParametersSetter parametersSetter = null;
boolean hasParameterSetter = false;
if (hasParameterSetter = paramLen - defineParamLen > 0 && JdbcUtil.ParametersSetter.class.isAssignableFrom(paramTypes[defineParamLen])) {
parametersSetter = (preparedQuery, args) -> preparedQuery.settParameters((JdbcUtil.ParametersSetter) args[defineParamLen]);
} else if (hasParameterSetter = paramLen - defineParamLen > 1 && JdbcUtil.BiParametersSetter.class.isAssignableFrom(paramTypes[defineParamLen + 1])) {
parametersSetter = (preparedQuery, args) -> preparedQuery.settParameters(args[defineParamLen],
(JdbcUtil.BiParametersSetter) args[defineParamLen + 1]);
} else if (hasParameterSetter = paramLen - defineParamLen > 1 && JdbcUtil.TriParametersSetter.class.isAssignableFrom(paramTypes[defineParamLen + 1])) {
parametersSetter = (preparedQuery, args) -> ((NamedQuery) preparedQuery).setParameters(args[defineParamLen],
(JdbcUtil.TriParametersSetter) args[defineParamLen + 1]);
}
if (hasParameterSetter) {
throw new UnsupportedOperationException(
"Setting parameters by 'ParametersSetter/BiParametersSetter/TriParametersSetter' is not enabled at present. Can't use it in method: "
+ fullClassMethodName);
}
if (parametersSetter != null || stmtParamLen == 0 || isBatch) {
// ignore
} else if (stmtParamLen == 1) {
final Class> paramTypeOne = paramTypes[stmtParamIndexes[0]];
if (isCall) {
final String paramName = StreamEx.of(m.getParameterAnnotations()[stmtParamIndexes[0]])
.select(Dao.Bind.class)
.map(b -> b.value())
.first()
.orNull();
if (N.notNullOrEmpty(paramName)) {
parametersSetter = (preparedQuery, args) -> ((PreparedCallableQuery) preparedQuery).setObject(paramName, args[stmtParamIndexes[0]]);
} else if (isSingleParameter) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setObject(1, args[stmtParamIndexes[0]]);
} else if (Map.class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> ((PreparedCallableQuery) preparedQuery)
.setParameters((Map) args[stmtParamIndexes[0]]);
} else if (ClassUtil.isEntity(paramTypeOne) || EntityId.class.isAssignableFrom(paramTypeOne)) {
throw new UnsupportedOperationException("In method: " + fullClassMethodName
+ ", parameters for call(procedure) have to be binded with names through annotation @Bind, or Map. Entity/EntityId type parameter are not supported");
} else if (Collection.class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters((Collection) args[stmtParamIndexes[0]]);
} else if (Object[].class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters((Object[]) args[stmtParamIndexes[0]]);
} else {
parametersSetter = (preparedQuery, args) -> preparedQuery.setObject(1, args[stmtParamIndexes[0]]);
}
} else if (isNamedQuery) {
final String paramName = StreamEx.of(m.getParameterAnnotations()[stmtParamIndexes[0]])
.select(Dao.Bind.class)
.map(b -> b.value())
.first()
.orNull();
if (N.notNullOrEmpty(paramName)) {
parametersSetter = (preparedQuery, args) -> ((NamedQuery) preparedQuery).setObject(paramName, args[stmtParamIndexes[0]]);
} else if (isSingleParameter) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setObject(1, args[stmtParamIndexes[0]]);
} else if (ClassUtil.isEntity(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> ((NamedQuery) preparedQuery).setParameters(args[stmtParamIndexes[0]]);
} else if (Map.class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> ((NamedQuery) preparedQuery).setParameters((Map) args[stmtParamIndexes[0]]);
} else if (EntityId.class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> ((NamedQuery) preparedQuery).setParameters(args[stmtParamIndexes[0]]);
} else {
throw new UnsupportedOperationException("In method: " + fullClassMethodName
+ ", parameters for named query have to be binded with names through annotation @Bind, or Map/Entity with getter/setter methods. Can not be: "
+ ClassUtil.getSimpleClassName(paramTypeOne));
}
} else {
if (isSingleParameter) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setObject(1, args[stmtParamIndexes[0]]);
} else if (Collection.class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters((Collection) args[stmtParamIndexes[0]]);
} else if (Object[].class.isAssignableFrom(paramTypeOne)) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters((Object[]) args[stmtParamIndexes[0]]);
} else {
parametersSetter = (preparedQuery, args) -> preparedQuery.setObject(1, args[stmtParamIndexes[0]]);
}
}
} else {
if (isCall) {
final String[] paramNames = IntStreamEx.of(stmtParamIndexes)
.mapToObj(i -> StreamEx.of(m.getParameterAnnotations()[i]).select(Dao.Bind.class).first().orElse(null))
.skipNull()
.map(b -> b.value())
.toArray(IntFunctions.ofStringArray());
if (N.notNullOrEmpty(paramNames)) {
parametersSetter = (preparedQuery, args) -> {
final PreparedCallableQuery namedQuery = ((PreparedCallableQuery) preparedQuery);
for (int i = 0; i < stmtParamLen; i++) {
namedQuery.setObject(paramNames[i], args[stmtParamIndexes[i]]);
}
};
} else {
if (stmtParamLen == paramLen) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters(args);
} else {
parametersSetter = (preparedQuery, args) -> {
for (int i = 0; i < stmtParamLen; i++) {
preparedQuery.setObject(i + 1, args[stmtParamIndexes[i]]);
}
};
}
}
} else if (isNamedQuery) {
final String[] paramNames = IntStreamEx.of(stmtParamIndexes)
.mapToObj(i -> StreamEx.of(m.getParameterAnnotations()[i])
.select(Dao.Bind.class)
.first()
.orElseThrow(() -> new UnsupportedOperationException("In method: " + fullClassMethodName + ", parameters[" + i + "]: "
+ ClassUtil.getSimpleClassName(m.getParameterTypes()[i])
+ " is not binded with parameter named through annotation @Bind")))
.map(b -> b.value())
.toArray(IntFunctions.ofStringArray());
if (namedSql != null) {
final List diffParamNames = N.difference(namedSql.getNamedParameters(), N.asList(paramNames));
if (N.notNullOrEmpty(diffParamNames)) {
throw new UnsupportedOperationException("In method: " + fullClassMethodName
+ ", The named parameters in sql are different from the names binded by method parameters: " + diffParamNames);
}
}
parametersSetter = (preparedQuery, args) -> {
final NamedQuery namedQuery = ((NamedQuery) preparedQuery);
for (int i = 0; i < stmtParamLen; i++) {
namedQuery.setObject(paramNames[i], args[stmtParamIndexes[i]]);
}
};
} else {
if (stmtParamLen == paramLen) {
parametersSetter = (preparedQuery, args) -> preparedQuery.setParameters(args);
} else {
parametersSetter = (preparedQuery, args) -> {
for (int i = 0; i < stmtParamLen; i++) {
preparedQuery.setObject(i + 1, args[stmtParamIndexes[i]]);
}
};
}
}
}
return parametersSetter == null ? JdbcUtil.BiParametersSetter.DO_NOTHING : parametersSetter;
}
@SuppressWarnings("rawtypes")
private static AbstractPreparedQuery prepareQuery(final Dao proxy, final Method method, final Object[] args, final int[] defineParamIndexes,
final String[] defines, final boolean isNamedQuery, String query, ParsedSql namedSql, final int fetchSize, final boolean isBatch,
final int batchSize, final int queryTimeout, final boolean returnGeneratedKeys, final String[] returnColumnNames, final boolean isCall,
final List outParameterList) throws SQLException, Exception {
if (N.notNullOrEmpty(defines)) {
for (int i = 0, len = defines.length; i < len; i++) {
query = StringUtil.replaceAll(query, defines[i], N.stringOf(args[defineParamIndexes[i]]));
}
if (isNamedQuery) {
namedSql = ParsedSql.parse(query);
}
}
final AbstractPreparedQuery preparedQuery = isCall ? proxy.prepareCallableQuery(query)
: (isNamedQuery ? (returnGeneratedKeys ? proxy.prepareNamedQuery(namedSql, returnColumnNames) : proxy.prepareNamedQuery(namedSql))
: (returnGeneratedKeys ? proxy.prepareQuery(query, returnColumnNames) : proxy.prepareQuery(query)));
if (isCall && N.notNullOrEmpty(outParameterList)) {
final PreparedCallableQuery callableQuery = ((PreparedCallableQuery) preparedQuery);
for (OutParameter op : outParameterList) {
if (N.isNullOrEmpty(op.name())) {
callableQuery.registerOutParameter(op.position(), op.sqlType());
} else {
callableQuery.registerOutParameter(op.name(), op.sqlType());
}
}
}
if (fetchSize > 0) {
preparedQuery.setFetchSize(fetchSize);
}
if (queryTimeout >= 0) {
preparedQuery.setQueryTimeout(queryTimeout);
}
return preparedQuery;
}
private static String createCacheKey(final Method method, final String fullClassMethodName, final Object[] args, final Logger daoLogger) {
String cachekey = null;
if (kryoParser != null) {
try {
cachekey = kryoParser.serialize(N.toJSON(N.asMap(fullClassMethodName, args)));
} catch (Exception e) {
// ignore;
daoLogger.warn("Failed to generated cache key and not able cache the result for method: " + fullClassMethodName);
}
} else {
final List newArgs = Stream.of(args).map(it -> {
if (it == null) {
return "null";
}
final Type> type = N.typeOf(it.getClass());
if (type.isSerializable() || type.isCollection() || type.isMap() || type.isArray() || type.isEntity() || type.isEntityId()) {
return it;
} else {
return it.toString();
}
}).toList();
try {
cachekey = N.toJSON(N.asMap(fullClassMethodName, newArgs));
} catch (Exception e) {
// ignore;
daoLogger.warn("Failed to generated cache key and not able cache the result for method: " + fullClassMethodName);
}
}
return cachekey;
}
@SuppressWarnings({ "rawtypes" })
static > TD createDao(final Class daoInterface, final javax.sql.DataSource ds,
final SQLMapper sqlMapper, final Cache daoCache, final Executor executor) {
N.checkArgNotNull(daoInterface, "daoInterface");
N.checkArgNotNull(ds, "dataSource");
final String key = ClassUtil.getCanonicalClassName(daoInterface) + "_" + System.identityHashCode(ds) + "_"
+ (sqlMapper == null ? "null" : System.identityHashCode(sqlMapper)) + "_" + (executor == null ? "null" : System.identityHashCode(executor));
TD daoInstance = (TD) daoPool.get(key);
if (daoInstance != null) {
return daoInstance;
}
final Logger daoLogger = LoggerFactory.getLogger(daoInterface);
final javax.sql.DataSource primaryDataSource = ds;
final SQLMapper nonNullSQLMapper = sqlMapper == null ? new SQLMapper() : sqlMapper;
final Executor nonNullExecutor = executor == null ? JdbcUtil.asyncExecutor.getExecutor() : executor;
final boolean isUnchecked = JdbcUtil.UncheckedDao.class.isAssignableFrom(daoInterface);
final boolean isCrudDao = JdbcUtil.CrudDao.class.isAssignableFrom(daoInterface) || JdbcUtil.UncheckedCrudDao.class.isAssignableFrom(daoInterface);
final boolean isCrudDaoL = JdbcUtil.CrudDaoL.class.isAssignableFrom(daoInterface) || JdbcUtil.UncheckedCrudDaoL.class.isAssignableFrom(daoInterface);
java.lang.reflect.Type[] typeArguments = null;
if (N.notNullOrEmpty(daoInterface.getGenericInterfaces()) && daoInterface.getGenericInterfaces()[0] instanceof ParameterizedType) {
final ParameterizedType parameterizedType = (ParameterizedType) daoInterface.getGenericInterfaces()[0];
typeArguments = parameterizedType.getActualTypeArguments();
if (typeArguments.length >= 1 && typeArguments[0] instanceof Class) {
if (!ClassUtil.isEntity((Class) typeArguments[0])) {
throw new IllegalArgumentException(
"Entity Type parameter must be: Object.class or entity class with getter/setter methods. Can't be: " + typeArguments[0]);
}
}
if (typeArguments.length >= 2 && SQLBuilder.class.isAssignableFrom((Class) typeArguments[1])) {
if (!(typeArguments[1].equals(PSC.class) || typeArguments[1].equals(PAC.class) || typeArguments[1].equals(PLC.class))) {
throw new IllegalArgumentException("SQLBuilder Type parameter must be: SQLBuilder.PSC/PAC/PLC. Can't be: " + typeArguments[1]);
}
} else if (typeArguments.length >= 3 && SQLBuilder.class.isAssignableFrom((Class) typeArguments[2])) {
if (!(typeArguments[2].equals(PSC.class) || typeArguments[2].equals(PAC.class) || typeArguments[2].equals(PLC.class))) {
throw new IllegalArgumentException("SQLBuilder Type parameter must be: SQLBuilder.PSC/PAC/PLC. Can't be: " + typeArguments[2]);
}
}
if (isCrudDao) {
final List idFieldNames = ClassUtil.getIdFieldNames((Class) typeArguments[0]);
if (idFieldNames.size() == 0) {
throw new IllegalArgumentException("To support CRUD operations by extending CrudDao interface, the entity class: " + typeArguments[0]
+ " must have at least one field annotated with @Id");
} else if (idFieldNames.size() == 1 && !SQLBuilder.class.isAssignableFrom((Class) typeArguments[1])) {
if (!(Primitives.wrap((Class) typeArguments[1]))
.isAssignableFrom(Primitives.wrap(ClassUtil.getPropGetMethod((Class) typeArguments[0], idFieldNames.get(0)).getReturnType()))) {
throw new IllegalArgumentException("The 'ID' type declared in Dao type parameters: " + typeArguments[1]
+ " is not assignable from the id property type in the entity class: "
+ ClassUtil.getPropGetMethod((Class) typeArguments[0], idFieldNames.get(0)).getReturnType());
}
} else if (idFieldNames.size() > 1 && !(EntityId.class.equals(typeArguments[1]) || ClassUtil.isEntity((Class) typeArguments[1]))) {
throw new IllegalArgumentException("To support multiple ids, the 'ID' type type must be EntityId. It can't be: " + typeArguments[1]);
}
}
}
final Map> methodInvokerMap = new HashMap<>();
final List sqlMethods = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.reversed()
.distinct()
.flatMapp(clazz -> clazz.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.toList();
final Class entityClass = N.isNullOrEmpty(typeArguments) ? null : (Class) typeArguments[0];
final boolean isDirtyMarker = entityClass == null ? false : ClassUtil.isDirtyMarker(entityClass);
final Class> idClass = isCrudDao ? (isCrudDaoL ? Long.class : (Class) typeArguments[1]) : null;
final boolean isEntityId = idClass != null && EntityId.class.isAssignableFrom(idClass);
final Class extends SQLBuilder> sbc = N.isNullOrEmpty(typeArguments) ? PSC.class
: (typeArguments.length >= 2 && SQLBuilder.class.isAssignableFrom((Class) typeArguments[1]) ? (Class) typeArguments[1]
: (typeArguments.length >= 3 && SQLBuilder.class.isAssignableFrom((Class) typeArguments[2]) ? (Class) typeArguments[2] : PSC.class));
final NamingPolicy namingPolicy = sbc.equals(PSC.class) ? NamingPolicy.LOWER_CASE_WITH_UNDERSCORE
: (sbc.equals(PAC.class) ? NamingPolicy.UPPER_CASE_WITH_UNDERSCORE : NamingPolicy.LOWER_CAMEL_CASE);
final Function selectFromSQLBuilderFunc = sbc.equals(PSC.class) ? cond -> PSC.selectFrom(entityClass).append(cond).pair()
: (sbc.equals(PAC.class) ? cond -> PAC.selectFrom(entityClass).append(cond).pair() : cond -> PLC.selectFrom(entityClass).append(cond).pair());
final BiFunction singleQuerySQLBuilderFunc = sbc.equals(PSC.class)
? (selectPropName, cond) -> PSC.select(selectPropName).from(entityClass).append(cond).pair()
: (sbc.equals(PAC.class) ? (selectPropName, cond) -> PAC.select(selectPropName).from(entityClass).append(cond).pair()
: (selectPropName, cond) -> PLC.select(selectPropName).from(entityClass).append(cond).pair());
final BiFunction, Condition, SQLBuilder> selectSQLBuilderFunc = sbc.equals(PSC.class)
? ((selectPropNames, cond) -> N.isNullOrEmpty(selectPropNames) ? PSC.selectFrom(entityClass).append(cond)
: PSC.select(selectPropNames).from(entityClass).append(cond))
: (sbc.equals(PAC.class)
? ((selectPropNames, cond) -> N.isNullOrEmpty(selectPropNames) ? PAC.selectFrom(entityClass).append(cond)
: PAC.select(selectPropNames).from(entityClass).append(cond))
: (selectPropNames, cond) -> (N.isNullOrEmpty(selectPropNames) ? PLC.selectFrom(entityClass).append(cond)
: PLC.select(selectPropNames).from(entityClass).append(cond)));
final BiFunction, Condition, SQLBuilder> namedSelectSQLBuilderFunc = sbc.equals(PSC.class)
? ((selectPropNames, cond) -> N.isNullOrEmpty(selectPropNames) ? NSC.selectFrom(entityClass).append(cond)
: NSC.select(selectPropNames).from(entityClass).append(cond))
: (sbc.equals(PAC.class)
? ((selectPropNames, cond) -> N.isNullOrEmpty(selectPropNames) ? NAC.selectFrom(entityClass).append(cond)
: NAC.select(selectPropNames).from(entityClass).append(cond))
: (selectPropNames, cond) -> (N.isNullOrEmpty(selectPropNames) ? NLC.selectFrom(entityClass).append(cond)
: NLC.select(selectPropNames).from(entityClass).append(cond)));
final Function, SQLBuilder> namedInsertSQLBuilderFunc = sbc.equals(PSC.class)
? (propNamesToInsert -> N.isNullOrEmpty(propNamesToInsert) ? NSC.insertInto(entityClass) : NSC.insert(propNamesToInsert).into(entityClass))
: (sbc.equals(PAC.class)
? (propNamesToInsert -> N.isNullOrEmpty(propNamesToInsert) ? NAC.insertInto(entityClass)
: NAC.insert(propNamesToInsert).into(entityClass))
: (propNamesToInsert -> N.isNullOrEmpty(propNamesToInsert) ? NLC.insertInto(entityClass)
: NLC.insert(propNamesToInsert).into(entityClass)));
final Function, SQLBuilder> parameterizedUpdateFunc = sbc.equals(PSC.class) ? clazz -> PSC.update(clazz)
: (sbc.equals(PAC.class) ? clazz -> PAC.update(clazz) : clazz -> PLC.update(clazz));
final Function, SQLBuilder> parameterizedDeleteFromFunc = sbc.equals(PSC.class) ? clazz -> PSC.deleteFrom(clazz)
: (sbc.equals(PAC.class) ? clazz -> PAC.deleteFrom(clazz) : clazz -> PLC.deleteFrom(clazz));
final Function, SQLBuilder> namedUpdateFunc = sbc.equals(PSC.class) ? clazz -> NSC.update(clazz)
: (sbc.equals(PAC.class) ? clazz -> NAC.update(clazz) : clazz -> NLC.update(clazz));
final List idPropNameList = entityClass == null ? N.emptyList() : ClassUtil.getIdFieldNames(entityClass);
final boolean isNoId = entityClass == null || N.isNullOrEmpty(idPropNameList) || ClassUtil.isFakeId(idPropNameList);
final Set idPropNameSet = N.newHashSet(idPropNameList);
final String oneIdPropName = isNoId ? null : idPropNameList.get(0);
final EntityInfo entityInfo = entityClass == null ? null : ParserUtil.getEntityInfo(entityClass);
final PropInfo idPropInfo = isNoId ? null : entityInfo.getPropInfo(oneIdPropName);
final boolean isOneId = isNoId ? false : idPropNameList.size() == 1;
final Condition idCond = isNoId ? null : isOneId ? CF.eq(oneIdPropName) : CF.and(StreamEx.of(idPropNameList).map(CF::eq).toList());
String sql_getById = null;
String sql_existsById = null;
String sql_insertWithId = null;
String sql_insertWithoutId = null;
String sql_updateById = null;
String sql_deleteById = null;
if (sbc.equals(PSC.class)) {
sql_getById = isNoId ? null : NSC.selectFrom(entityClass).where(idCond).sql();
sql_existsById = isNoId ? null : NSC.select(SQLBuilder._1).from(entityClass).where(idCond).sql();
sql_insertWithId = entityClass == null ? null : NSC.insertInto(entityClass).sql();
sql_insertWithoutId = entityClass == null ? null
: (idPropNameSet.containsAll(ClassUtil.getPropNameList(entityClass)) ? sql_insertWithId : NSC.insertInto(entityClass, idPropNameSet).sql());
sql_updateById = isNoId ? null : NSC.update(entityClass, idPropNameSet).where(idCond).sql();
sql_deleteById = isNoId ? null : NSC.deleteFrom(entityClass).where(idCond).sql();
} else if (sbc.equals(PAC.class)) {
sql_getById = isNoId ? null : NAC.selectFrom(entityClass).where(idCond).sql();
sql_existsById = isNoId ? null : NAC.select(SQLBuilder._1).from(entityClass).where(idCond).sql();
sql_updateById = isNoId ? null : NAC.update(entityClass, idPropNameSet).where(idCond).sql();
sql_insertWithId = entityClass == null ? null : NAC.insertInto(entityClass).sql();
sql_insertWithoutId = entityClass == null ? null
: (idPropNameSet.containsAll(ClassUtil.getPropNameList(entityClass)) ? sql_insertWithId : NAC.insertInto(entityClass, idPropNameSet).sql());
sql_deleteById = isNoId ? null : NAC.deleteFrom(entityClass).where(idCond).sql();
} else {
sql_getById = isNoId ? null : NLC.selectFrom(entityClass).where(idCond).sql();
sql_existsById = isNoId ? null : NLC.select(SQLBuilder._1).from(entityClass).where(idCond).sql();
sql_insertWithId = entityClass == null ? null : NLC.insertInto(entityClass).sql();
sql_insertWithoutId = entityClass == null ? null
: (idPropNameSet.containsAll(ClassUtil.getPropNameList(entityClass)) ? sql_insertWithId : NLC.insertInto(entityClass, idPropNameSet).sql());
sql_updateById = isNoId ? null : NLC.update(entityClass, idPropNameSet).where(idCond).sql();
sql_deleteById = isNoId ? null : NLC.deleteFrom(entityClass).where(idCond).sql();
}
final ParsedSql namedGetByIdSQL = N.isNullOrEmpty(sql_getById) ? null : ParsedSql.parse(sql_getById);
final ParsedSql namedExistsByIdSQL = N.isNullOrEmpty(sql_existsById) ? null : ParsedSql.parse(sql_existsById);
final ParsedSql namedInsertWithIdSQL = N.isNullOrEmpty(sql_insertWithId) ? null : ParsedSql.parse(sql_insertWithId);
final ParsedSql namedInsertWithoutIdSQL = N.isNullOrEmpty(sql_insertWithoutId) ? null : ParsedSql.parse(sql_insertWithoutId);
final ParsedSql namedUpdateByIdSQL = N.isNullOrEmpty(sql_updateById) ? null : ParsedSql.parse(sql_updateById);
final ParsedSql namedDeleteByIdSQL = N.isNullOrEmpty(sql_deleteById) ? null : ParsedSql.parse(sql_deleteById);
final ImmutableMap propColumnNameMap = SQLBuilder.getPropColumnNameMap(entityClass, namingPolicy);
final String[] returnColumnNames = isNoId ? N.EMPTY_STRING_ARRAY
: (isOneId ? Array.of(propColumnNameMap.get(oneIdPropName))
: Stream.of(idPropNameList).map(idName -> propColumnNameMap.get(idName)).toArray(IntFunctions.ofStringArray()));
final Tuple3, Function, BiConsumer> tp3 = JdbcUtil.getIdGeneratorGetterSetter(daoInterface, entityClass,
namingPolicy, idClass);
final JdbcUtil.BiRowMapper keyExtractor = tp3._1;
final Function idGetter = tp3._2;
final BiConsumer idSetter = tp3._3;
final EntityInfo idEntityInfo = idClass != null && ClassUtil.isEntity(idClass) ? ParserUtil.getEntityInfo(idClass) : null;
final Predicate isDefaultIdTester = isNoId ? id -> true
: (isOneId ? id -> JdbcUtil.isDefaultIdPropValue(id)
: (isEntityId ? id -> Stream.of(((EntityId) id).entrySet()).allMatch(it -> JdbcUtil.isDefaultIdPropValue(it.getValue()))
: id -> Stream.of(idPropNameList).allMatch(idName -> JdbcUtil.isDefaultIdPropValue(idEntityInfo.getPropValue(id, idName)))));
final JdbcUtil.BiParametersSetter idParamSetter = isOneId ? (pq, id) -> pq.setObject(oneIdPropName, id, idPropInfo.dbType)
: (isEntityId ? (pq, id) -> {
final EntityId entityId = (EntityId) id;
PropInfo propInfo = null;
for (String idName : idPropNameList) {
propInfo = entityInfo.getPropInfo(idName);
pq.setObject(idName, entityId.get(idName), propInfo.dbType);
}
} : (pq, id) -> {
PropInfo propInfo = null;
for (String idName : idPropNameList) {
propInfo = idEntityInfo.getPropInfo(idName);
pq.setObject(idName, propInfo.getPropValue(id), propInfo.dbType);
}
});
final JdbcUtil.BiParametersSetter idParamSetterByEntity = isOneId
? (pq, entity) -> pq.setObject(oneIdPropName, idPropInfo.getPropValue(entity), idPropInfo.dbType)
: (pq, entity) -> pq.setParameters(entity);
final Dao.SqlLogEnabled daoClassSqlLogAnno = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.flatMapp(cls -> cls.getAnnotations())
.select(Dao.SqlLogEnabled.class)
.first()
.orNull();
final Dao.PerfLog daoClassPerfLogAnno = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.flatMapp(cls -> cls.getAnnotations())
.select(Dao.PerfLog.class)
.first()
.orNull();
final Dao.CacheResult daoClassCacheResultAnno = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.flatMapp(cls -> cls.getAnnotations())
.select(Dao.CacheResult.class)
.first()
.orNull();
final Dao.RefreshCache daoClassRefreshCacheAnno = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.flatMapp(cls -> cls.getAnnotations())
.select(Dao.RefreshCache.class)
.first()
.orNull();
final MutableBoolean hasCacheResult = MutableBoolean.of(false);
final MutableBoolean hasRefreshCache = MutableBoolean.of(false);
final List daoClassHandlerList = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.reversed()
.flatMapp(cls -> cls.getAnnotations())
.filter(anno -> anno.annotationType().equals(Dao.Handler.class) || anno.annotationType().equals(DaoUtil.HandlerList.class))
.flattMap(
anno -> anno.annotationType().equals(Dao.Handler.class) ? N.asList((Dao.Handler) anno) : N.asList(((DaoUtil.HandlerList) anno).value()))
.toList();
final Dao.Cache daoClassCacheAnno = StreamEx.of(ClassUtil.getAllInterfaces(daoInterface))
.prepend(daoInterface)
.flatMapp(cls -> cls.getAnnotations())
.select(Dao.Cache.class)
.first()
.orNull();
final int capacity = daoClassCacheAnno == null ? 1000 : daoClassCacheAnno.capacity();
final long evictDelay = daoClassCacheAnno == null ? 3000 : daoClassCacheAnno.evictDelay();
final Cache cache = daoCache == null ? CacheFactory.createLocalCache(capacity, evictDelay) : daoCache;
final Set nonDBOperationSet = new HashSet<>();
final Map sqlCache = new ConcurrentHashMap<>(0);
final Map> sqlsCache = new ConcurrentHashMap<>(0);
for (Method m : sqlMethods) {
if (!Modifier.isPublic(m.getModifiers())) {
continue;
}
final Class> declaringClass = m.getDeclaringClass();
final String methodName = m.getName();
final String simpleClassMethodName = declaringClass.getSimpleName() + "." + methodName;
final String fullClassMethodName = ClassUtil.getCanonicalClassName(declaringClass) + "." + methodName;
final Class>[] paramTypes = m.getParameterTypes();
final Class> returnType = m.getReturnType();
final int paramLen = paramTypes.length;
final Dao.Sqls sqlsAnno = StreamEx.of(m.getAnnotations()).select(Dao.Sqls.class).onlyOne().orNull();
List sqlList = null;
if (sqlsAnno != null) {
if (Modifier.isAbstract(m.getModifiers())) {
throw new UnsupportedOperationException(
"Annotation @Sqls is only supported by interface methods with default implementation: default xxx dbOperationABC(someParameters, String ... sqls), not supported by abstract method: "
+ fullClassMethodName);
}
if (paramLen == 0 || !paramTypes[paramLen - 1].equals(String[].class)) {
throw new UnsupportedOperationException(
"To support sqls binding by @Sqls, the type of last parameter must be: String... sqls. It can't be : " + paramTypes[paramLen - 1]
+ " on method: " + fullClassMethodName);
}
if (sqlMapper == null) {
sqlList = Stream.of(sqlsAnno.value()).filter(Fn.notNullOrEmpty()).toList();
} else {
sqlList = Stream.of(sqlsAnno.value())
.map(it -> sqlMapper.get(it) == null ? it : sqlMapper.get(it).sql())
.filter(Fn.notNullOrEmpty())
.toList();
}
}
final String[] sqls = sqlList == null ? N.EMPTY_STRING_ARRAY : sqlList.toArray(new String[sqlList.size()]);
Throwables.BiFunction call = null;
if (!Modifier.isAbstract(m.getModifiers())) {
final MethodHandle methodHandle = createMethodHandle(m);
call = (proxy, args) -> {
if (sqlsAnno != null) {
if (N.notNullOrEmpty((String[]) args[paramLen - 1])) {
throw new IllegalArgumentException(
"The last parameter(String[]) of method annotated by @Sqls must be null, don't specify it. It will auto-filled by sqls from annotation @Sqls on the method: "
+ fullClassMethodName);
}
args[paramLen - 1] = sqls;
}
return methodHandle.bindTo(proxy).invokeWithArguments(args);
};
} else if (methodName.equals("executor") && Executor.class.isAssignableFrom(returnType) && paramLen == 0) {
call = (proxy, args) -> nonNullExecutor;
} else if (m.getName().equals("targetEntityClass") && Class.class.isAssignableFrom(returnType) && paramLen == 0) {
call = (proxy, args) -> entityClass;
} else if (m.getName().equals("targetDaoInterface") && Class.class.isAssignableFrom(returnType) && paramLen == 0) {
call = (proxy, args) -> daoInterface;
} else if (methodName.equals("dataSource") && javax.sql.DataSource.class.isAssignableFrom(returnType) && paramLen == 0) {
call = (proxy, args) -> primaryDataSource;
} else if (methodName.equals("sqlMapper") && SQLMapper.class.isAssignableFrom(returnType) && paramLen == 0) {
call = (proxy, args) -> nonNullSQLMapper;
} else if (methodName.equals("cacheSql") && void.class.isAssignableFrom(returnType) && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(String.class)) {
call = (proxy, args) -> {
sqlCache.put(N.checkArgNotNullOrEmpty((String) args[0], "key"), N.checkArgNotNullOrEmpty((String) args[1], "sql"));
return null;
};
} else if (methodName.equals("cacheSqls") && void.class.isAssignableFrom(returnType) && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Collection.class)) {
call = (proxy, args) -> {
sqlsCache.put(N.checkArgNotNullOrEmpty((String) args[0], "key"),
ImmutableList.copyOf(N.checkArgNotNullOrEmpty((Collection) args[1], "sqls")));
return null;
};
} else if (methodName.equals("getCachedSql") && String.class.isAssignableFrom(returnType) && paramLen == 1 && paramTypes[0].equals(String.class)) {
call = (proxy, args) -> sqlCache.get(args[0]);
} else if (methodName.equals("getCachedSqls") && ImmutableList.class.isAssignableFrom(returnType) && paramLen == 1
&& paramTypes[0].equals(String.class)) {
call = (proxy, args) -> sqlsCache.get(args[0]);
} else {
final boolean isStreamReturn = Stream.class.isAssignableFrom(returnType) || ExceptionalStream.class.isAssignableFrom(returnType);
final boolean throwsSQLException = StreamEx.of(m.getExceptionTypes()).anyMatch(e -> SQLException.class.equals(e));
final Annotation sqlAnno = StreamEx.of(m.getAnnotations()).filter(anno -> sqlAnnoMap.containsKey(anno.annotationType())).first().orNull();
if (declaringClass.equals(JdbcUtil.Dao.class) || declaringClass.equals(JdbcUtil.UncheckedDao.class)) {
if (methodName.equals("save") && paramLen == 1) {
call = (proxy, args) -> {
final Object entity = args[0];
if (entity instanceof DirtyMarker) {
final Collection propNamesToSave = SQLBuilder.getInsertPropNames(entity, null);
N.checkArgNotNullOrEmpty(propNamesToSave, "propNamesToSave");
final String namedInsertSQL = namedInsertSQLBuilderFunc.apply(propNamesToSave).sql();
proxy.prepareNamedQuery(namedInsertSQL).setParameters(entity).update();
DirtyMarkerUtil.markDirty((DirtyMarker) entity, false);
} else {
final ParsedSql namedInsertSQL = isNoId || isDefaultIdTester.test(idGetter.apply(entity)) ? namedInsertWithoutIdSQL
: namedInsertWithIdSQL;
proxy.prepareNamedQuery(namedInsertSQL).setParameters(entity).update();
}
return null;
};
} else if (methodName.equals("save") && paramLen == 2 && Collection.class.isAssignableFrom(paramTypes[1])) {
call = (proxy, args) -> {
final Object entity = args[0];
final Collection propNamesToSave = (Collection) args[1];
N.checkArgNotNullOrEmpty(propNamesToSave, "propNamesToSave");
final String namedInsertSQL = namedInsertSQLBuilderFunc.apply(propNamesToSave).sql();
proxy.prepareNamedQuery(namedInsertSQL).setParameters(entity).update();
if (entity instanceof DirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, propNamesToSave, false);
}
return null;
};
} else if (methodName.equals("save") && paramLen == 2 && String.class.equals(paramTypes[0])) {
call = (proxy, args) -> {
final String namedInsertSQL = (String) args[0];
final Object entity = args[1];
proxy.prepareNamedQuery(namedInsertSQL).setParameters(entity).update();
if (entity instanceof DirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, false);
}
return null;
};
} else if (methodName.equals("batchSave") && paramLen == 2 && Collection.class.isAssignableFrom(paramTypes[0])
&& int.class.equals(paramTypes[1])) {
call = (proxy, args) -> {
final Collection> entities = (Collection) args[0];
final int batchSize = (Integer) args[1];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
final ParsedSql namedInsertSQL = isNoId || isDefaultIdTester.test(idGetter.apply(N.firstOrNullIfEmpty(entities)))
? namedInsertWithoutIdSQL
: namedInsertWithIdSQL;
if (entities.size() <= batchSize) {
proxy.prepareNamedQuery(namedInsertSQL).addBatchParameters(entities).batchUpdate();
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL).closeAfterExecution(false)) {
ExceptionalStream.of(entities)
.splitToList(batchSize) //
.forEach(bp -> nameQuery.addBatchParameters(bp).batchUpdate());
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
return null;
};
} else if (methodName.equals("batchSave") && paramLen == 3 && Collection.class.isAssignableFrom(paramTypes[0])
&& Collection.class.isAssignableFrom(paramTypes[1]) && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final Collection> entities = (Collection) args[0];
final Collection propNamesToSave = (Collection) args[1];
N.checkArgNotNullOrEmpty(propNamesToSave, "propNamesToSave");
final int batchSize = (Integer) args[2];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
final String namedInsertSQL = namedInsertSQLBuilderFunc.apply(propNamesToSave).sql();
if (entities.size() <= batchSize) {
proxy.prepareNamedQuery(namedInsertSQL).addBatchParameters(entities).batchUpdate();
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL).closeAfterExecution(false)) {
ExceptionalStream.of(entities)
.splitToList(batchSize) //
.forEach(bp -> nameQuery.addBatchParameters(bp).batchUpdate());
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
return null;
};
} else if (methodName.equals("batchSave") && paramLen == 3 && String.class.equals(paramTypes[0])
&& Collection.class.isAssignableFrom(paramTypes[1]) && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final String namedInsertSQL = (String) args[0];
final Collection> entities = (Collection) args[1];
final int batchSize = (Integer) args[2];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
if (entities.size() <= batchSize) {
proxy.prepareNamedQuery(namedInsertSQL).addBatchParameters(entities).batchUpdate();
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL).closeAfterExecution(false)) {
ExceptionalStream.of(entities)
.splitToList(batchSize) //
.forEach(bp -> nameQuery.addBatchParameters(bp).batchUpdate());
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
return null;
};
} else if (methodName.equals("exists") && paramLen == 1 && Condition.class.isAssignableFrom(paramTypes[0])) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply(SQLBuilder._1, (Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).exists();
};
} else if (methodName.equals("count") && paramLen == 1 && Condition.class.isAssignableFrom(paramTypes[0])) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply(SQLBuilder.COUNT_ALL, (Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForInt().orZero();
};
} else if (methodName.equals("findFirst") && paramLen == 1 && paramTypes[0].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst(entityClass);
};
} else if (methodName.equals("findFirst") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst((JdbcUtil.RowMapper) args[1]);
};
} else if (methodName.equals("findFirst") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst((JdbcUtil.BiRowMapper) args[1]);
};
} else if (methodName.equals("findFirst") && paramLen == 2 && paramTypes[0].equals(Collection.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst(entityClass);
};
} else if (methodName.equals("findFirst") && paramLen == 3 && paramTypes[0].equals(Collection.class)
&& paramTypes[1].equals(Condition.class) && paramTypes[2].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst((JdbcUtil.RowMapper) args[2]);
};
} else if (methodName.equals("findFirst") && paramLen == 3 && paramTypes[0].equals(Collection.class)
&& paramTypes[1].equals(Condition.class) && paramTypes[2].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).findFirst((JdbcUtil.BiRowMapper) args[2]);
};
} else if (methodName.equals("queryForBoolean") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForBoolean();
};
} else if (methodName.equals("queryForChar") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForChar();
};
} else if (methodName.equals("queryForByte") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForByte();
};
} else if (methodName.equals("queryForShort") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForShort();
};
} else if (methodName.equals("queryForInt") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForInt();
};
} else if (methodName.equals("queryForLong") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForLong();
};
} else if (methodName.equals("queryForFloat") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForFloat();
};
} else if (methodName.equals("queryForDouble") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForDouble();
};
} else if (methodName.equals("queryForString") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForString();
};
} else if (methodName.equals("queryForDate") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForDate();
};
} else if (methodName.equals("queryForTime") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForTime();
};
} else if (methodName.equals("queryForTimestamp") && paramLen == 2 && paramTypes[0].equals(String.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[0], (Condition) args[1]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForTimestamp();
};
} else if (methodName.equals("queryForSingleResult") && paramLen == 3 && paramTypes[0].equals(Class.class)
&& paramTypes[1].equals(String.class) && paramTypes[2].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[1], (Condition) args[2]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForSingleResult((Class) args[0]);
};
} else if (methodName.equals("queryForSingleNonNull") && paramLen == 3 && paramTypes[0].equals(Class.class)
&& paramTypes[1].equals(String.class) && paramTypes[2].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[1], (Condition) args[2]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForSingleNonNull((Class) args[0]);
};
} else if (methodName.equals("queryForUniqueResult") && paramLen == 3 && paramTypes[0].equals(Class.class)
&& paramTypes[1].equals(String.class) && paramTypes[2].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[1], (Condition) args[2]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForUniqueResult((Class) args[0]);
};
} else if (methodName.equals("queryForUniqueNonNull") && paramLen == 3 && paramTypes[0].equals(Class.class)
&& paramTypes[1].equals(String.class) && paramTypes[2].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = singleQuerySQLBuilderFunc.apply((String) args[1], (Condition) args[2]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).queryForUniqueNonNull((Class) args[0]);
};
} else if (methodName.equals("query") && paramLen == 1 && paramTypes[0].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query();
};
} else if (methodName.equals("query") && paramLen == 2 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query();
};
} else if (methodName.equals("query") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.ResultExtractor.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query((JdbcUtil.ResultExtractor) args[1]);
};
} else if (methodName.equals("query") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.ResultExtractor.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query((JdbcUtil.ResultExtractor) args[2]);
};
} else if (methodName.equals("query") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiResultExtractor.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query((JdbcUtil.BiResultExtractor) args[1]);
};
} else if (methodName.equals("query") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.BiResultExtractor.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).query((JdbcUtil.BiResultExtractor) args[2]);
};
} else if (methodName.equals("list") && paramLen == 1 && paramTypes[0].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list(entityClass);
};
} else if (methodName.equals("list") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.RowMapper) args[1]);
};
} else if (methodName.equals("list") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.BiRowMapper) args[1]);
};
} else if (methodName.equals("list") && paramLen == 3 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.RowFilter.class) && paramTypes[2].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.RowFilter) args[1], (JdbcUtil.RowMapper) args[2]);
};
} else if (methodName.equals("list") && paramLen == 3 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiRowFilter.class) && paramTypes[2].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.BiRowFilter) args[1], (JdbcUtil.BiRowMapper) args[2]);
};
} else if (methodName.equals("list") && paramLen == 2 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list(entityClass);
};
} else if (methodName.equals("list") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.RowMapper) args[2]);
};
} else if (methodName.equals("list") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.BiRowMapper) args[2]);
};
} else if (methodName.equals("list") && paramLen == 4 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.RowFilter.class) && paramTypes[3].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.RowFilter) args[2], (JdbcUtil.RowMapper) args[3]);
};
} else if (methodName.equals("list") && paramLen == 4 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.BiRowFilter.class) && paramTypes[3].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).list((JdbcUtil.BiRowFilter) args[2], (JdbcUtil.BiRowMapper) args[3]);
};
} else if (methodName.equals("stream") && paramLen == 1 && paramTypes[0].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream(entityClass);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.RowMapper) args[1]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 2 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.BiRowMapper) args[1]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 3 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.RowFilter.class) && paramTypes[2].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.RowFilter) args[1], (JdbcUtil.RowMapper) args[2]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 3 && paramTypes[0].equals(Condition.class)
&& paramTypes[1].equals(JdbcUtil.BiRowFilter.class) && paramTypes[2].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectFromSQLBuilderFunc.apply((Condition) args[0]);
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.BiRowFilter) args[1], (JdbcUtil.BiRowMapper) args[2]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 2 && paramTypes[0].equals(Collection.class)
&& paramTypes[1].equals(Condition.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream(entityClass);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.RowMapper) args[2]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 3 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.BiRowMapper) args[2]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 4 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.RowFilter.class) && paramTypes[3].equals(JdbcUtil.RowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.RowFilter) args[2], (JdbcUtil.RowMapper) args[3]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("stream") && paramLen == 4 && paramTypes[0].equals(Collection.class) && paramTypes[1].equals(Condition.class)
&& paramTypes[2].equals(JdbcUtil.BiRowFilter.class) && paramTypes[3].equals(JdbcUtil.BiRowMapper.class)) {
call = (proxy, args) -> {
final SP sp = selectSQLBuilderFunc.apply((Collection) args[0], (Condition) args[1]).pair();
final Throwables.Supplier supplier = () -> proxy.prepareQuery(sp.sql)
.setParameters(sp.parameters)
.stream((JdbcUtil.BiRowFilter) args[2], (JdbcUtil.BiRowMapper) args[3]);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else if (methodName.equals("update") && paramLen == 2 && Map.class.equals(paramTypes[0])
&& Condition.class.isAssignableFrom(paramTypes[1])) {
call = (proxy, args) -> {
final Map props = (Map) args[0];
final Condition cond = (Condition) args[1];
N.checkArgNotNullOrEmpty(props, "updateProps");
final SP sp = parameterizedUpdateFunc.apply(entityClass).set(props).append(cond).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).update();
};
} else if (methodName.equals("delete") && paramLen == 1 && Condition.class.isAssignableFrom(paramTypes[0])) {
call = (proxy, args) -> {
final SP sp = parameterizedDeleteFromFunc.apply(entityClass).append((Condition) args[0]).pair();
return proxy.prepareQuery(sp.sql).setParameters(sp.parameters).update();
};
} else {
call = (proxy, args) -> {
throw new UnsupportedOperationException("Unsupported operation: " + m);
};
}
} else if (declaringClass.equals(JdbcUtil.CrudDao.class) || declaringClass.equals(JdbcUtil.UncheckedCrudDao.class)) {
if (methodName.equals("insert") && paramLen == 1) {
call = (proxy, args) -> {
final Object entity = args[0];
ParsedSql namedInsertSQL = null;
if (isDirtyMarker) {
final Collection propNamesToInsert = SQLBuilder.getInsertPropNames(entity, null);
N.checkArgNotNullOrEmpty(propNamesToInsert, "propNamesToInsert");
namedInsertSQL = ParsedSql.parse(namedInsertSQLBuilderFunc.apply(propNamesToInsert).sql());
} else {
if (isDefaultIdTester.test(idGetter.apply(entity))) {
namedInsertSQL = namedInsertWithoutIdSQL;
} else {
namedInsertSQL = namedInsertWithIdSQL;
}
}
Object newId = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames)
.setParameters(entity)
.insert(keyExtractor)
.ifPresent(ret -> idSetter.accept(ret, entity))
.orElse(idGetter.apply(entity));
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, false);
}
return newId;
};
} else if (methodName.equals("insert") && paramLen == 2 && Collection.class.isAssignableFrom(paramTypes[1])) {
call = (proxy, args) -> {
final Object entity = args[0];
final Collection propNamesToInsert = (Collection) args[1];
N.checkArgNotNullOrEmpty(propNamesToInsert, "propNamesToInsert");
final String namedInsertSQL = namedInsertSQLBuilderFunc.apply(propNamesToInsert).sql();
final Object id = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames)
.setParameters(entity)
.insert(keyExtractor)
.ifPresent(ret -> idSetter.accept(ret, entity))
.orElse(idGetter.apply(entity));
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, propNamesToInsert, false);
}
return id;
};
} else if (methodName.equals("insert") && paramLen == 2 && String.class.equals(paramTypes[0])) {
call = (proxy, args) -> {
final String namedInsertSQL = (String) args[0];
final Object entity = args[1];
final Object id = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames)
.setParameters(entity)
.insert(keyExtractor)
.ifPresent(ret -> idSetter.accept(ret, entity))
.orElse(idGetter.apply(entity));
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, false);
}
return id;
};
} else if (methodName.equals("batchInsert") && paramLen == 2 && Collection.class.isAssignableFrom(paramTypes[0])
&& int.class.equals(paramTypes[1])) {
call = (proxy, args) -> {
final Collection> entities = (Collection) args[0];
final int batchSize = (Integer) args[1];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
final boolean isDefaultIdPropValue = isDefaultIdTester.test(idGetter.apply(N.firstOrNullIfEmpty(entities)));
final ParsedSql namedInsertSQL = isDefaultIdPropValue ? namedInsertWithoutIdSQL : namedInsertWithIdSQL;
List ids = null;
if (entities.size() <= batchSize) {
ids = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).addBatchParameters(entities).batchInsert(keyExtractor);
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).closeAfterExecution(false)) {
ids = ExceptionalStream.of(entities)
.splitToList(batchSize)
.flattMap(bp -> nameQuery.addBatchParameters(bp).batchInsert(keyExtractor))
.toList();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (N.notNullOrEmpty(ids) && Stream.of(ids).allMatch(Fn.isNull())) {
ids = new ArrayList<>();
}
if (N.notNullOrEmpty(ids) && N.notNullOrEmpty(entities) && ids.size() == N.size(entities)) {
int idx = 0;
for (Object e : entities) {
idSetter.accept(ids.get(idx++), e);
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
if (N.notNullOrEmpty(ids) && ids.size() != entities.size()) {
if (daoLogger.isWarnEnabled()) {
daoLogger.warn("The size of returned id list: {} is different from the size of input entity list: {}", ids.size(),
entities.size());
}
}
if (N.isNullOrEmpty(ids)) {
ids = Stream.of(entities).map(idGetter).toList();
}
return ids;
};
} else if (methodName.equals("batchInsert") && paramLen == 3 && Collection.class.isAssignableFrom(paramTypes[0])
&& Collection.class.isAssignableFrom(paramTypes[1]) && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final Collection> entities = (Collection) args[0];
final Collection propNamesToInsert = (Collection) args[1];
N.checkArgNotNullOrEmpty(propNamesToInsert, "propNamesToInsert");
final int batchSize = (Integer) args[1];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
final String namedInsertSQL = namedInsertSQLBuilderFunc.apply(propNamesToInsert).sql();
List ids = null;
if (entities.size() <= batchSize) {
ids = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).addBatchParameters(entities).batchInsert(keyExtractor);
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).closeAfterExecution(false)) {
ids = ExceptionalStream.of(entities)
.splitToList(batchSize)
.flattMap(bp -> nameQuery.addBatchParameters(bp).batchInsert(keyExtractor))
.toList();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (N.notNullOrEmpty(ids) && Stream.of(ids).allMatch(Fn.isNull())) {
ids = new ArrayList<>();
}
if (N.notNullOrEmpty(ids) && N.notNullOrEmpty(entities) && ids.size() == N.size(entities)) {
int idx = 0;
for (Object e : entities) {
idSetter.accept(ids.get(idx++), e);
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
if (N.notNullOrEmpty(ids) && ids.size() != entities.size()) {
if (daoLogger.isWarnEnabled()) {
daoLogger.warn("The size of returned id list: {} is different from the size of input entity list: {}", ids.size(),
entities.size());
}
}
if (N.isNullOrEmpty(ids)) {
ids = Stream.of(entities).map(idGetter).toList();
}
return ids;
};
} else if (methodName.equals("batchInsert") && paramLen == 3 && String.class.equals(paramTypes[0])
&& Collection.class.isAssignableFrom(paramTypes[1]) && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final String namedInsertSQL = (String) args[0];
final Collection> entities = (Collection) args[1];
final int batchSize = (Integer) args[2];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
List ids = null;
if (entities.size() <= batchSize) {
ids = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).addBatchParameters(entities).batchInsert(keyExtractor);
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedInsertSQL, returnColumnNames).closeAfterExecution(false)) {
ids = ExceptionalStream.of(entities)
.splitToList(batchSize)
.flattMap(bp -> nameQuery.addBatchParameters(bp).batchInsert(keyExtractor))
.toList();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (N.notNullOrEmpty(ids) && Stream.of(ids).allMatch(Fn.isNull())) {
ids = new ArrayList<>();
}
if (N.notNullOrEmpty(ids) && N.notNullOrEmpty(entities) && ids.size() == N.size(entities)) {
int idx = 0;
for (Object e : entities) {
idSetter.accept(ids.get(idx++), e);
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
if (N.notNullOrEmpty(ids) && ids.size() != entities.size()) {
if (daoLogger.isWarnEnabled()) {
daoLogger.warn("The size of returned id list: {} is different from the size of input entity list: {}", ids.size(),
entities.size());
}
}
if (N.isNullOrEmpty(ids)) {
ids = Stream.of(entities).map(idGetter).toList();
}
return ids;
};
} else if (methodName.equals("gett")) {
if (paramLen == 1) {
call = (proxy, args) -> proxy.prepareNamedQuery(namedGetByIdSQL).settParameters(args[0], idParamSetter).gett(entityClass);
} else {
call = (proxy, args) -> {
final Collection selectPropNames = (Collection) args[1];
if (N.isNullOrEmpty(selectPropNames)) {
return proxy.prepareNamedQuery(namedGetByIdSQL).settParameters(args[0], idParamSetter).gett(entityClass);
} else {
return proxy.prepareNamedQuery(namedSelectSQLBuilderFunc.apply(selectPropNames, idCond).sql())
.settParameters(args[0], idParamSetter)
.gett(entityClass);
}
};
}
} else if (methodName.equals("batchGet") && paramLen == 3 && Collection.class.equals(paramTypes[0])
&& Collection.class.equals(paramTypes[1]) && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final Collection ids = (Collection) args[0];
final Collection selectPropNames = (Collection) args[1];
final int batchSize = (Integer) args[2];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(ids)) {
return new ArrayList<>();
}
final Object firstId = N.first(ids).get();
final boolean isMap = firstId instanceof Map;
final boolean isEntity = firstId != null && ClassUtil.isEntity(firstId.getClass());
N.checkArgument(idPropNameList.size() > 1 || !(isEntity || isMap || isEntityId),
"Input 'ids' can not be EntityIds/Maps or entities for single id ");
final List idList = ids instanceof List ? (List) ids : new ArrayList(ids);
final List resultList = new ArrayList<>(idList.size());
if (idPropNameList.size() == 1) {
String sql_selectPart = selectSQLBuilderFunc.apply(selectPropNames, idCond).sql();
sql_selectPart = sql_selectPart.substring(0, sql_selectPart.lastIndexOf('=')) + "IN ";
if (ids.size() >= batchSize) {
final Joiner joiner = Joiner.with(", ", "(", ")").reuseCachedBuffer(true);
for (int i = 0; i < batchSize; i++) {
joiner.append('?');
}
final String qery = sql_selectPart + joiner.toString();
try (JdbcUtil.PreparedQuery preparedQuery = proxy.prepareQuery(qery).closeAfterExecution(false)) {
for (int i = 0, to = ids.size() - batchSize; i <= to; i += batchSize) {
resultList.addAll(preparedQuery.setParameters(idList.subList(i, i + batchSize)).list(entityClass));
}
}
}
if (ids.size() % batchSize != 0) {
final int remaining = ids.size() % batchSize;
final Joiner joiner = Joiner.with(", ", "(", ")").reuseCachedBuffer(true);
for (int i = 0; i < remaining; i++) {
joiner.append('?');
}
final String qery = sql_selectPart + joiner.toString();
resultList.addAll(
proxy.prepareQuery(qery).setParameters(idList.subList(ids.size() - remaining, ids.size())).list(entityClass));
}
} else {
if (ids.size() >= batchSize) {
for (int i = 0, to = ids.size() - batchSize; i <= to; i += batchSize) {
if (isEntityId) {
resultList.addAll(proxy.list(CF.id2Cond(idList.subList(i, i + batchSize))));
} else if (isMap) {
resultList.addAll(proxy.list(CF.eqAndOr(idList.subList(i, i + batchSize))));
} else {
resultList.addAll(proxy.list(CF.eqAndOr(idList.subList(i, i + batchSize), idPropNameList)));
}
}
}
if (ids.size() % batchSize != 0) {
final int remaining = ids.size() % batchSize;
if (isEntityId) {
resultList.addAll(proxy.list(CF.id2Cond(idList.subList(idList.size() - remaining, ids.size()))));
} else if (isMap) {
resultList.addAll(proxy.list(CF.eqAndOr(idList.subList(ids.size() - remaining, ids.size()))));
} else {
resultList.addAll(proxy.list(CF.eqAndOr(idList.subList(ids.size() - remaining, ids.size()), idPropNameList)));
}
}
}
if (resultList.size() > ids.size()) {
throw new DuplicatedResultException(
"The size of result: " + resultList.size() + " is bigger than the size of input ids: " + ids.size());
}
return resultList;
};
} else if (methodName.equals("exists") && paramLen == 1 && !Condition.class.isAssignableFrom(paramTypes[0])) {
call = (proxy, args) -> proxy.prepareNamedQuery(namedExistsByIdSQL).settParameters(args[0], idParamSetter).exists();
} else if (methodName.equals("update") && paramLen == 1) {
if (isDirtyMarker) {
call = (proxy, args) -> {
final DirtyMarker entity = (DirtyMarker) args[0];
final String query = namedUpdateFunc.apply(entityClass).set(entity, idPropNameSet).where(idCond).sql();
final int result = proxy.prepareNamedQuery(query).setParameters(entity).update();
DirtyMarkerUtil.markDirty(entity, false);
return result;
};
} else {
call = (proxy, args) -> {
return proxy.prepareNamedQuery(namedUpdateByIdSQL).setParameters(args[0]).update();
};
}
} else if (methodName.equals("update") && paramLen == 2 && !Map.class.equals(paramTypes[0]) && Collection.class.equals(paramTypes[1])) {
call = (proxy, args) -> {
final Collection propNamesToUpdate = (Collection) args[1];
N.checkArgNotNullOrEmpty(propNamesToUpdate, "propNamesToUpdate");
final String query = namedUpdateFunc.apply(entityClass).set(propNamesToUpdate).where(idCond).sql();
final int result = proxy.prepareNamedQuery(query).setParameters(args[0]).update();
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) args[0], propNamesToUpdate, false);
}
return result;
};
} else if (methodName.equals("update") && paramLen == 2 && Map.class.equals(paramTypes[0])) {
call = (proxy, args) -> {
final Map props = (Map) args[0];
final Object id = args[1];
N.checkArgNotNullOrEmpty(props, "updateProps");
final String query = namedUpdateFunc.apply(entityClass).set(props.keySet()).where(idCond).sql();
return proxy.prepareNamedQuery(query).setParameters(props).settParameters(id, idParamSetter).update();
};
} else if (methodName.equals("batchUpdate") && paramLen == 2 && int.class.equals(paramTypes[1])) {
call = (proxy, args) -> {
final Collection entities = (Collection) args[0];
final int batchSize = (Integer) args[1];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(entities)) {
return 0;
}
long result = 0;
if (entities.size() <= batchSize) {
result = N.sum(proxy.prepareNamedQuery(namedUpdateByIdSQL).addBatchParameters(entities).batchUpdate());
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedUpdateByIdSQL).closeAfterExecution(false)) {
result = ExceptionalStream.of(entities)
.splitToList(batchSize) //
.sumInt(bp -> N.sum(nameQuery.addBatchParameters(bp).batchUpdate()))
.orZero();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
return N.toIntExact(result);
};
} else if (methodName.equals("batchUpdate") && paramLen == 3 && int.class.equals(paramTypes[2])) {
call = (proxy, args) -> {
final Collection entities = (Collection) args[0];
final Collection propNamesToUpdate = (Collection) args[1];
final int batchSize = (Integer) args[2];
N.checkArgPositive(batchSize, "batchSize");
N.checkArgNotNullOrEmpty(propNamesToUpdate, "propNamesToUpdate");
if (N.isNullOrEmpty(entities)) {
return 0;
}
final String query = namedUpdateFunc.apply(entityClass).set(propNamesToUpdate).where(idCond).sql();
long result = 0;
if (entities.size() <= batchSize) {
result = N.sum(proxy.prepareNamedQuery(query).addBatchParameters(entities).batchUpdate());
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(query).closeAfterExecution(false)) {
result = ExceptionalStream.of(entities)
.splitToList(batchSize) //
.sumInt(bp -> N.sum(nameQuery.addBatchParameters(bp).batchUpdate()))
.orZero();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (isDirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, propNamesToUpdate, false);
}
}
return N.toIntExact(result);
};
} else if (methodName.equals("deleteById")) {
call = (proxy, args) -> proxy.prepareNamedQuery(namedDeleteByIdSQL).settParameters(args[0], idParamSetter).update();
} else if (methodName.equals("delete") && paramLen == 1 && !Condition.class.isAssignableFrom(paramTypes[0])) {
call = (proxy, args) -> proxy.prepareNamedQuery(namedDeleteByIdSQL).settParameters(args[0], idParamSetterByEntity).update();
//} else if (methodName.equals("delete") && paramLen == 2 && !Condition.class.isAssignableFrom(paramTypes[0])
// && OnDeleteAction.class.equals(paramTypes[1])) {
// call = (proxy, args) -> {
// final Object entity = (args[0]);
// final OnDeleteAction onDeleteAction = (OnDeleteAction) args[1];
//
// if (onDeleteAction == null || onDeleteAction == OnDeleteAction.NO_ACTION) {
// return proxy.prepareNamedQuery(namedDeleteByIdSQL).settParameters(entity, idParamSetterByEntity).update();
// }
//
// final Map entityJoinInfo = JoinInfo.getEntityJoinInfo(entityClass);
//
// if (N.isNullOrEmpty(entityJoinInfo)) {
// return proxy.prepareNamedQuery(namedDeleteByIdSQL).settParameters(entity, idParamSetterByEntity).update();
// } else {
// final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
// long result = 0;
//
// try {
// Tuple2> tp = null;
//
// for (JoinInfo propJoinInfo : entityJoinInfo.values()) {
// tp = onDeleteAction == OnDeleteAction.SET_NULL ? propJoinInfo.getSetNullSqlAndParamSetter(sbc)
// : propJoinInfo.getDeleteSqlAndParamSetter(sbc);
//
// result += proxy.prepareQuery(tp._1).setParameters(entity, tp._2).update();
// }
//
// result += proxy.prepareNamedQuery(namedDeleteByIdSQL).settParameters(entity, idParamSetterByEntity).update();
//
// tran.commit();
// } finally {
// tran.rollbackIfNotCommitted();
// }
//
// return N.toIntExact(result);
// }
// };
} else if ((methodName.equals("batchDelete") || methodName.equals("batchDeleteByIds")) && paramLen == 2
&& int.class.equals(paramTypes[1])) {
final JdbcUtil.BiParametersSetter paramSetter = methodName.equals("batchDeleteByIds") ? idParamSetter
: idParamSetterByEntity;
call = (proxy, args) -> {
final Collection idsOrEntities = (Collection) args[0];
final int batchSize = (Integer) args[1];
N.checkArgPositive(batchSize, "batchSize");
if (N.isNullOrEmpty(idsOrEntities)) {
return 0;
}
if (idsOrEntities.size() <= batchSize) {
return N.sum(proxy.prepareNamedQuery(namedDeleteByIdSQL).addBatchParameters(idsOrEntities, paramSetter).batchUpdate());
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
long result = 0;
try {
try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedDeleteByIdSQL).closeAfterExecution(false)) {
result = ExceptionalStream.of(idsOrEntities)
.splitToList(batchSize)
.sumInt(bp -> N.sum(nameQuery.addBatchParameters(bp, paramSetter).batchUpdate()))
.orZero();
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
return N.toIntExact(result);
}
};
// } else if (methodName.equals("batchDelete") && paramLen == 3 && OnDeleteAction.class.equals(paramTypes[1])
// && int.class.equals(paramTypes[2])) {
// final JdbcUtil.BiParametersSetter paramSetter = idParamSetterByEntity;
//
// call = (proxy, args) -> {
// final Collection entities = (Collection) args[0];
// final OnDeleteAction onDeleteAction = (OnDeleteAction) args[1];
// final int batchSize = (Integer) args[2];
// N.checkArgPositive(batchSize, "batchSize");
//
// if (N.isNullOrEmpty(entities)) {
// return 0;
// } else if (onDeleteAction == null || onDeleteAction == OnDeleteAction.NO_ACTION) {
// return ((JdbcUtil.CrudDao) proxy).batchDelete(entities, batchSize);
// }
//
// final Map entityJoinInfo = JoinInfo.getEntityJoinInfo(entityClass);
//
// if (N.isNullOrEmpty(entityJoinInfo)) {
// return ((JdbcUtil.CrudDao) proxy).batchDelete(entities, batchSize);
// }
//
// final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
// long result = 0;
//
// try {
// try (NamedQuery nameQuery = proxy.prepareNamedQuery(namedDeleteByIdSQL).closeAfterExecution(false)) {
// result = ExceptionalStream.of(entities) //
// .splitToList(batchSize) //
// .sumLong(bp -> {
// long tmpResult = 0;
//
// Tuple2> tp = null;
//
// for (JoinInfo propJoinInfo : entityJoinInfo.values()) {
// tp = onDeleteAction == OnDeleteAction.SET_NULL ? propJoinInfo.getSetNullSqlAndParamSetter(sbc)
// : propJoinInfo.getDeleteSqlAndParamSetter(sbc);
//
// tmpResult += N.sum(proxy.prepareQuery(tp._1).addBatchParameters2(bp, tp._2).batchUpdate());
// }
//
// tmpResult += N.sum(nameQuery.addBatchParameters(bp, paramSetter).batchUpdate());
//
// return tmpResult;
// })
// .orZero();
// }
//
// tran.commit();
// } finally {
// tran.rollbackIfNotCommitted();
// }
//
// return N.toIntExact(result);
// };
} else {
call = (proxy, args) -> {
throw new UnsupportedOperationException("Unsupported operation: " + m);
};
}
} else if (declaringClass.equals(JdbcUtil.JoinEntityHelper.class) || declaringClass.equals(JdbcUtil.UncheckedJoinEntityHelper.class)) {
if (methodName.equals("loadJoinEntities") && paramLen == 3 && !Collection.class.isAssignableFrom(paramTypes[0])
&& String.class.isAssignableFrom(paramTypes[1]) && Collection.class.isAssignableFrom(paramTypes[2])) {
call = (proxy, args) -> {
final Object entity = args[0];
final String joinEntityPropName = (String) args[1];
final Collection selectPropNames = (Collection) args[2];
final JoinInfo propJoinInfo = JoinInfo.getPropJoinInfo(daoInterface, entityClass, joinEntityPropName);
final Tuple2, String>, JdbcUtil.BiParametersSetter> tp = propJoinInfo
.getSelectSQLBuilderAndParamSetter(sbc);
final JdbcUtil.PreparedQuery preparedQuery = proxy.prepareQuery(tp._1.apply(selectPropNames)).setParameters(entity, tp._2);
if (propJoinInfo.joinPropInfo.type.isCollection()) {
final List> propEntities = preparedQuery.list(propJoinInfo.referencedEntityClass);
if (propJoinInfo.joinPropInfo.clazz.isAssignableFrom(propEntities.getClass())) {
propJoinInfo.joinPropInfo.setPropValue(entity, propEntities);
} else {
final Collection c = (Collection) N.newInstance(propJoinInfo.joinPropInfo.clazz);
c.addAll(propEntities);
propJoinInfo.joinPropInfo.setPropValue(entity, c);
}
} else {
propJoinInfo.joinPropInfo.setPropValue(entity, preparedQuery.findFirst(propJoinInfo.referencedEntityClass).orNull());
}
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, propJoinInfo.joinPropInfo.name, false);
}
return null;
};
} else if (methodName.equals("loadJoinEntities") && paramLen == 3 && Collection.class.isAssignableFrom(paramTypes[0])
&& String.class.isAssignableFrom(paramTypes[1]) && Collection.class.isAssignableFrom(paramTypes[2])) {
call = (proxy, args) -> {
final Collection entities = (Collection) args[0];
final String joinEntityPropName = (String) args[1];
final Collection selectPropNames = (Collection) args[2];
final JoinInfo propJoinInfo = JoinInfo.getPropJoinInfo(daoInterface, entityClass, joinEntityPropName);
if (N.isNullOrEmpty(entities)) {
// Do nothing.
} else if (entities.size() == 1) {
final Object entity = N.firstOrNullIfEmpty(entities);
final Tuple2, String>, JdbcUtil.BiParametersSetter> tp = propJoinInfo
.getSelectSQLBuilderAndParamSetter(sbc);
final JdbcUtil.PreparedQuery preparedQuery = proxy.prepareQuery(tp._1.apply(selectPropNames)).setParameters(entity, tp._2);
if (propJoinInfo.joinPropInfo.type.isCollection()) {
final List> propEntities = preparedQuery.list(propJoinInfo.referencedEntityClass);
if (propJoinInfo.joinPropInfo.clazz.isAssignableFrom(propEntities.getClass())) {
propJoinInfo.joinPropInfo.setPropValue(entity, propEntities);
} else {
final Collection c = (Collection) N.newInstance(propJoinInfo.joinPropInfo.clazz);
c.addAll(propEntities);
propJoinInfo.joinPropInfo.setPropValue(entity, c);
}
} else {
propJoinInfo.joinPropInfo.setPropValue(entity, preparedQuery.findFirst(propJoinInfo.referencedEntityClass).orNull());
}
if (isDirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, propJoinInfo.joinPropInfo.name, false);
}
} else {
final Tuple2, Integer, String>, JdbcUtil.BiParametersSetter>> tp = propJoinInfo
.getSelectSQLBuilderAndParamSetterForBatch(sbc);
if (propJoinInfo.isManyToManyJoin()) {
final BiRowMapper biRowMapper = BiRowMapper.to(propJoinInfo.referencedEntityClass, true);
final BiRowMapper> pairBiRowMapper = (rs, cls) -> Pair.of(rs.getObject(1), biRowMapper.apply(rs, cls));
final List> joinPropEntities = proxy.prepareQuery(tp._1.apply(selectPropNames, entities.size()))
.setParameters(entities, tp._2)
.list(pairBiRowMapper);
propJoinInfo.setJoinPropEntities(entities, Stream.of(joinPropEntities).groupTo(it -> it.left, it -> it.right));
} else {
final List> joinPropEntities = proxy.prepareQuery(tp._1.apply(selectPropNames, entities.size()))
.setParameters(entities, tp._2)
.list(propJoinInfo.referencedEntityClass);
propJoinInfo.setJoinPropEntities(entities, joinPropEntities);
}
}
return null;
};
} else if (methodName.equals("deleteJoinEntities") && paramLen == 2 && !Collection.class.isAssignableFrom(paramTypes[0])
&& String.class.isAssignableFrom(paramTypes[1])) {
call = (proxy, args) -> {
final Object entity = args[0];
final String joinEntityPropName = (String) args[1];
final JoinInfo propJoinInfo = JoinInfo.getPropJoinInfo(daoInterface, entityClass, joinEntityPropName);
final Tuple2> tp = propJoinInfo.getDeleteSqlAndParamSetter(sbc);
return proxy.prepareQuery(tp._1).setParameters(entity, tp._2).update();
};
} else if (methodName.equals("deleteJoinEntities") && paramLen == 2 && Collection.class.isAssignableFrom(paramTypes[0])
&& String.class.isAssignableFrom(paramTypes[1])) {
call = (proxy, args) -> {
final Collection entities = (Collection) args[0];
final String joinEntityPropName = (String) args[1];
final JoinInfo propJoinInfo = JoinInfo.getPropJoinInfo(daoInterface, entityClass, joinEntityPropName);
final Tuple2> tp = propJoinInfo.getDeleteSqlAndParamSetter(sbc);
if (N.isNullOrEmpty(entities)) {
return 0;
} else if (entities.size() == 1) {
return proxy.prepareQuery(tp._1).setParameters(N.firstOrNullIfEmpty(entities), tp._2).update();
} else {
return proxy.prepareQuery(tp._1).addBatchParametters(entities, tp._2).update();
}
};
} else {
call = (proxy, args) -> {
throw new UnsupportedOperationException("Unsupported operation: " + m);
};
}
} else {
if (java.util.Optional.class.isAssignableFrom(returnType) || java.util.OptionalInt.class.isAssignableFrom(returnType)
|| java.util.OptionalLong.class.isAssignableFrom(returnType) || java.util.OptionalDouble.class.isAssignableFrom(returnType)) {
throw new UnsupportedOperationException("the return type of the method: " + fullClassMethodName + " can't be: " + returnType
+ ". Please use the OptionalXXX classes defined in com.landawn.abacus.util.u");
}
if (!(isUnchecked || throwsSQLException || isStreamReturn)) {
throw new UnsupportedOperationException("'throws SQLException' is not declared in method: " + fullClassMethodName
+ ". It's required for Dao interface extends Dao, not UncheckedDao");
}
if (isStreamReturn && throwsSQLException) {
throw new UnsupportedOperationException("'throws SQLException' is not allowed in method: " + fullClassMethodName
+ " because its return type is Stream/ExceptionalStream which will be lazy evaluation");
}
final Class> lastParamType = paramLen == 0 ? null : paramTypes[paramLen - 1];
final QueryInfo queryInfo = sqlAnnoMap.get(sqlAnno.annotationType()).apply(sqlAnno, sqlMapper);
final String query = N.checkArgNotNullOrEmpty(queryInfo.sql, "sql can't be null or empty");
final int queryTimeout = queryInfo.queryTimeout;
final int fetchSize = queryInfo.fetchSize;
final boolean isBatch = queryInfo.isBatch;
final int tmpBatchSize = queryInfo.batchSize;
final OP op = queryInfo.op;
final boolean isSingleParameter = queryInfo.isSingleParameter;
final boolean returnGeneratedKeys = isNoId == false
&& (sqlAnno.annotationType().equals(Dao.Insert.class) || sqlAnno.annotationType().equals(Dao.NamedInsert.class));
final boolean isCall = sqlAnno.annotationType().getSimpleName().endsWith("Call");
final boolean isNamedQuery = sqlAnno.annotationType().getSimpleName().startsWith("Named");
final Predicate> isRowMapperOrResultExtractor = it -> JdbcUtil.ResultExtractor.class.isAssignableFrom(it)
|| JdbcUtil.BiResultExtractor.class.isAssignableFrom(it) || JdbcUtil.RowMapper.class.isAssignableFrom(it)
|| JdbcUtil.BiRowMapper.class.isAssignableFrom(it);
if (isNamedQuery) {
// @Bind parameters are not always required for named query. It's not required if parameter is Entity/Map/EnityId/...
// if (IntStreamEx.range(0, paramLen)
// .noneMatch(i -> StreamEx.of(m.getParameterAnnotations()[i]).anyMatch(it -> it.annotationType().equals(Dao.Bind.class)))) {
// throw new UnsupportedOperationException(
// "@Bind parameters are required for named query but none is defined in method: " + fullClassMethodName);
// }
} else {
if (IntStreamEx.range(0, paramLen)
.anyMatch(i -> StreamEx.of(m.getParameterAnnotations()[i]).anyMatch(it -> it.annotationType().equals(Dao.Bind.class)))) {
throw new UnsupportedOperationException("@Bind parameters are defined for non-named query in method: " + fullClassMethodName);
}
}
final int[] tmp = IntStreamEx.range(0, paramLen).filter(i -> !isRowMapperOrResultExtractor.test(paramTypes[i])).toArray();
if (N.notNullOrEmpty(tmp) && tmp[tmp.length - 1] != tmp.length - 1) {
throw new UnsupportedOperationException(
"RowMapper/ResultExtractor must be the last parameter but not in method: " + fullClassMethodName);
}
final Predicate> isRowFilter = it -> JdbcUtil.RowFilter.class.isAssignableFrom(it)
|| JdbcUtil.BiRowFilter.class.isAssignableFrom(it);
final int[] tmp2 = IntStreamEx.of(tmp).filter(i -> !isRowFilter.test(paramTypes[i])).toArray();
if (N.notNullOrEmpty(tmp2) && tmp2[tmp2.length - 1] != tmp2.length - 1) {
throw new UnsupportedOperationException(
"RowFilter/BiRowFilter must be the last parameter or just before RowMapper/ResultExtractor but not in method: "
+ fullClassMethodName);
}
final Predicate> isParameterSetter = it -> JdbcUtil.ParametersSetter.class.isAssignableFrom(it)
|| JdbcUtil.BiParametersSetter.class.isAssignableFrom(it) || JdbcUtil.TriParametersSetter.class.isAssignableFrom(it);
final int[] tmp3 = IntStreamEx.of(tmp2).filter(i -> !isParameterSetter.test(paramTypes[i])).toArray();
if (N.notNullOrEmpty(tmp3) && tmp3[tmp3.length - 1] != tmp3.length - 1) {
throw new UnsupportedOperationException(
"ParametersSetter/BiParametersSetter/TriParametersSetter must be the last parameter or just before RowFilter/BiRowFilter/RowMapper/ResultExtractor but not in method: "
+ fullClassMethodName);
}
final boolean hasRowMapperOrResultExtractor = paramLen > 0 && isRowMapperOrResultExtractor.test(lastParamType);
final boolean hasRowFilter = paramLen >= 2 && (JdbcUtil.RowFilter.class.isAssignableFrom(paramTypes[paramLen - 2])
|| JdbcUtil.BiRowFilter.class.isAssignableFrom(paramTypes[paramLen - 2]));
if (hasRowFilter && !hasRowMapperOrResultExtractor) {
throw new UnsupportedOperationException(
"Parameter 'RowFilter/BiRowFilter' is not supported without last paramere to be 'RowMapper/BiRowMapper' in method: "
+ fullClassMethodName);
}
if (hasRowMapperOrResultExtractor) {
throw new UnsupportedOperationException(
"Retrieving result/record by 'ResultExtractor/BiResultExtractor/RowMapper/BiRowMapper' is not enabled at present. Can't use it in method: "
+ fullClassMethodName);
}
final int[] defineParamIndexes = IntStreamEx.of(tmp3)
.filter(i -> StreamEx.of(m.getParameterAnnotations()[i]).anyMatch(it -> it.annotationType().equals(Dao.Define.class)))
.toArray();
final int defineParamLen = N.len(defineParamIndexes);
if (defineParamLen > 0) {
// if (defineParamIndexes[0] != 0 || defineParamIndexes[defineParamLen - 1] - defineParamIndexes[0] + 1 != defineParamLen) {
// throw new UnsupportedOperationException(
// "Parameters annotated with @Define must be at the head of the parameter list of method: " + fullClassMethodName);
// }
// for (int i = 0; i < defineParamLen; i++) {
// if (!paramTypes[defineParamIndexes[i]].equals(String.class)) {
// throw new UnsupportedOperationException("The type of parameter[" + i + "] annotated with @Define can't be: " + paramTypes[i]
// + " in method: " + fullClassMethodName + ". It must be String");
// }
// }
}
final String[] defines = defineParamLen == 0 ? N.EMPTY_STRING_ARRAY
: IntStreamEx.of(defineParamIndexes)
.mapToObj(i -> StreamEx.of(m.getParameterAnnotations()[i]).select(Dao.Define.class).first().get().value())
.map(it -> it.charAt(0) == '{' && it.charAt(it.length() - 1) == '}' ? it : "{" + it + "}")
.toArray(IntFunctions.ofStringArray());
final int[] stmtParamIndexes = IntStreamEx.of(tmp3)
.filter(i -> StreamEx.of(m.getParameterAnnotations()[i]).noneMatch(it -> it.annotationType().equals(Dao.Define.class)))
.toArray();
final int stmtParamLen = stmtParamIndexes.length;
if (stmtParamLen == 1 && (ClassUtil.isEntity(paramTypes[stmtParamIndexes[0]]) || Map.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]])
|| EntityId.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]])) && isNamedQuery == false) {
throw new UnsupportedOperationException(
"Using named query: @NamedSelect/NamedUpdate/NamedInsert/NamedDelete when parameter type is Entity/Map/EntityId in method: "
+ fullClassMethodName);
}
if (isSingleParameter && stmtParamLen != 1) {
throw new UnsupportedOperationException(
"Don't set 'isSingleParameter' to true if the count of statement/query parameter is not one in method: " + fullClassMethodName);
}
final List outParameterList = StreamEx.of(m.getAnnotations())
.select(Dao.OutParameter.class)
.append(StreamEx.of(m.getAnnotations()).select(DaoUtil.OutParameterList.class).flatMapp(e -> e.value()))
.toList();
if (N.notNullOrEmpty(outParameterList)) {
if (!isCall) {
throw new UnsupportedOperationException(
"@OutParameter annotations are only supported by method annotated by @Call, not supported in method: "
+ fullClassMethodName);
}
if (StreamEx.of(outParameterList).anyMatch(it -> N.isNullOrEmpty(it.name()) && it.position() < 0)) {
throw new UnsupportedOperationException(
"One of the attribute: (name, position) of @OutParameter must be set in method: " + fullClassMethodName);
}
}
if (isBatch) {
if (!((stmtParamLen == 1 && Collection.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]])) || (stmtParamLen == 2
&& Collection.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]]) && int.class.equals(paramTypes[stmtParamIndexes[1]])))) {
throw new UnsupportedOperationException("For batch operations(" + fullClassMethodName
+ "), the first parameter must be Collection. The second parameter is optional, it only can be int if it's set");
}
// if (isNamedQuery == false) {
// throw new UnsupportedOperationException(
// "Only named query/sql is supported for batch operations(" + fullClassMethodName + ") at present");
// }
// if (isNamedQuery == false) {
// final java.lang.reflect.Type[] genericParameterTypes = m.getGenericParameterTypes();
// final java.lang.reflect.Type genericBatchParametersType = genericParameterTypes[stmtParamIndexes[0]];
//
// boolean isArrayBatchParameters = List.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]])
// && genericBatchParametersType instanceof ParameterizedType
// && Object[].class.isAssignableFrom((Class) ((ParameterizedType) genericBatchParametersType).getActualTypeArguments()[0]);
//
// final boolean isListBatchParameters = Collection.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]])
// && genericBatchParametersType instanceof ParameterizedType
// && List.class.isAssignableFrom((Class) ((ParameterizedType) genericBatchParametersType).getActualTypeArguments()[0]);
//
// if (!(isArrayBatchParameters || isListBatchParameters)) {
// throw new UnsupportedOperationException("For non-named batch operations(" + fullClassMethodName
// + "), the batch parameter type must be: List extends Object[]) or Collection extends List>>. The second parameter is optional, it only can be int if it's set");
// }
// }
}
final ParsedSql namedSql = isNamedQuery && defineParamLen == 0 ? ParsedSql.parse(query) : null;
final JdbcUtil.BiParametersSetter parametersSetter = createParametersSetter(m, fullClassMethodName,
paramTypes, paramLen, isBatch, isSingleParameter, isCall, isNamedQuery, namedSql, stmtParamLen, stmtParamIndexes, stmtParamLen);
// final boolean isUpdateReturnType = returnType.equals(int.class) || returnType.equals(Integer.class) || returnType.equals(long.class)
// || returnType.equals(Long.class) || returnType.equals(boolean.class) || returnType.equals(Boolean.class)
// || returnType.equals(void.class);
final boolean isUpdateReturnType = returnType.equals(int.class) || returnType.equals(long.class) || returnType.equals(boolean.class)
|| returnType.equals(void.class);
final boolean idDirtyMarkerReturnType = ClassUtil.isEntity(returnType) && DirtyMarker.class.isAssignableFrom(returnType);
if (sqlAnno.annotationType().equals(Dao.Select.class) || sqlAnno.annotationType().equals(Dao.NamedSelect.class)
|| (isCall && !isUpdateReturnType)) {
final Throwables.BiFunction queryFunc = createQueryFunctionByMethod(m,
hasRowMapperOrResultExtractor, hasRowFilter, op, fullClassMethodName);
// Getting ClassCastException. Not sure why query result is being casted Dao. It seems there is a bug in JDk compiler.
// call = (proxy, args) -> queryFunc.apply(JdbcUtil.prepareQuery(proxy, ds, query, isNamedQuery, fetchSize, queryTimeout, returnGeneratedKeys, args, paramSetter), args);
call = (proxy, args) -> {
Object result = queryFunc.apply(prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, -1, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList).settParameters(args,
parametersSetter),
args);
if (idDirtyMarkerReturnType) {
((DirtyMarker) result).markDirty(false);
}
return result;
};
} else if (sqlAnno.annotationType().equals(Dao.Insert.class) || sqlAnno.annotationType().equals(Dao.NamedInsert.class)) {
if (isNoId) {
if (!returnType.isAssignableFrom(void.class)) {
throw new UnsupportedOperationException("The return type of insert operations(" + fullClassMethodName
+ ") for no id entities only can be: void. It can't be: " + returnType);
}
}
final TriFunction, Object, Boolean, ?> insertResultConvertor = void.class.equals(returnType)
? (ret, entity, isEntity) -> null
: (Optional.class.equals(returnType) ? (ret, entity, isEntity) -> ret
: (ret, entity, isEntity) -> ret.orElse(isEntity ? idGetter.apply(entity) : N.defaultValueOf(returnType)));
if (isBatch == false) {
if (!(returnType.isAssignableFrom(void.class) || idClass == null
|| Primitives.wrap(idClass).isAssignableFrom(Primitives.wrap(returnType)) || returnType.isAssignableFrom(Optional.class))) {
throw new UnsupportedOperationException("The return type of insert operations(" + fullClassMethodName
+ ") only can be: void or 'ID' type. It can't be: " + returnType);
}
call = (proxy, args) -> {
final boolean isEntity = stmtParamLen == 1 && args[stmtParamIndexes[0]] != null
&& ClassUtil.isEntity(args[stmtParamIndexes[0]].getClass());
final Object entity = isEntity ? args[stmtParamIndexes[0]] : null;
final Optional id = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, -1, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.settParameters(args, parametersSetter)
.insert(keyExtractor);
if (isEntity && id.isPresent()) {
id.ifPresent(ret -> idSetter.accept(ret, entity));
}
if (isEntity && entity instanceof DirtyMarker) {
DirtyMarkerUtil.markDirty((DirtyMarker) entity, false);
}
return insertResultConvertor.apply(id, entity, isEntity);
};
} else {
if (!(returnType.equals(void.class) || returnType.isAssignableFrom(List.class))) {
throw new UnsupportedOperationException("The return type of batch insert operations(" + fullClassMethodName
+ ") only can be: void/List/Collection. It can't be: " + returnType);
}
call = (proxy, args) -> {
final Collection batchParameters = (Collection) args[stmtParamIndexes[0]];
int batchSize = tmpBatchSize;
if (stmtParamLen == 2) {
batchSize = (Integer) args[stmtParamIndexes[1]];
}
if (batchSize == 0) {
batchSize = JdbcUtil.DEFAULT_BATCH_SIZE;
}
N.checkArgPositive(batchSize, "batchSize");
List ids = null;
if (N.isNullOrEmpty(batchParameters)) {
ids = new ArrayList<>(0);
} else if (batchParameters.size() < batchSize) {
AbstractPreparedQuery preparedQuery = null;
if (isSingleParameter) {
preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.addBatchParameters(batchParameters, ColumnOne.SET_OBJECT);
} else {
preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.addBatchParameters(batchParameters);
}
ids = preparedQuery.batchInsert();
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (AbstractPreparedQuery preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery,
query, namedSql, fetchSize, isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall,
outParameterList).closeAfterExecution(false)) {
if (isSingleParameter) {
ids = ExceptionalStream.of(batchParameters)
.splitToList(batchSize) //
.flattMap(bp -> preparedQuery.addBatchParameters(bp, ColumnOne.SET_OBJECT).batchInsert())
.toList();
} else {
ids = ExceptionalStream.of((Collection>) (Collection) batchParameters)
.splitToList(batchSize) //
.flattMap(bp -> preparedQuery.addBatchParameters(bp).batchInsert())
.toList();
}
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
final boolean isEntity = ClassUtil.isEntity(N.firstOrNullIfEmpty(batchParameters).getClass());
if (N.notNullOrEmpty(ids) && Stream.of(ids).allMatch(Fn.isNull())) {
ids = new ArrayList<>();
}
if (isEntity) {
final Collection entities = batchParameters;
if (N.notNullOrEmpty(ids) && N.notNullOrEmpty(entities) && ids.size() == N.size(entities)) {
int idx = 0;
for (Object e : entities) {
idSetter.accept(ids.get(idx++), e);
}
}
if (N.firstOrNullIfEmpty(entities) instanceof DirtyMarker) {
for (Object e : entities) {
DirtyMarkerUtil.markDirty((DirtyMarker) e, false);
}
}
if (N.isNullOrEmpty(ids)) {
ids = Stream.of(entities).map(idGetter).toList();
}
}
if (N.notNullOrEmpty(ids) && ids.size() != batchParameters.size()) {
if (daoLogger.isWarnEnabled()) {
daoLogger.warn("The size of returned id list: {} is different from the size of input entity list: {}", ids.size(),
batchParameters.size());
}
}
return void.class.equals(returnType) ? null : ids;
};
}
} else if (sqlAnno.annotationType().equals(Dao.Update.class) || sqlAnno.annotationType().equals(Dao.Delete.class)
|| sqlAnno.annotationType().equals(Dao.NamedUpdate.class) || sqlAnno.annotationType().equals(Dao.NamedDelete.class)
|| (sqlAnno.annotationType().equals(Dao.Call.class) && isUpdateReturnType)) {
if (!isUpdateReturnType) {
throw new UnsupportedOperationException("The return type of update/delete operations(" + fullClassMethodName
+ ") only can be: int/Integer/long/Long/boolean/Boolean/void. It can't be: " + returnType);
}
final LongFunction> updateResultConvertor = void.class.equals(returnType) ? updatedRecordCount -> null
: (Boolean.class.equals(Primitives.wrap(returnType)) ? updatedRecordCount -> updatedRecordCount > 0
: (Integer.class.equals(Primitives.wrap(returnType)) ? updatedRecordCount -> N.toIntExact(updatedRecordCount)
: LongFunction.identity()));
final boolean isLargeUpdate = op == OP.largeUpdate
|| (op == OP.DEFAULT && (returnType.equals(long.class) || returnType.equals(Long.class)));
if (isBatch == false) {
final boolean idDirtyMarker = stmtParamLen == 1 && ClassUtil.isEntity(paramTypes[stmtParamIndexes[0]])
&& DirtyMarker.class.isAssignableFrom(paramTypes[stmtParamIndexes[0]]);
call = (proxy, args) -> {
final AbstractPreparedQuery preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query,
namedSql, fetchSize, isBatch, -1, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.settParameters(args, parametersSetter);
final long updatedRecordCount = isLargeUpdate ? preparedQuery.largeUpdate() : preparedQuery.update();
if (idDirtyMarker) {
if (sqlAnno.annotationType().equals(Dao.NamedUpdate.class)) {
((DirtyMarker) args[stmtParamIndexes[0]]).markDirty(namedSql.getNamedParameters(), false);
} else {
((DirtyMarker) args[stmtParamIndexes[0]]).markDirty(false);
}
}
return updateResultConvertor.apply(updatedRecordCount);
};
} else {
call = (proxy, args) -> {
final Collection batchParameters = (Collection) args[stmtParamIndexes[0]];
int batchSize = tmpBatchSize;
if (stmtParamLen == 2) {
batchSize = (Integer) args[stmtParamIndexes[1]];
}
if (batchSize == 0) {
batchSize = JdbcUtil.DEFAULT_BATCH_SIZE;
}
N.checkArgPositive(batchSize, "batchSize");
long updatedRecordCount = 0;
if (N.isNullOrEmpty(batchParameters)) {
updatedRecordCount = 0;
} else if (batchParameters.size() < batchSize) {
AbstractPreparedQuery preparedQuery = null;
if (isSingleParameter) {
preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.addBatchParameters(batchParameters, ColumnOne.SET_OBJECT);
} else {
preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery, query, namedSql, fetchSize,
isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall, outParameterList)
.addBatchParameters(batchParameters);
}
if (isLargeUpdate) {
updatedRecordCount = N.sum(preparedQuery.largeBatchUpdate());
} else {
updatedRecordCount = N.sum(preparedQuery.batchUpdate());
}
} else {
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
try {
try (AbstractPreparedQuery preparedQuery = prepareQuery(proxy, m, args, defineParamIndexes, defines, isNamedQuery,
query, namedSql, fetchSize, isBatch, batchSize, queryTimeout, returnGeneratedKeys, returnColumnNames, isCall,
outParameterList).closeAfterExecution(false)) {
if (isSingleParameter) {
updatedRecordCount = ExceptionalStream.of(batchParameters)
.splitToList(batchSize) //
.sumLong(bp -> isLargeUpdate
? N.sum(preparedQuery.addBatchParameters(bp, ColumnOne.SET_OBJECT).largeBatchUpdate())
: N.sum(preparedQuery.addBatchParameters(bp, ColumnOne.SET_OBJECT).batchUpdate()))
.orZero();
} else {
updatedRecordCount = ExceptionalStream.of((Collection>) (Collection) batchParameters)
.splitToList(batchSize) //
.sumLong(bp -> isLargeUpdate //
? N.sum(preparedQuery.addBatchParameters(bp).largeBatchUpdate())
: N.sum(preparedQuery.addBatchParameters(bp).batchUpdate()))
.orZero();
}
}
tran.commit();
} finally {
tran.rollbackIfNotCommitted();
}
}
if (N.firstOrNullIfEmpty(batchParameters) instanceof DirtyMarker) {
if (sqlAnno.annotationType().equals(Dao.NamedUpdate.class)) {
for (Object e : batchParameters) {
((DirtyMarker) e).markDirty(namedSql.getNamedParameters(), false);
}
} else {
for (Object e : batchParameters) {
((DirtyMarker) e).markDirty(false);
}
}
}
return updateResultConvertor.apply(updatedRecordCount);
};
}
} else {
throw new UnsupportedOperationException(
"Unsupported sql annotation: " + sqlAnno.annotationType() + " in method: " + fullClassMethodName);
}
}
if (isStreamReturn) {
if (ExceptionalStream.class.isAssignableFrom(returnType)) {
final Throwables.BiFunction tmp = (Throwables.BiFunction) call;
call = (proxy, args) -> {
final Throwables.Supplier supplier = () -> tmp.apply(proxy, args);
return ExceptionalStream.of(supplier).flatMap(it -> it.get());
};
} else {
final Throwables.BiFunction tmp = (Throwables.BiFunction) call;
call = (proxy, args) -> {
final Supplier supplier = () -> Throwables.call(() -> tmp.apply(proxy, args));
return Stream.of(supplier).flatMap(it -> it.get());
};
}
} else if (throwsSQLException == false) {
final Throwables.BiFunction tmp = call;
call = (proxy, args) -> {
try {
return tmp.apply(proxy, args);
} catch (SQLException e) {
throw new UncheckedSQLException(e);
}
};
call = tmp;
}
}
final boolean isNonDBOperation = StreamEx.of(m.getAnnotations()).anyMatch(anno -> anno.annotationType().equals(DaoUtil.NonDBOperation.class));
if (isNonDBOperation) {
nonDBOperationSet.add(m);
if (daoLogger.isDebugEnabled()) {
daoLogger.debug("Non-DB operation method: " + simpleClassMethodName);
}
// ignore
} else {
final Dao.Transactional transactionalAnno = StreamEx.of(m.getAnnotations()).select(Dao.Transactional.class).last().orNull();
// if (transactionalAnno != null && Modifier.isAbstract(m.getModifiers())) {
// throw new UnsupportedOperationException(
// "Annotation @Transactional is only supported by interface methods with default implementation: default xxx dbOperationABC(someParameters, String ... sqls), not supported by abstract method: "
// + fullClassMethodName);
// }
final Dao.SqlLogEnabled sqlLogAnno = StreamEx.of(m.getAnnotations()).select(Dao.SqlLogEnabled.class).last().orElse(daoClassSqlLogAnno);
final Dao.PerfLog perfLogAnno = StreamEx.of(m.getAnnotations()).select(Dao.PerfLog.class).last().orElse(daoClassPerfLogAnno);
final boolean hasSqlLogAnno = sqlLogAnno != null;
final boolean hasPerfLogAnno = perfLogAnno != null;
final Throwables.BiFunction tmp = call;
if (transactionalAnno == null || transactionalAnno.propagation() == Propagation.SUPPORTS) {
if (hasSqlLogAnno || hasPerfLogAnno) {
call = (proxy, args) -> {
final boolean prevSqlLogEnabled = hasSqlLogAnno ? JdbcUtil.isSQLLogEnabled() : false;
final long prevMinExecutionTimeForSQLPerfLog = hasPerfLogAnno ? JdbcUtil.getMinExecutionTimeForSQLPerfLog() : -1;
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(sqlLogAnno.value());
}
if (hasPerfLogAnno) {
JdbcUtil.setMinExecutionTimeForSQLPerfLog(perfLogAnno.minExecutionTimeForSql());
}
final long startTime = hasPerfLogAnno ? System.currentTimeMillis() : -1;
try {
return tmp.apply(proxy, args);
} finally {
if (hasPerfLogAnno) {
if (perfLogAnno.minExecutionTimeForOperation() >= 0 && daoLogger.isInfoEnabled()) {
final long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime >= perfLogAnno.minExecutionTimeForOperation()) {
daoLogger.info("[DAO-OP-PERF]: " + elapsedTime + ", " + simpleClassMethodName);
}
}
JdbcUtil.setMinExecutionTimeForSQLPerfLog(prevMinExecutionTimeForSQLPerfLog);
}
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(prevSqlLogEnabled);
}
}
};
} else {
// Do not need to do anything.
}
} else if (transactionalAnno.propagation() == Propagation.REQUIRED) {
call = (proxy, args) -> {
final boolean prevSqlLogEnabled = hasSqlLogAnno ? JdbcUtil.isSQLLogEnabled() : false;
final long prevMinExecutionTimeForSQLPerfLog = hasPerfLogAnno ? JdbcUtil.getMinExecutionTimeForSQLPerfLog() : -1;
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(sqlLogAnno.value());
}
if (hasPerfLogAnno) {
JdbcUtil.setMinExecutionTimeForSQLPerfLog(perfLogAnno.minExecutionTimeForSql());
}
final long startTime = hasPerfLogAnno ? System.currentTimeMillis() : -1;
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
Object result = null;
try {
result = tmp.apply(proxy, args);
tran.commit();
} finally {
if (hasSqlLogAnno || hasPerfLogAnno) {
try {
tran.rollbackIfNotCommitted();
} finally {
if (hasPerfLogAnno) {
if (perfLogAnno.minExecutionTimeForOperation() >= 0 && daoLogger.isInfoEnabled()) {
final long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime >= perfLogAnno.minExecutionTimeForOperation()) {
daoLogger.info("[DAO-OP-PERF]: " + elapsedTime + ", " + simpleClassMethodName);
}
}
JdbcUtil.setMinExecutionTimeForSQLPerfLog(prevMinExecutionTimeForSQLPerfLog);
}
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(prevSqlLogEnabled);
}
}
} else {
tran.rollbackIfNotCommitted();
}
}
return result;
};
} else if (transactionalAnno.propagation() == Propagation.REQUIRES_NEW) {
call = (proxy, args) -> {
final javax.sql.DataSource dataSource = proxy.dataSource();
return JdbcUtil.callNotInStartedTransaction(dataSource, () -> {
final boolean prevSqlLogEnabled = hasSqlLogAnno ? JdbcUtil.isSQLLogEnabled() : false;
final long prevMinExecutionTimeForSQLPerfLog = hasPerfLogAnno ? JdbcUtil.getMinExecutionTimeForSQLPerfLog() : -1;
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(sqlLogAnno.value());
}
if (hasPerfLogAnno) {
JdbcUtil.setMinExecutionTimeForSQLPerfLog(perfLogAnno.minExecutionTimeForSql());
}
final long startTime = hasPerfLogAnno ? System.currentTimeMillis() : -1;
final SQLTransaction tran = JdbcUtil.beginTransaction(proxy.dataSource());
Object result = null;
try {
result = tmp.apply(proxy, args);
tran.commit();
} finally {
if (hasSqlLogAnno || hasPerfLogAnno) {
try {
tran.rollbackIfNotCommitted();
} finally {
if (hasPerfLogAnno) {
if (perfLogAnno.minExecutionTimeForOperation() >= 0 && daoLogger.isInfoEnabled()) {
final long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime >= perfLogAnno.minExecutionTimeForOperation()) {
daoLogger.info("[DAO-OP-PERF]: " + elapsedTime + ", " + simpleClassMethodName);
}
}
JdbcUtil.setMinExecutionTimeForSQLPerfLog(prevMinExecutionTimeForSQLPerfLog);
}
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(prevSqlLogEnabled);
}
}
} else {
tran.rollbackIfNotCommitted();
}
}
return result;
});
};
} else if (transactionalAnno.propagation() == Propagation.NOT_SUPPORTED) {
call = (proxy, args) -> {
final javax.sql.DataSource dataSource = proxy.dataSource();
if (hasSqlLogAnno || hasPerfLogAnno) {
return JdbcUtil.callNotInStartedTransaction(dataSource, () -> {
final boolean prevSqlLogEnabled = hasSqlLogAnno ? JdbcUtil.isSQLLogEnabled() : false;
final long prevMinExecutionTimeForSQLPerfLog = hasPerfLogAnno ? JdbcUtil.getMinExecutionTimeForSQLPerfLog() : -1;
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(sqlLogAnno.value());
}
if (hasPerfLogAnno) {
JdbcUtil.setMinExecutionTimeForSQLPerfLog(perfLogAnno.minExecutionTimeForSql());
}
final long startTime = hasPerfLogAnno ? System.currentTimeMillis() : -1;
try {
return tmp.apply(proxy, args);
} finally {
if (hasPerfLogAnno) {
if (perfLogAnno.minExecutionTimeForOperation() >= 0 && daoLogger.isInfoEnabled()) {
final long elapsedTime = System.currentTimeMillis() - startTime;
if (elapsedTime >= perfLogAnno.minExecutionTimeForOperation()) {
daoLogger.info("[DAO-OP-PERF]: " + elapsedTime + ", " + simpleClassMethodName);
}
}
JdbcUtil.setMinExecutionTimeForSQLPerfLog(prevMinExecutionTimeForSQLPerfLog);
}
if (hasSqlLogAnno) {
JdbcUtil.enableSQLLog(prevSqlLogEnabled);
}
}
});
} else {
return JdbcUtil.callNotInStartedTransaction(dataSource, () -> tmp.apply(proxy, args));
}
};
}
final Predicate filterByMethodName = it -> N.notNullOrEmpty(it)
&& (StringUtil.containsIgnoreCase(m.getName(), it) || Pattern.matches(it, m.getName()));
final Dao.CacheResult cacheResultAnno = StreamEx.of(m.getAnnotations())
.select(Dao.CacheResult.class)
.last()
.orElse((daoClassCacheResultAnno != null && N.anyMatch(daoClassCacheResultAnno.filter(), filterByMethodName)) ? daoClassCacheResultAnno
: null);
final Dao.RefreshCache refreshResultAnno = StreamEx.of(m.getAnnotations())
.select(Dao.RefreshCache.class)
.last()
.orElse((daoClassRefreshCacheAnno != null && N.anyMatch(daoClassRefreshCacheAnno.filter(), filterByMethodName))
? daoClassRefreshCacheAnno
: null);
if (cacheResultAnno != null && cacheResultAnno.disabled() == false) {
if (daoLogger.isDebugEnabled()) {
daoLogger.debug("Add CacheResult method: " + m);
}
if (Stream.of(notCacheableTypes).anyMatch(it -> it.isAssignableFrom(returnType))) {
throw new UnsupportedOperationException(
"The return type of method: " + simpleClassMethodName + " is not cacheable: " + m.getReturnType());
}
final String transferAttr = cacheResultAnno.transfer();
if (!(N.isNullOrEmpty(transferAttr) || N.asSet("none", "kryo").contains(transferAttr.toLowerCase()))) {
throw new UnsupportedOperationException(
"Unsupported 'cloneWhenReadFromCache' : " + transferAttr + " in annotation 'CacheResult' on method: " + simpleClassMethodName);
}
final Function cloneFunc = N.isNullOrEmpty(transferAttr) || "none".equalsIgnoreCase(transferAttr) ? Fn.identity() : r -> {
if (r == null) {
return r;
} else if (isValuePresentMap.getOrDefault(r.getClass(), Fn.alwaysFalse()).test(r) == false) {
return r;
} else {
return kryoParser.clone(r);
}
};
final Throwables.BiFunction temp = call;
call = (proxy, args) -> {
final String cachekey = createCacheKey(m, fullClassMethodName, args, daoLogger);
Object result = N.isNullOrEmpty(cachekey) ? null : cache.gett(cachekey);
if (result != null) {
return cloneFunc.apply(result);
}
result = temp.apply(proxy, args);
if (N.notNullOrEmpty(cachekey) && result != null) {
if (result instanceof DataSet) {
final DataSet dataSet = (DataSet) result;
if (dataSet.size() >= cacheResultAnno.minSize() && dataSet.size() <= cacheResultAnno.maxSize()) {
cache.put(cachekey, cloneFunc.apply(result), cacheResultAnno.liveTime(), cacheResultAnno.idleTime());
}
} else if (result instanceof Collection) {
final Collection c = (Collection) result;
if (c.size() >= cacheResultAnno.minSize() && c.size() <= cacheResultAnno.maxSize()) {
cache.put(cachekey, cloneFunc.apply(result), cacheResultAnno.liveTime(), cacheResultAnno.idleTime());
}
} else {
cache.put(cachekey, cloneFunc.apply(result), cacheResultAnno.liveTime(), cacheResultAnno.idleTime());
}
}
return result;
};
hasCacheResult.setTrue();
}
if (refreshResultAnno != null && refreshResultAnno.disabled() == false) {
if (daoLogger.isDebugEnabled()) {
daoLogger.debug("Add RefreshCache method: " + m);
}
final Throwables.BiFunction temp = call;
call = (proxy, args) -> {
cache.clear();
return temp.apply(proxy, args);
};
hasRefreshCache.setTrue();
}
final List> handlerList = StreamEx.of(m.getAnnotations())
.filter(anno -> anno.annotationType().equals(Dao.Handler.class) || anno.annotationType().equals(DaoUtil.HandlerList.class))
.flattMap(anno -> anno.annotationType().equals(Dao.Handler.class) ? N.asList((Dao.Handler) anno)
: N.asList(((DaoUtil.HandlerList) anno).value()))
.prepend(StreamEx.of(daoClassHandlerList).filter(h -> StreamEx.of(h.filter()).anyMatch(filterByMethodName)))
.map(handlerAnno -> N.notNullOrEmpty(handlerAnno.qualifier()) ? HandlerFactory.get(handlerAnno.qualifier())
: HandlerFactory.getOrCreate(handlerAnno.type()))
.onEach(handler -> N.checkArgNotNull(handler,
"No handler found/registered with qualifier or type in class/method: " + fullClassMethodName))
.reversed()
.toList();
if (N.notNullOrEmpty(handlerList)) {
final Throwables.BiFunction temp = call;
final Tuple3>, Class>> methodSignature = Tuple.of(m, ImmutableList.of(m.getParameterTypes()),
m.getReturnType());
call = (proxy, args) -> {
for (JdbcUtil.Handler handler : handlerList) {
handler.beforeInvoke(proxy, args, methodSignature);
}
Result, Exception> result = null;
Exception ex = null;
try {
result = Result.of(temp.apply(proxy, args), null);
} catch (Exception e) {
result = Result.of(null, e);
ex = e;
} finally {
try {
for (JdbcUtil.Handler handler : handlerList) {
handler.afterInvoke(result, proxy, args, methodSignature);
}
} catch (Exception e) {
if (ex != null) {
ex.addSuppressed(e);
} else {
result = Result.of(null, e);
ex = e;
}
}
}
return result.orElseThrow();
};
}
}
methodInvokerMap.put(m, call);
}
if (hasRefreshCache.isTrue() && hasCacheResult.isFalse()) {
throw new UnsupportedOperationException("Class: " + daoInterface
+ " or its super interfaces or methods are annotated by @RefreshCache, but none of them is annotated with @CacheResult. "
+ "Please remove the unnecessary @RefreshCache annotations or Add @CacheResult annotation if it's really needed.");
}
final Throwables.TriFunction proxyInvoker = (proxy, method, args) -> methodInvokerMap.get(method)
.apply(proxy, args);
final Class[] interfaceClasses = N.asArray(daoInterface);
final InvocationHandler h = (proxy, method, args) -> {
if (daoLogger.isDebugEnabled() && !nonDBOperationSet.contains(method)) {
daoLogger.debug("Invoking Dao method: {} with args: {}", method.getName(), args);
}
return proxyInvoker.apply((JdbcUtil.Dao) proxy, method, args);
};
daoInstance = N.newProxyInstance(interfaceClasses, h);
daoPool.put(key, daoInstance);
return daoInstance;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
static @interface OutParameterList {
Dao.OutParameter[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.TYPE })
static @interface HandlerList {
Dao.Handler[] value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD })
static @interface NonDBOperation {
}
static final class QueryInfo {
final String sql;
final int queryTimeout;
final int fetchSize;
final boolean isBatch;
final int batchSize;
final OP op;
final boolean isSingleParameter;
QueryInfo(final String sql, final int queryTimeout, final int fetchSize, final boolean isBatch, final int batchSize, final OP op,
final boolean isSingleParameter) {
this.sql = sql;
this.queryTimeout = queryTimeout;
this.fetchSize = fetchSize;
this.isBatch = isBatch;
this.batchSize = batchSize;
this.op = op;
this.isSingleParameter = isSingleParameter;
}
}
}