Jdbi выдает ошибку при использовании ReduRows для соединения, отношение один ко многим - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь написать объектный запрос с JDBI, чтобы иметь дело с отношением один-ко-многим. Как видно из документации, reduceRows - это способ сделать это. Но я получил эти ошибки, обвиняя конструктора;

Обновление: оставьте по одному конструктору для каждой сущности.

Я проверил 2 кода из документов и получил следующие трассировки стека:

get1
Exception in thread "main" java.lang.IllegalArgumentException: Could not find column mapper for type 'join.AppJoin' of parameter 'c_id' for instance factory 'public join.AppJoin$Contact(join.AppJoin,int,java.lang.String)'
at org.jdbi.v3.core.mapper.reflect.ConstructorMapper.lambda$specialize0$3(ConstructorMapper.java:231)
at java.base/java.util.Optional.orElseThrow(Optional.java:408)
at org.jdbi.v3.core.mapper.reflect.ConstructorMapper.specialize0(ConstructorMapper.java:230)
at org.jdbi.v3.core.mapper.reflect.ConstructorMapper.specialize(ConstructorMapper.java:189)
at org.jdbi.v3.core.result.internal.RowViewImpl.rowMapperFor(RowViewImpl.java:63)
at org.jdbi.v3.core.result.internal.RowViewImpl.getRow(RowViewImpl.java:50)
at org.jdbi.v3.core.result.RowView.getRow(RowView.java:35)
at join.AppJoin.lambda$get1$0(AppJoin.java:70)


get2
Exception in thread "main" java.lang.NoSuchMethodException: no such constructor: join.AppJoin$Contact.<init>()void/newInvokeSpecial
at java.base/java.lang.invoke.MemberName.makeAccessException(MemberName.java:961)
at java.base/java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1101)
at java.base/java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:2030)
at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:1264)
at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties$PropertiesHolder.<init>(BeanPropertiesFactory.java:200)
at org.jdbi.v3.core.config.JdbiCaches.lambda$declare$0(JdbiCaches.java:49)
at org.jdbi.v3.core.config.JdbiCaches$1.lambda$get$1(JdbiCaches.java:63)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1705)
at org.jdbi.v3.core.config.JdbiCaches$1.get(JdbiCaches.java:63)
at org.jdbi.v3.core.mapper.reflect.internal.BeanPropertiesFactory$BeanPojoProperties.getProperties(BeanPropertiesFactory.java:81)
at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize0(PojoMapper.java:99)
at org.jdbi.v3.core.mapper.reflect.internal.PojoMapper.specialize(PojoMapper.java:80)
at org.jdbi.v3.core.result.internal.RowViewImpl.rowMapperFor(RowViewImpl.java:63)
at org.jdbi.v3.core.result.internal.RowViewImpl.getRow(RowViewImpl.java:50)
at org.jdbi.v3.core.result.RowView.getRow(RowView.java:35)
at join.AppJoin.lambda$getList$1(AppJoin.java:92)
at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1133)
at join.AppJoin.lambda$getList$2(AppJoin.java:90)
at org.jdbi.v3.core.result.ResultBearing.lambda$reduceRows$5(ResultBearing.java:236)

Мой код:

import org.jdbi.v3.core.Handle;
import org.jdbi.v3.core.Jdbi;
import org.jdbi.v3.core.mapper.reflect.BeanMapper;
import org.jdbi.v3.core.mapper.reflect.ColumnName;
import org.jdbi.v3.core.mapper.reflect.ConstructorMapper;
import org.jdbi.v3.core.result.RowView;
import org.jdbi.v3.sqlobject.SqlObjectPlugin;

import java.beans.ConstructorProperties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import static java.util.stream.Collectors.toList;

