org.jdbi.v3.sqlobject.SqlObjectFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdbi3-sqlobject Show documentation
Show all versions of jdbi3-sqlobject Show documentation
SqlObject is a declarative, annotation-driven API
for database access. It complements the core API.
Jdbi 3 is designed to provide convenient tabular data access in
Java(tm) and other JVM based languages.
It uses the Java collections framework for query results,
provides a convenient means of externalizing SQL statements, and
named parameter support for any database that supports JDBC.
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jdbi.v3.sqlobject;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.config.JdbiCache;
import org.jdbi.v3.core.config.JdbiCaches;
import org.jdbi.v3.core.extension.ExtensionFactory;
import org.jdbi.v3.core.extension.Extensions;
import org.jdbi.v3.core.extension.HandleSupplier;
import org.jdbi.v3.core.internal.OnDemandExtensions;
import org.jdbi.v3.sqlobject.config.Configurer;
import org.jdbi.v3.sqlobject.config.ConfiguringAnnotation;
import org.jdbi.v3.sqlobject.internal.SqlObjectInitData;
import org.jdbi.v3.sqlobject.internal.SqlObjectInitData.InContextInvoker;
/**
* Creates implementations for SqlObject interfaces.
*/
public class SqlObjectFactory implements ExtensionFactory, OnDemandExtensions.Factory {
private final JdbiCache, SqlObjectInitData> sqlObjectCache =
JdbiCaches.declare(SqlObjectFactory::initDataFor);
SqlObjectFactory() {}
@Override
public boolean accepts(Class> extensionType) {
if (looksLikeSqlObject(extensionType)) {
if (extensionType.getAnnotation(GenerateSqlObject.class) != null) {
return true;
}
if (!extensionType.isInterface()) {
throw new IllegalArgumentException("SQL Objects are only supported for interfaces.");
}
return true;
}
return false;
}
private boolean looksLikeSqlObject(Class> extensionType) {
if (SqlObject.class.isAssignableFrom(extensionType)) {
return true;
}
return Stream.of(extensionType.getMethods())
.flatMap(m -> Stream.of(m.getAnnotations()))
.anyMatch(a -> a.annotationType().isAnnotationPresent(SqlOperation.class));
}
/**
* Create a sql object of the specified type bound to this handle. Any state changes to the handle, or the sql
* object, such as transaction status, closing it, etc, will apply to both the object and the handle.
*
* @param extensionType the type of sql object to create
* @param handle the Handle instance to attach ths sql object to
* @return the new sql object bound to this handle
*/
@Override
public E attach(Class extensionType, HandleSupplier handle) {
final SqlObjectInitData data = sqlObjectCache.get(extensionType, handle.getConfig());
final ConfigRegistry instanceConfig = data.configureInstance(handle.getConfig().createCopy());
if (data.isConcrete()) {
return data.instantiate(extensionType, handle, instanceConfig);
}
instanceConfig.get(Extensions.class).onCreateProxy();
Map> handlers = new HashMap<>();
final Object proxy = Proxy.newProxyInstance(
extensionType.getClassLoader(),
new Class[] {extensionType},
(p, m, a) -> handlers.get(m).get().invoke(a));
data.forEachMethodHandler((m, h) ->
handlers.put(m, data.lazyInvoker(proxy, m, handle, instanceConfig)));
return extensionType.cast(proxy);
}
@Override
public Optional