/*
 * 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.core.mapper;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.config.JdbiConfig;
import org.jdbi.v3.core.generic.GenericType;
import org.jdbi.v3.core.internal.JdbiOptionals;
import org.jdbi.v3.core.mapper.reflect.internal.PojoMapperFactory;
import org.jdbi.v3.core.statement.Query;

Configuration registry for RowMapperFactory instances.
/** * Configuration registry for {@link RowMapperFactory} instances. */
public class RowMappers implements JdbiConfig<RowMappers> { private final List<RowMapperFactory> factories = new CopyOnWriteArrayList<>(); private final ConcurrentHashMap<Type, RowMapper<?>> cache = new ConcurrentHashMap<>(); private ConfigRegistry registry; public RowMappers() { register(MapEntryMapper.factory()); register(new PojoMapperFactory()); } private RowMappers(RowMappers that) { factories.addAll(that.factories); cache.putAll(that.cache); } @Override public void setRegistry(ConfigRegistry registry) { this.registry = registry; }
Register a row mapper which will have its parameterized type inspected to determine what it maps to. Will be used with ResultBearing.mapTo(Class<Object>) for registered mappings.

The parameter must be concretely parameterized, we use the type argument T to determine if it applies to a given type.

Params:
  • mapper – the row mapper
Throws:
Returns:this
/** * Register a row mapper which will have its parameterized type inspected to determine what it maps to. * Will be used with {@link Query#mapTo(Class)} for registered mappings. * <p> * The parameter must be concretely parameterized, we use the type argument T to * determine if it applies to a given type. * * @param mapper the row mapper * @return this * @throws UnsupportedOperationException if the RowMapper is not a concretely parameterized type */
public RowMappers register(RowMapper<?> mapper) { return this.register(new InferredRowMapperFactory(mapper)); }
Register a row mapper for a given type.
Params:
  • type – the type to match with equals.
  • mapper – the row mapper
Type parameters:
  • <T> – the type
Returns:this
/** * Register a row mapper for a given type. * * @param <T> the type * @param type the type to match with equals. * @param mapper the row mapper * @return this */
public <T> RowMappers register(GenericType<T> type, RowMapper<T> mapper) { return this.register(RowMapperFactory.of(type.getType(), mapper)); }
Register a row mapper for a given type.
Params:
  • type – the type to match with equals.
  • mapper – the row mapper
Returns:this
/** * Register a row mapper for a given type. * * @param type the type to match with equals. * @param mapper the row mapper * @return this */
public RowMappers register(Type type, RowMapper<?> mapper) { return this.register(RowMapperFactory.of(type, mapper)); }
Register a row mapper factory.

Will be used with ResultBearing.mapTo(Class<Object>) for registered mappings.

Params:
  • factory – the row mapper factory
Returns:this
/** * Register a row mapper factory. * <p> * Will be used with {@link Query#mapTo(Class)} for registered mappings. * * @param factory the row mapper factory * @return this */
public RowMappers register(RowMapperFactory factory) { factories.add(0, factory); cache.clear(); return this; }
Obtain a row mapper for the given type in the given context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type of the mapper to find
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in the given context. * * @param <T> the type of the mapper to find * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
@SuppressWarnings("unchecked") public <T> Optional<RowMapper<T>> findFor(Class<T> type) { RowMapper<T> mapper = (RowMapper<T>) findFor((Type) type).orElse(null); return Optional.ofNullable(mapper); }
Obtain a row mapper for the given type in the given context.
Params:
  • type – the target type to map to
Type parameters:
  • <T> – the type of the mapper to find
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in the given context. * * @param <T> the type of the mapper to find * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
@SuppressWarnings("unchecked") public <T> Optional<RowMapper<T>> findFor(GenericType<T> type) { RowMapper<T> mapper = (RowMapper<T>) findFor(type.getType()).orElse(null); return Optional.ofNullable(mapper); }
Obtain a row mapper for the given type in the given context.
Params:
  • type – the target type to map to
Returns:a RowMapper for the given type, or empty if no row mapper is registered for the given type.
/** * Obtain a row mapper for the given type in the given context. * * @param type the target type to map to * @return a RowMapper for the given type, or empty if no row mapper is registered for the given type. */
public Optional<RowMapper<?>> findFor(Type type) { // ConcurrentHashMap can enter an infinite loop on nested computeIfAbsent calls. // Since row mappers can decorate other row mappers, we have to populate the cache the old fashioned way. // See https://bugs.openjdk.java.net/browse/JDK-8062841, https://bugs.openjdk.java.net/browse/JDK-8142175 RowMapper<?> cached = cache.get(type); if (cached != null) { return Optional.of(cached); } Optional<RowMapper<?>> mapper = factories.stream() .flatMap(factory -> JdbiOptionals.stream(factory.build(type, registry))) .findFirst(); mapper.ifPresent(m -> cache.put(type, m)); return mapper; } @Override public RowMappers createCopy() { return new RowMappers(this); } }