org.sfm.jdbc.ResultSetMapperBuilderImpl Maven / Gradle / Ivy
package org.sfm.jdbc;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.sfm.map.FieldMapper;
import org.sfm.map.FieldMapperErrorHandler;
import org.sfm.map.FieldMapperImpl;
import org.sfm.map.MapperBuilderErrorHandler;
import org.sfm.map.MapperBuildingException;
import org.sfm.map.RethrowFieldMapperErrorHandler;
import org.sfm.map.RethrowMapperBuilderErrorHandler;
import org.sfm.reflect.ClassMeta;
import org.sfm.reflect.ConstructorPropertyMeta;
import org.sfm.reflect.Getter;
import org.sfm.reflect.Instantiator;
import org.sfm.reflect.InstantiatorFactory;
import org.sfm.reflect.PropertyFinder;
import org.sfm.reflect.PropertyMeta;
import org.sfm.reflect.ReflectionService;
import org.sfm.reflect.Setter;
import org.sfm.reflect.SubPropertyMeta;
import org.sfm.reflect.asm.ConstructorParameter;
public final class ResultSetMapperBuilderImpl implements ResultSetMapperBuilder {
private FieldMapperErrorHandler fieldMapperErrorHandler = new RethrowFieldMapperErrorHandler();
private MapperBuilderErrorHandler mapperBuilderErrorHandler = new RethrowMapperBuilderErrorHandler();
private JdbcMapperErrorHandler jdbcMapperErrorHandler = new RethrowJdbcMapperErrorHandler();
private final Class target;
private final PrimitiveFieldMapperFactory primitiveFieldMapperFactory;
private final PropertyFinder propertyFinder;
private final ReflectionService reflectionService;
private final List> fields = new ArrayList>();
private final Map> constructorInjections;
private final List> subProperties = new ArrayList<>();
private final Map aliases;
private final Map> customMappings;
private int columnIndex = 1;
public ResultSetMapperBuilderImpl(final Class target) throws MapperBuildingException {
this(target, new ReflectionService());
}
public ResultSetMapperBuilderImpl(final Class target, ReflectionService reflectService) throws MapperBuildingException {
this(target, reflectService, null, null);
}
public ResultSetMapperBuilderImpl(final Class target, ReflectionService reflectService, final Map aliases, Map> customMappings) throws MapperBuildingException {
this(target, new ClassMeta(target, reflectService), aliases, customMappings);
}
public ResultSetMapperBuilderImpl(final Class target, final ClassMeta classMeta, final Map aliases, Map> customMappings) throws MapperBuildingException {
this.target = target;
this.reflectionService = classMeta.getReflectionService();
this.primitiveFieldMapperFactory = new PrimitiveFieldMapperFactory<>(reflectionService.getSetterFactory());
this.constructorInjections = new HashMap>();
this.propertyFinder = new PropertyFinder<>(classMeta);
this.aliases = aliases;
this.customMappings = customMappings;
}
@Override
public final ResultSetMapperBuilder fieldMapperErrorHandler(final FieldMapperErrorHandler errorHandler) {
if (!fields.isEmpty()) {
throw new IllegalStateException(
"Error Handler need to be set before adding fields");
}
fieldMapperErrorHandler = errorHandler;
return this;
}
@Override
public final ResultSetMapperBuilder mapperBuilderErrorHandler(final MapperBuilderErrorHandler errorHandler) {
mapperBuilderErrorHandler = errorHandler;
return this;
}
@Override
public final ResultSetMapperBuilder addNamedColumn(final String column) {
return addNamedColumn(column, ResultSetGetterFactory.UNDEFINED);
}
@Override
public final ResultSetMapperBuilder addIndexedColumn(final String column) {
return addIndexedColumn(column, columnIndex ++);
}
@Override
public final ResultSetMapperBuilder addIndexedColumn(final String column, final int columnIndex) {
return addIndexedColumn(column, columnIndex, ResultSetGetterFactory.UNDEFINED);
}
@Override
public final ResultSetMapperBuilder addMapping(final String property, final String column) {
return addMapping(property, column, ResultSetGetterFactory.UNDEFINED);
}
@Override
public final ResultSetMapperBuilder addMapping(final String property, final int column) {
return addMapping(property, column, ResultSetGetterFactory.UNDEFINED);
}
@Override
public final ResultSetMapperBuilder addNamedColumn(final String column, final int sqlType) {
return addMapping(columnToPropertyName(column), column, sqlType);
}
private String columnToPropertyName(String column) {
if (aliases == null || aliases.isEmpty()) {
return column;
}
String alias = aliases.get(column.toUpperCase());
if (alias == null) {
return column;
}
return alias;
}
@Override
public final ResultSetMapperBuilder addIndexedColumn(final String column, final int columnIndex, final int sqlType) {
return addMapping(columnToPropertyName(column), column, columnIndex, sqlType);
}
@Override
public final ResultSetMapperBuilder addMapping(final String propertyName, final int columnIndex, final int sqlType) {
return addMapping(propertyName, "column:"+ columnIndex, columnIndex, sqlType);
}
@Override
public final ResultSetMapperBuilder addMapping(final String propertyName, final String columnName, final int sqlType) {
FieldMapper fieldMapper = getCustomMapper(columnName);
if (fieldMapper != null) {
fields.add(fieldMapper);
} else {
final PropertyMeta property = propertyFinder.findProperty(propertyName);
if (property == null) {
mapperBuilderErrorHandler.setterNotFound(target, propertyName);
} else {
addMapping(property, columnName, sqlType);
}
}
return this;
}
public final ResultSetMapperBuilder addMapping(final String propertyName, final String columnName, final int columnIndex, final int sqlType) {
FieldMapper fieldMapper = getCustomMapper(columnName);
if (fieldMapper != null) {
fields.add(fieldMapper);
} else {
final PropertyMeta property = propertyFinder.findProperty(propertyName);
if (property == null) {
mapperBuilderErrorHandler.setterNotFound(target, propertyName);
} else {
addMapping(property, columnName, columnIndex, sqlType);
}
}
return this;
}
@SuppressWarnings("unchecked")
public FieldMapper getCustomMapper(final String columnName) {
return customMappings != null ? (FieldMapper) customMappings.get(columnName.toUpperCase()) : null;
}
@Override
public final ResultSetMapperBuilder addMapping(final ResultSetMetaData metaData) throws SQLException {
for(int i = 1; i <= metaData.getColumnCount(); i++) {
addIndexedColumn(metaData.getColumnLabel(i), i, metaData.getColumnType(i));
}
return this;
}
@Override
public final JdbcMapper mapper() throws MapperBuildingException {
if (reflectionService.isAsmActivated()) {
try {
return reflectionService.getAsmFactory().createJdbcMapper(fields(), getInstantiator(), target, jdbcMapperErrorHandler);
} catch(Exception e) {
return new JdbcMapperImpl(fields(), getInstantiator(), jdbcMapperErrorHandler);
}
} else {
return new JdbcMapperImpl(fields(), getInstantiator(), jdbcMapperErrorHandler);
}
}
private Instantiator getInstantiator() throws MapperBuildingException {
InstantiatorFactory instantiatorFactory = reflectionService.getInstantiatorFactory();
if (!reflectionService.isAsmPresent()) {
try {
return instantiatorFactory.getInstantiator(ResultSet.class, target);
} catch(Exception e) {
throw new MapperBuildingException(e.getMessage(), e);
}
} else {
try {
return instantiatorFactory.getInstantiator(ResultSet.class, propertyFinder.getEligibleConstructorDefinitions(), constructorInjections());
} catch(Exception e) {
throw new MapperBuildingException(e.getMessage(), e);
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map> constructorInjections() {
Map> injections = new HashMap<>(constructorInjections);
for(SubProperty subProp : subProperties) {
PropertyMeta prop = subProp.subProperty.getProperty();
if (prop instanceof ConstructorPropertyMeta) {
ResultSetMapperBuilderImpl> builder = subProp.mapperBuilder;
final JdbcMapper mapper = (JdbcMapper) builder.mapper();
Getter getter = new JdbcMapperGetterAdapter<>(mapper);
injections.put(((ConstructorPropertyMeta) prop).getConstructorParameter(), getter);
}
}
return injections;
}
public final Class getTarget() {
return target;
}
@Override
@SuppressWarnings("unchecked")
public final FieldMapper[] fields() {
List> fields = new ArrayList<>(this.fields);
for(SubProperty subProp : subProperties) {
PropertyMeta prop = subProp.subProperty.getProperty();
if (!(prop instanceof ConstructorPropertyMeta)) {
Setter setter = (Setter) prop.getSetter();
JdbcMapper mapper = (JdbcMapper) subProp.mapperBuilder.mapper();
Getter getter = new JdbcMapperGetterAdapter<>(mapper);
fields.add(new FieldMapperImpl(prop.getName(), getter, setter, fieldMapperErrorHandler));
}
}
return fields.toArray(new FieldMapper[fields.size()]);
}
private SubProperty getOrAddSubPropertyMapperBuilder(SubPropertyMeta property) {
for(SubProperty subProp : subProperties) {
if (subProp.subProperty.getName().equals(property.getName())) {
return subProp;
}
}
ResultSetMapperBuilderImpl builder = new ResultSetMapperBuilderImpl<>(property.getType(), property.getClassMeta(), aliases, customMappings);
SubProperty subProp = new SubProperty(builder, property);
subProperties.add(subProp);
return subProp;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void addMapping(PropertyMeta property, String column, int sqlType) {
if (property instanceof ConstructorPropertyMeta) {
ConstructorParameter constructorParameter = ((ConstructorPropertyMeta) property).getConstructorParameter();
constructorInjections.put(constructorParameter, ResultSetGetterFactory.newGetter(constructorParameter.getType(), column, sqlType));
} else if (property instanceof SubPropertyMeta) {
SubProperty subProp = getOrAddSubPropertyMapperBuilder((SubPropertyMeta)property);
subProp.mapperBuilder.addMapping(((SubPropertyMeta) property).getSubProperty(), column, sqlType);
} else if (property.getType().isPrimitive()) {
FieldMapper fieldMapper = primitiveFieldMapperFactory.primitiveFieldMapper(column, property.getSetter(), column, fieldMapperErrorHandler);
fields.add(fieldMapper);
} else {
FieldMapper fieldMapper = objectFieldMapper(column, property.getSetter(), sqlType);
fields.add(fieldMapper);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private void addMapping(PropertyMeta property, String columnName, int column, int sqlType) {
if (property instanceof ConstructorPropertyMeta) {
ConstructorParameter constructorParameter = ((ConstructorPropertyMeta) property).getConstructorParameter();
constructorInjections.put(constructorParameter, ResultSetGetterFactory.newGetter(constructorParameter.getType(), column, sqlType));
} else if (property instanceof SubPropertyMeta) {
SubProperty subProp = getOrAddSubPropertyMapperBuilder((SubPropertyMeta)property);
subProp.mapperBuilder.addMapping(((SubPropertyMeta) property).getSubProperty(), columnName, column, sqlType);
} else if (property.getType().isPrimitive()) {
FieldMapper fieldMapper = primitiveFieldMapperFactory.primitiveFieldMapper(column, property.getSetter(), columnName, fieldMapperErrorHandler);
fields.add(fieldMapper);
} else {
FieldMapper fieldMapper = objectFieldMapper(columnName, column, property.getSetter(), sqlType);
fields.add(fieldMapper);
}
}
@SuppressWarnings("unchecked")
private FieldMapper objectFieldMapper(String column, Setter setter, int sqlType) {
Class extends Object> type = setter.getPropertyType();
Getter getter = ResultSetGetterFactory.newGetter(type, column, sqlType);
if (getter == null) {
mapperBuilderErrorHandler.getterNotFound("No getter for column " + column + " type " + type);
return null;
} else {
return new FieldMapperImpl(column, getter, (Setter) setter, fieldMapperErrorHandler);
}
}
@SuppressWarnings("unchecked")
private FieldMapper objectFieldMapper(String columnName, int column, Setter setter, int sqlType) {
Class extends Object> type = setter.getPropertyType();
Getter getter = ResultSetGetterFactory.newGetter(type, column, sqlType);
if (getter == null) {
mapperBuilderErrorHandler.getterNotFound("No getter for column " + columnName + " type " + type);
return null;
} else {
return new FieldMapperImpl(columnName, getter, (Setter) setter, fieldMapperErrorHandler);
}
}
private static class SubProperty {
final ResultSetMapperBuilderImpl> mapperBuilder;
final SubPropertyMeta subProperty;
SubProperty(ResultSetMapperBuilderImpl> mapperBuilder,
SubPropertyMeta subProperty) {
this.mapperBuilder = mapperBuilder;
this.subProperty = subProperty;
}
}
@Override
public ResultSetMapperBuilder addMapper(FieldMapper mapper) {
fields.add(mapper);
return this;
}
} © 2015 - 2025 Weber Informatics LLC | Privacy Policy