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

Array / List Column Naming #668

Closed chriszwickerocteris closed 4 years ago

chriszwickerocteris commented 5 years ago

The documentation seems to state that columns with a name that ends in _[0-9]+ will map to the nth element in an array / list.

One issue is with the documentation: what seems to happen is that SFM actually maps to element n+1 (a_1 maps to the second element, the element at index 1).

The bigger problem is that whenever a path segment ends in a number, it seems to be interpreted as just a number; a_b1 seems to equally be interpreted as the second element in a list a- ignoring that b1 might be the name of an element the type that is contained in said list.

Here's a reproducing test:

public class SfmListTests {
  private static Connection conn;

  @BeforeClass
  public static void init() throws SQLException {
    conn = DriverManager.getConnection("jdbc:postgresql://localhost:4001/testing", "postgres", "testing");
  }

  @Test
  public void failing() throws SQLException {
    final String query = String.join("\n",
      "with t (",
        "id, name, foos_id, foos_bar1_id, foos_bar2_id",
      " ) as ( values",
        "('a', 'foo', 'b', 'c', 'd')",
      ")",
      "select * from t"
    );
    PreparedStatement stmt = conn.prepareStatement(query);
    ResultSet rs = stmt.executeQuery();
    final JdbcMapperFactory mapperFactory = JdbcMapperFactory.newInstance()
      .useAsm(false)
      .ignorePropertyNotFound()
      .addKeys("id")
      .addKeys("foos_id")
      .addKeys("foos_bar1_id")
      .addKeys("foos_bar2_id")
    ;
    final Iterator<Root> iterator = mapperFactory.newMapper(Root.class).iterator(rs);

    assertTrue(iterator.hasNext());
    final Root found = iterator.next();
    assertNotNull(found);
    assertEquals(1, found.foos.size());
  }

  public static class Root {
    private String id;
    private String name;
    private List<Foo> foos;

    public Root() {}
    public Root(final String id, final String name, final List<Foo> foos) {
      this.id = id;
      this.name = name;
      this.foos = foos;
    }
  }

  public static class Foo {
    private String id;
    private Bar bar1;
    private Bar bar2;

    public Foo() {}
    public Foo(final String id, final Bar bar1, final Bar bar2) {
      this.id = id;
      this.bar1 = bar1;
      this.bar2 = bar2;
    }
  }

  public static class Bar {
    private String id;

    public Bar() {}
    public Bar(final String id) {
      this.id = id;
    }
  }
}
chriszwickerocteris commented 4 years ago

May I suggest also updating the docs?

I would suggest the following:

List<String> myList, "my_list_3" -> will match against the 4th element (the element at array index 3) of the list as a String. List<MyObject> myList, "my_list_3_id" -> will match against property id of the 4th element (the element at array index 3) of the list.

Index behaviour can be switched from zero-indexed to one-indexed by adding column property ArrayIndexStartAtProperty.ONE to the configuration, e.g.

import org.simpleflatmapper.reflect.property.ArrayIndexStartAtProperty;

// ...
mapperFactory.addColumnProperty("my_column", ArrayIndexStartAtProperty.ONE);
// ...
arnaudroger commented 4 years ago

Definitely