arnaudroger / SimpleFlatMapper

Fast and Easy mapping from database and csv to POJO. A java micro ORM, lightweight alternative to iBatis and Hibernate. Fast Csv Parser and Csv Mapper
http://simpleflatmapper.org
MIT License
437 stars 76 forks source link

Converting "Y"/"N" to boolean values #656

Open Yuri-Harel opened 5 years ago

Yuri-Harel commented 5 years ago

Hey,

I use SFM in conjunction with JOOQ, and for whatever reason, boolean values are stored as a single character, either as "Y" or "N". I have tried to implement a converter this way :

public class StringToBoolConverterFactoryProducer extends AbstractConverterFactoryProducer {

  @Override
  public void produce(Consumer<? super ConverterFactory<?, ?>> consumer) {
    constantConverter(consumer, CharSequence.class, Boolean.class, new StringBoolConverter());
  }

  private class StringBoolConverter implements Converter<CharSequence, Boolean> {

    @Override
    public Boolean convert(CharSequence in) {

      if (in == null) {
        return false;
      }
      String inString = in.toString();
      return "Y".equalsIgnoreCase(inString) || Boolean.valueOf(inString);
    }
  }
}

As well as a reference to it in : org.simpleflatmapper.converter.ConverterFactoryProducer.

The converter is picked on, but it isn't being called. I have a similar issue with a converter I have tried to write that performs trim on every string, the converter being from CharSequence to String. I have also tried String to String.

Do you know what I am missing ? I'm assuming my converters are losing in priority or something similar.

arnaudroger commented 5 years ago

Yea the converter is not automatically picked on the best way is to provide getter for that field, I’ll had a sample for that later.

Yuri-Harel commented 5 years ago

I saw that works, but I need a converter on every String and Boolean field, that is manually defnining hundreds of properties, is there no other way ?

arnaudroger commented 5 years ago

Yes not really able to give you an example just at the minute, there is some new code in the last few commits that aims at making it easier jdbcgetterfactory... with a test as an example if you want to have a look

Yuri-Harel commented 5 years ago

I made it work with the JdbcGetterFactoryProperty, but I have stumbled on a limitation.

As far as I understand, we map ResultSet to type P, but there can only be one such mapping, no matter what the type P is, however I need several such mappings.

Because I want to map both all strings, and all booleans, the source of both is ResultSet, but the output is different. The only way to tell which converter to use, is by the type on the target. If the target has a property of type boolean, then use the StringToBolean converter, otherwise the StringToTrimmedString converter.

I will keep looking into this.

Yuri-Harel commented 5 years ago

Well, I ended with this for now :

public class JdbcGetterFactoryProperty {

  public static <T> GetterFactoryProperty complexConverter() {
    GetterFactory<ResultSet, JdbcColumnKey> setterFactory = new GetterFactory<>() {

      @Override
      public Getter<ResultSet, ? extends Object> newGetter(Type target, JdbcColumnKey key, Object... properties) {

        final int i = key.getIndex();

        if (target.equals(boolean.class)) {
          return new ResultSetGetterAdapter<Boolean>(SFMConverters::ynConverter, i);
        }

        if (target.equals(String.class)) {
          return new ResultSetGetterAdapter<String>(SFMConverters::stringTrimmer, i);
        }

        return null;
      }
    };
    return new GetterFactoryProperty(setterFactory);
  }
  public interface ResultSetGetter<T> {
    T get(ResultSet ps, int i) throws SQLException;
  }

  private static class ResultSetGetterAdapter<T> implements Getter<ResultSet, T> {
    private final ResultSetGetter<T> getter;
    private final int index;

    public ResultSetGetterAdapter(ResultSetGetter<T> getter, int index) {
      this.getter = getter;
      this.index = index;
    }

    @Override
    public T get(ResultSet target) throws Exception {
      return getter.get(target, index);
    }
  }
}
arnaudroger commented 5 years ago

yes that would be the idea, for now, will try to make it better. and your example will help. this kind of customisation is right now a bit laborious and not well documented .... it was raised a few weeks ago considering enums. my goal for the next release is to make that easier, that will be via helper method and dsl.

arnaudroger commented 5 years ago

following up on that was it mapping from a jooq record? what is the type of the Field in jooq? what's the type in the db? what db is it ?

Yuri-Harel commented 5 years ago

I was mapping from a ResultSet, because the query was a join of multiple tables, so no record existed, but I use JOOQ to generate the query. The field type in the result set in both cases was String. The DB is IBM Db2 for i.