package org.simpleflatmapper.jdbc; import org.simpleflatmapper.jdbc.impl.PreparedStatementIndexedSetterFactory; import org.simpleflatmapper.map.MappingContext; import org.simpleflatmapper.reflect.getter.GetterFactory; import org.simpleflatmapper.jdbc.impl.JdbcColumnKeyMapperKeyComparator; import org.simpleflatmapper.jdbc.impl.PreparedStatementSetterFactory; import org.simpleflatmapper.map.FieldMapper; import org.simpleflatmapper.map.SetRowMapper; import org.simpleflatmapper.map.property.FieldMapperColumnDefinition; import org.simpleflatmapper.reflect.Getter; import org.simpleflatmapper.map.mapper.AbstractMapperFactory; import org.simpleflatmapper.map.mapper.ConstantTargetFieldMapperFactoryImpl; import org.simpleflatmapper.map.mapper.DynamicSetRowMapper; import org.simpleflatmapper.map.mapper.FieldMapperColumnDefinitionProviderImpl; import org.simpleflatmapper.map.mapper.MapperKey; import org.simpleflatmapper.util.TypeHelper; import org.simpleflatmapper.util.TypeReference; import org.simpleflatmapper.reflect.meta.ClassMeta; import org.simpleflatmapper.util.UnaryFactory; import org.simpleflatmapper.util.UnaryFactoryWithException; import java.lang.reflect.Type; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; /** * JdbcMapperFactory allows you to customise the mappers and create an newInstance of it using a fluent syntax. * <p> * JdbcMapperFactory is not Thread-Safe but the mappers are. * It is strongly advised to instantiate one jdbcMapper per class for the life of your application. * <p> * You can instantiate dynamic jdbcMapper which will use the ResultSetMetaData * to figure out the list of the columns or a static one using a builder. * <p> * <code> * // create a dynamic jdbcMapper targeting MyClass<br> * JdbcMapperFactory<br> *     .newInstance()<br> *     .newMapper(MyClass.class);<br> * <br> * // create a static jdbcMapper targeting MyClass<br> * JdbcMapperFactory<br> *     .newInstance()<br> *     .newBuilder(MyClass.class)<br> *     .addMapping("id")<br> *     .addMapping("field1")<br> *     .addMapping("field2")<br> *     .mapper();<br> * <br> * </code> * */ public final class JdbcMapperFactory extends AbstractMapperFactory<JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>, JdbcMapperFactory> { /** * instantiate a new JdbcMapperFactory * @return a new newInstance JdbcMapperFactory */ public static JdbcMapperFactory newInstance() { return new JdbcMapperFactory(); } public static JdbcMapperFactory newInstance( AbstractMapperFactory<JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>, ?> config) { return new JdbcMapperFactory(config); } private GetterFactory<ResultSet, JdbcColumnKey> getterFactory = ResultSetGetterFactory.INSTANCE; private JdbcMapperFactory(AbstractMapperFactory<JdbcColumnKey, FieldMapperColumnDefinition<JdbcColumnKey>, ?> config) { super(config); } private JdbcMapperFactory() { super(new FieldMapperColumnDefinitionProviderImpl<JdbcColumnKey>(), FieldMapperColumnDefinition.<JdbcColumnKey>identity()); } /** * Override the default implementation of the GetterFactory used to get access to value from the ResultSet. * @param getterFactory the getterFactory * @return the current factory */ public JdbcMapperFactory getterFactory(final GetterFactory<ResultSet, JdbcColumnKey> getterFactory) { this.getterFactory = getterFactory; return this; } /** * Associate the specified FieldMapper for the specified property. * @param key the property * @param fieldMapper the fieldMapper * @return the current factory */ public JdbcMapperFactory addCustomFieldMapper(String key, FieldMapper<ResultSet, ?> fieldMapper) { return addColumnDefinition(key, FieldMapperColumnDefinition.<JdbcColumnKey>customFieldMapperDefinition(fieldMapper)); } /** * Associate the specified Getter for the specified property. * @param key the property * @param getter the getter * @return the current factory */ public JdbcMapperFactory addCustomGetter(String key, Getter<ResultSet, ?> getter) { return addColumnDefinition(key, FieldMapperColumnDefinition.<JdbcColumnKey>customGetter(getter)); } /** * Will create a newInstance of JdbcMapper based on the specified metadata and the target class. * @param target the target class of the jdbcMapper * @param metaData the metadata to create the jdbcMapper from * @param <T> the jdbcMapper target type * @return a jdbcMapper that will map the data represented by the metadata to an newInstance of target * @throws java.sql.SQLException if an error occurs getting the metaData */ public <T> JdbcMapper<T> newMapper(final Class<T> target, final ResultSetMetaData metaData) throws SQLException { JdbcMapperBuilder<T> builder = newBuilder(target); builder.addMapping(metaData); return builder.mapper(); } /** * Will create a newInstance of JdbcMapperBuilder on the specified target class. * @param target the target class * @param <T> the jdbcMapper target type * @return the builder */ public <T> JdbcMapperBuilder<T> newBuilder(final Class<T> target) { return newBuilder((Type)target); } /** * Will create a newInstance of JdbcMapperBuilder on the type T specified by the typeReference. * @param target the typeReference * @param <T> the jdbcMapper target type * @return the builder */ public <T> JdbcMapperBuilder<T> newBuilder(final TypeReference<T> target) { return newBuilder(target.getType()); } /** * Will create a newInstance of JdbcMapperBuilder on the specified type. * @param target the type * @param <T> the jdbcMapper target type * @return the builder */ public <T> JdbcMapperBuilder<T> newBuilder(final Type target) { ClassMeta<T> classMeta = getClassMeta(target); return newBuilder(classMeta); } public <T> JdbcMapperBuilder<T> newBuilder(ClassMeta<T> classMeta) { return new JdbcMapperBuilder<T>( classMeta, mapperConfig(), getterFactory, new JdbcMappingContextFactoryBuilder()); } /** * * @param target the type * @param <T> the type * @return a builder to create a mapper from target to PreparedStatement */ public <T> PreparedStatementMapperBuilder<T> buildFrom(final Class<T> target) { return buildFrom((Type) target); } public <T> PreparedStatementMapperBuilder<T> buildFrom(final Type target) { ClassMeta<T> classMeta = getClassMeta(target); return buildFrom(classMeta); } public <T> PreparedStatementMapperBuilder<T> buildFrom(final TypeReference<T> target) { return buildFrom(target.getType()); } public <T> PreparedStatementMapperBuilder<T> buildFrom(final ClassMeta<T> classMeta) { return new PreparedStatementMapperBuilder<T>(classMeta, mapperConfig(), ConstantTargetFieldMapperFactoryImpl.newInstance(PreparedStatementSetterFactory.INSTANCE, PreparedStatement.class)); } public <T> PreparedStatementMapperBuilder<T> from(final Class<T> target) { return buildFrom(target); } public <T> PreparedStatementMapperBuilder<T> from(final Type target) { return buildFrom(target); } public <T> PreparedStatementMapperBuilder<T> from(final TypeReference<T> target) { return buildFrom(target); } public <T> PreparedStatementMapperBuilder<T> from(final ClassMeta<T> classMeta) { return buildFrom(classMeta); } /** * Will create a DynamicMapper on the specified target class. * @param target the class * @param <T> the jdbcMapper target type * @return the DynamicMapper */ public <T> DynamicJdbcMapper<T> newMapper(final Class<T> target) { return newMapper((Type) target); } /** * Will create a DynamicMapper on the type specified by the TypeReference. * @param target the TypeReference * @param <T> the jdbcMapper target type * @return the DynamicMapper */ public <T> DynamicJdbcMapper<T> newMapper(final TypeReference<T> target) { return newMapper(target.getType()); } public <T, K> CrudDSL<T, K> crud(final Type target, final Type keyTarget) { return crud(this.<T>getClassMeta(target), this.<K>getClassMeta(TypeHelper.toBoxedClass(keyTarget))); } public <T, K> CrudDSL<T, K> crud(final ClassMeta<T> target, final ClassMeta<K> keyTarget) { return new CrudDSL<T, K>(target, keyTarget, JdbcMapperFactory.newInstance(this)); } public <T, K> CrudDSL<T, K> crud(final Class<T> target, final Class<K> keyTarget) { return crud((Type)target, (Type)keyTarget); } /** * Will create a DynamicMapper on the specified type. * @param target the type * @param <T> the jdbcMapper target type * @return the DynamicMapper */ public <T> DynamicJdbcMapper<T> newMapper(final Type target) { final ClassMeta<T> classMeta = getClassMeta(target); return new DynamicJdbcSetRowMapper<T>(new SetRowMapperFactory<T>(classMeta), new MapperKeyFactory(), new MapperKeyFactory()); } public static class DynamicJdbcSetRowMapper<T> extends DynamicSetRowMapper<ResultSet, ResultSet, T, SQLException, JdbcColumnKey> implements DynamicJdbcMapper<T> { public DynamicJdbcSetRowMapper( UnaryFactory<MapperKey<JdbcColumnKey>, SetRowMapper<ResultSet, ResultSet, T, SQLException>> mapperFactory, UnaryFactoryWithException<ResultSet, MapperKey<JdbcColumnKey>, SQLException> mapperKeyFromRow, UnaryFactoryWithException<ResultSet, MapperKey<JdbcColumnKey>, SQLException> mapperKeyFromSet) { super(mapperFactory, mapperKeyFromRow, mapperKeyFromSet, JdbcColumnKeyMapperKeyComparator.INSTANCE); } @Override public JdbcMapper<T> getMapper(ResultSetMetaData metaData) throws SQLException { return (JdbcMapper<T>) getMapper(JdbcColumnKey.mapperKey(metaData)); } @Override public String toString() { return "DynamicJdbcSetRowMapper{}"; } @Override public MappingContext<? super ResultSet> newMappingContext(ResultSet resultSet) throws SQLException { return getMapper(resultSet.getMetaData()).newMappingContext(resultSet); } } /** * Create a discriminator builder based on the specified property * @param column the discriminator property * @param <T> the root type of the jdbcMapper * @return a builder to specify the type mapping */ public <T> DiscriminatorJdbcBuilder<T> newDiscriminator(String column) { ignorePropertyNotFound(); addColumnDefinition(column, FieldMapperColumnDefinition.<JdbcColumnKey>ignoreDefinition()); return new DiscriminatorJdbcBuilder<T>(column, this); } private static class MapperKeyFactory implements UnaryFactoryWithException<ResultSet, MapperKey<JdbcColumnKey>, SQLException> { @Override public MapperKey<JdbcColumnKey> newInstance(ResultSet set) throws SQLException { return JdbcColumnKey.mapperKey(set.getMetaData()); } } private class SetRowMapperFactory<T> implements UnaryFactory<MapperKey<JdbcColumnKey>, SetRowMapper<ResultSet,ResultSet,T,SQLException>> { private final ClassMeta<T> classMeta; public SetRowMapperFactory(ClassMeta<T> classMeta) { this.classMeta = classMeta; } @Override public SetRowMapper<ResultSet,ResultSet,T,SQLException> newInstance(MapperKey<JdbcColumnKey> jdbcColumnKeyMapperKey) { final JdbcMapperBuilder<T> builder = newBuilder(classMeta); for(JdbcColumnKey key : jdbcColumnKeyMapperKey.getColumns()) { builder.addMapping(key); } return builder.mapper(); } } }