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
435 stars 76 forks source link

Map Database Column of Array type to POJO #701

Open ch4nd4n opened 4 years ago

ch4nd4n commented 4 years ago

I am trying to map Postgres array(strings) column to array of strings in POJO - https://www.postgresql.org/docs/9.1/arrays.html to map to POJO

In POJO class

  private String[] mystrings;
  // getters and setters etc.

For column data like (postgres data below)

{"AA2891"}

By default it maps to something like

"mystrings": [ "{AA2891}" ],
...

instead of

"mystrings": [ "AA2891" ],
...

Referring to this Test case example - https://github.com/arnaudroger/SimpleFlatMapper/blob/7552995ae9a34ebef69a8a336b529b3095252058/sfm-map/src/test/java/org/simpleflatmapper/test/map/FieldMapperColumnDefinitionTest.java

To fix this I am doing something like

// static initializer
SelectQueryMapperFactory.newInstance()
            .addColumnProperty(
                k -> k.getName().equals("mystrings"), stringArrayGetterFactoryProperty())
            .newMapper(MyObject.class);

Following does not work

private GetterFactoryProperty stringArrayGetterFactoryProperty() {
    return GetterFactoryProperty.forType(
        String.class,
        new IndexedGetter<Object, String[]>() {
            // It never enters here.
          public String[] get(Object rs, int i) {
            try {
              String[] array = (String[]) ((ResultSet) rs).getArray(i).getArray();
              return array; 
            } catch (SQLException e) {
              e.printStackTrace();
            }
            return null;
          }
        });
  }

Changing above code to forType.(String.class ...

  private GetterFactoryProperty stringArrayGetterFactoryProperty() {
    return GetterFactoryProperty.forType(
        String.class,
        new IndexedGetter<Object, String[]>() {
          public String[] get(Object rs, int i) {
            try {
              String[] array = (String[]) ((ResultSet) rs).getArray(i).getArray();
              logger.debug("array: ", array);
              return array;
            } catch (SQLException e) {
              e.printStackTrace();
            }
            return null;
          }
        });
  }

Leads to following exception

java.lang.ArrayStoreException: [Ljava.lang.String;
    at org.simpleflatmapper.reflect.setter.IndexedObjectArraySetter.set(IndexedObjectArraySetter.java:14)
    at org.simpleflatmapper.reflect.setter.IndexedObjectArraySetter.set(IndexedObjectArraySetter.java:5)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:44)
    at org.simpleflatmapper.map.generated.none.AsmMapperFromResultSetToStrings_Inj1_I4.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.none.AsmMapperFromResultSetToStrings_Inj1_I4.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:40)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightConnectionsInj13_I5.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightConnectionsInj13_I5.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:40)
    at org.simpleflatmapper.map.generated.java.util.AsmMapperFromResultSetToListInj1_I6.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.java.util.AsmMapperFromResultSetToListInj1_I6.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:40)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightRoutesInj6_I7.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightRoutesInj6_I7.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:40)
    at org.simpleflatmapper.map.generated.java.util.AsmMapperFromResultSetToListInj1_I8.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.java.util.AsmMapperFromResultSetToListInj1_I8.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.fieldmapper.MapperFieldMapper.mapTo(MapperFieldMapper.java:40)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightResponsesInj4_I9.mapFields(Unknown Source)
    at org.simpleflatmapper.map.generated.com.juicymiles.data.v2.entities.AsmMapperFromResultSetToFlightResponsesInj4_I9.mapFields(Unknown Source)
    at org.simpleflatmapper.map.mapper.AbstractMapper.map(AbstractMapper.java:23)
    at org.simpleflatmapper.map.mapper.ContextualSourceFieldMapperImpl.map(ContextualSourceFieldMapperImpl.java:38)
    at org.simpleflatmapper.map.mapper.JoinMapperEnumerable.next(JoinMapperEnumerable.java:37)
    at org.simpleflatmapper.util.EnumerableIterator.fetch(EnumerableIterator.java:26)
    at org.simpleflatmapper.util.EnumerableIterator.next(EnumerableIterator.java:33)
    at org.simpleflatmapper.jooq.SelectQueryMapper$ExceptionTranslatorIterator.next(SelectQueryMapper.java:300)
    at org.simpleflatmapper.util.AutoCloseableIterator.next(AutoCloseableIterator.java:34)

Is there an example that I can refer to?

arnaudroger commented 4 years ago

Will have a look thanks, i think it work directly on a resultset so might be some type thingy not handle properly with jooq

ch4nd4n commented 4 years ago

Thanks, while debugging String[] array = (String[]) rs.getArray(i).getArray(); this resolves to correct array value that I need to set to POJO. Please refer to the code snippet above. I think there is some problem with flat mapper when target data type to be set is an array of objects. I am not able to nail it down where exactly its errors out. But seems to me like casting exception.

arnaudroger commented 4 years ago

so I did a quick test


    @Test
    public void testIssue701StringArray() throws SQLException {
        Connection conn = DbHelper.objectDb();

        Configuration cfg = new DefaultConfiguration()
                .set(conn)
                .set(SQLDialect.HSQLDB);

        DSLContext dsl = DSL.using(cfg);

        SelectSelectStep<Record1<String[]>> select = dsl.select(DSL.val(new String[]{"11", "22"}).as("values"));

        SelectQueryMapper<Issue701Pojo> mapper = SelectQueryMapperFactory.newInstance().newMapper(Issue701Pojo.class);

        List<Issue701Pojo> pojos = mapper.asList(select);

        assertEquals(Arrays.asList(new Issue701Pojo(new String[] {"11", "22"})), pojos);

    }

    public static class Issue701Pojo {
        public final String[] values;

        public Issue701Pojo(String[] values) {
            this.values = values;
        }

        @Override
        public String toString() {
            return "Issue701Pojo{" +
                    "values=" + Arrays.toString(values) +
                    '}';
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Issue701Pojo that = (Issue701Pojo) o;

            // Probably incorrect - comparing Object[] arrays with Arrays.equals
            return Arrays.equals(values, that.values);
        }

        @Override
        public int hashCode() {
            return Arrays.hashCode(values);
        }
    }

and it seems to be working fine. I would prob need more to find out actually what is happening, could you send me the query, the Jooq Model Objects in the query and the Pojos? arnaud.roger at gmail.com

arnaudroger commented 4 years ago

looking at the ArrayStoreException error it tries to set it at and index on the array instead of the array as such. it relates to the way the mapping as resolved prop -> column.

ch4nd4n commented 4 years ago

I am not sure if I understand it. Is there any additional info that you need from me that will help?

arnaudroger commented 4 years ago

yes I would need the list of field the select has, and the object it's mapping to

On Thu, Dec 26, 2019 at 6:38 AM Chandan Kumar notifications@github.com wrote:

I am not sure if I understand it. Is there any additional info that you need from me that will help?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/arnaudroger/SimpleFlatMapper/issues/701?email_source=notifications&email_token=ABPAJLMTWMM6BKGEC2MRJXLQ2RGPLA5CNFSM4J6SBJQKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHVBA6A#issuecomment-568987768, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABPAJLP5D7DHUSMOSWJYTWTQ2RGPLANCNFSM4J6SBJQA .

ch4nd4n commented 4 years ago

I will need sometime to pull out relevant code(I can't post the code as is), I will update you in a couple of days time. Thanks for looking into it.