public class AppJoin {
  public static void main(final String[] args) {

    Properties properties = new Properties();
    properties.setProperty("username", "sa");
    properties.setProperty("password", "");
    Jdbi jdbi = Jdbi.create("jdbc:h2:mem:testDB", properties);
    jdbi.installPlugin(new SqlObjectPlugin());

    try (final var handle = jdbi.open()) {
      String sqlc1 = "CREATE TABLE contacts ( \n" +
              "  id BIGSERIAL PRIMARY KEY, \n" +
              "  name VARCHAR(255)\n" +
              ")";
      String sqlc2 = "CREATE TABLE phones ( \n" +
              "  id BIGSERIAL PRIMARY KEY, \n" +
              "  phone VARCHAR(255), \n" +
              "  contactId int, \n" +
              "  foreign key (contactId) references contacts(id) on delete cascade \n" +
              ")";
      handle.createUpdate(sqlc1).execute();
      handle.createUpdate(sqlc2).execute();

      handle.createUpdate("insert into contacts (name) values (:name)")
              .bind("name", "str")
              .execute();

      handle.createUpdate("insert into phones (phone, contactId) values (:phone, :contactId)")
              .bind("phone", "1111111")
              .bind("contactId", "1")
              .execute();

      // List<Contact> list = handle.select("select id, name from contacts")
      //        .mapToBean(Contact.class).list();
      // System.out.println(list);

      System.out.println(get1(handle));
//      System.out.println(get2(handle));
    }
  }

  private static Contact get1(Handle handle) {
    return handle.createQuery("select contacts.id c_id, name c_name, "
            + "phones.id p_id, phones.phone p_phone "
            + "from contacts left join phones on contacts.id = phones.contactId "
            + "where contacts.id = :id")
            .bind("id", 1)
            .registerRowMapper(ConstructorMapper.factory(Contact.class, "c_"))
            .registerRowMapper(ConstructorMapper.factory(Phone.class, "p_"))
            .reduceRows(null, (contact, rowView) -> {
              if (contact == null) {
                contact = rowView.getRow(Contact.class);
              }

              if (rowView.getColumn("p_id", Integer.class) != null) {
                contact.addPhone(rowView.getRow(Phone.class));
              }

              return contact;
            });
  }


  private static List<Contact> get2(Handle handle) {
    return handle.createQuery(
            "select contacts.id c_id, name c_name, phones.id p_id, phones.phone p_phone "
                    + " from contacts "
                    + " left join phones on contacts.id = phones.contactId ")
            .registerRowMapper(BeanMapper.factory(Contact.class, "c"))
            .registerRowMapper(BeanMapper.factory(Phone.class, "p"))
            .reduceRows((Map<Long, Contact> map, RowView rowView) -> {
              Contact contact = map.computeIfAbsent(
                      rowView.getColumn("c_id", Long.class),
                      id -> rowView.getRow(Contact.class));

              if (rowView.getColumn("p_id", Long.class) != null) {
                contact.addPhone(rowView.getRow(Phone.class));
              }

            })
            .collect(toList());
  }

  public class Contact {
    @ColumnName("id")
    private final int id;
    @ColumnName("name")
    private final String name;
    @ColumnName("phones")
    private List<Phone> phones;

    @ConstructorProperties({"id", "name"})
    public Contact(int id, String name) {
      this.id = id;
      this.name = name;
      this.phones = new ArrayList<>();
    }

    public int getId() {
      return id;
    }

    public String getName() {
      return name;
    }

    public List<Phone> getPhones() {
      return phones;
    }

    public void addPhone(Phone phone) {
      phones.add(phone);
    }

    @Override
    public String toString() {
      return "Contact{" +
              "id=" + id +
              ", name='" + name + '\'' +
              ", phones=" + phones +
              '}';
    }

  }

  public class Phone {
    @ColumnName("id")
    private final int id;
    @ColumnName("phone")
    private final String phone;

    @ConstructorProperties({"id", "phone"})
    public Phone(int id, String phone) {
      this.id = id;
      this.phone = phone;
    }

    public int getId() {
      return id;
    }

    public String getPhone() {
      return phone;
    }

    @Override
    public String toString() {
      return "Phone{" +
              "id=" + id +
              ", phone='" + phone + '\'' +
              '}';
    }
  }

}

Зависимости Gradle:

 compile "com.h2database:h2:1.4.199"
 compile group: 'org.jdbi', name: 'jdbi3-core', version: '3.12.2'
 compile group: 'org.jdbi', name: 'jdbi3-sqlobject', version: '3.12.2'

Буду признателен за любую помощь / кто-то, указывающий мне в правильном направлении! Можно ли это сделать в интерфейсе Дао?

...