I work on an application that has been converted from pure JDBC to Spring template with row mapper. The issue that I have is that the column in database doesn't match the property names which prevent me from using BeanPropertyRowMapper easily. 
I saw some posts about using aliases in queries. This would work but it makes it impossible to do a SELECT *
Isn't there any annotation that can be used with BeanPropertyRowMapper as @Column from JPA?
I saw Some posts about using aliases in queries
This is actually an approach suggested in JavaDocs:
To facilitate mapping between columns and fields that don't have matching names, try using column aliases in the SQL statement like "select fname as first_name from customer".
From: BeanPropertyRowMapper.
impossible to do a SELECT *
Please do not use SELECT *. This makes you vulnerable to any database schema change, including completely backward compatible ones like adding or rearranging columns.
Isn't there any annotation that can be used with BeanPropertyRowMapper as @Column from JPA?
Yes, it is called jpa, hibernate and maybe ibatis. Seriously, either use aliases or implement your own RowMapper, Spring is not a full-featured orm.
You can override the BeanPropertyRowMapper.underscoreName, and get the name of the Column annotation to mapping the field with @Column(name = "EXAMPLE_KEY") in the PropertyDescriptor(getter/setter binding).
@Slf4j
public class ColumnRowMapper<T> extends BeanPropertyRowMapper<T> {
  private ColumnRowMapper(final Class<T> mappedClass)
  {
    super(mappedClass);
  }
  @Override
  protected String underscoreName(final String name)
  {
    final Column annotation;
    final String columnName;
    Field declaredField = null;
    try
    {
      declaredField = getMappedClass().getDeclaredField(name);
    }
    catch (NoSuchFieldException | SecurityException e)
    {
      log.warn("Ups, field «{}» not found in «{}».", name, getMappedClass());
    }
    if (declaredField == null || (annotation = declaredField.getAnnotation(Column.class)) == null
        || StringUtils.isEmpty(columnName = annotation.name()))
    {
      return super.underscoreName(name);
    }
    return StringUtils.lowerCase(columnName);
  }
  /**
   * New instance.
   *
   * @param <T> the generic type
   * @param mappedClass the mapped class
   * @return the bean property row mapper
   */
  public static <T> BeanPropertyRowMapper<T> newInstance(final Class<T> mappedClass)
  {
    return new ColumnRowMapper<>(mappedClass);
  }
}
                        A version of above mapper but with early initiation of mapping index, since reflection is way too slow:
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.Column;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
@Slf4j
public class ColumnRowMapper<T> extends BeanPropertyRowMapper<T> {
    private Map<String, String> columnIndex;
    
    private ColumnRowMapper(final Class<T> mappedClass)
    {
        super(mappedClass);
    }
    @Override
    protected void initialize(Class<T> mappedClass) {
        columnIndex = new ConcurrentHashMap<>();
        for (Field f: mappedClass.getDeclaredFields()) {
            String fieldName = f.getName();
            Column annotation = f.getAnnotation(Column.class);
            if (annotation == null) {
                continue;
            }
            String columnName = annotation.name();
            if (StringUtils.isEmpty(columnName)) {
                continue;
            }
            columnIndex.put(fieldName, StringUtils.lowerCase(columnName));
        }
        super.initialize(mappedClass);
    }
    @Override
    protected @NonNull String underscoreName(final @NonNull String name)
    {
        if (columnIndex.containsKey(name)) {
            return columnIndex.get(name);
        }
        return super.underscoreName(name);
    }
    /**
     * New instance.
     *
     * @param <T> the generic type
     * @param mappedClass the mapped class
     * @return the bean property row mapper
     */
    public static <T> BeanPropertyRowMapper<T> newInstance(final Class<T> mappedClass)
    {
        return new ColumnRowMapper<>(mappedClass);
    }
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With