Как использовать Android Room с POJO, предоставленными внешней библиотекой?

Это разъяснение моего предыдущего вопроса Как использовать Android Room для сущностей без полей?.

Мой вариант использования заключается в том, что я использую библиотеку Java JAR, предоставленную третьей стороной, которая содержит множество POJO, которые я хочу хранить и извлекать через Android Room. Ясно, что я не могу аннотировать поля в классе, для которого у меня нет исходного кода. Внутренняя структура предоставленных POJO представляет собой черный ящик, все, к чему у меня есть доступ, — это сеттеры и геттеры.

Представьте, например, что я хочу использовать экземпляры android.graphics.Rect с комнатой.

Оберните каждый из исходных POJO в новый POJO, содержащий необходимые аннотации.

Robert Harvey 23.07.2019 21:22

Можно ли определить состояние POJO через геттеры? Иными словами, если бы вы использовали геттеры для установки атрибутов другого экземпляра объекта, были бы эти два объекта фактически одинаковыми?

Synergi 23.07.2019 21:47

@Synergi Правильно, в библиотеке POJO есть сеттеры и геттеры, и нет другого открытого состояния.

satur9nine 23.07.2019 22:09

@satur9nine Я полагаю, что в случае, который вы описываете, лучшим способом действий будет написание объекта модели объекта, который представляет POJO, который вы хотите сохранить, значениями из его сеттеров и служебной функцией для сопоставления вашего объекта объекта Room обратно с библиотека POJO. В противном случае единственный другой вариант, о котором я могу думать сейчас, - это ответ Бланделла.

Synergi 23.07.2019 22:11
2
4
1 381
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Взгляните на встроенную аннотацию

https://developer.android.com/reference/android/arch/persistence/room/Встроенный

Can be used as an annotation on a field of an Entity or Pojo to signal that nested fields (i.e. fields of the annotated field's class) can be referenced directly in the SQL queries.

Библиотека:

class BlackBox {
  String foo
}

Ваш код:

@Entity
class MyWhiteBox {

  @Embedded
  private BlackBox blackbox 

}

Room будет рассматривать foo так, как будто это поле класса MyWhiteBox при сопоставлении строки SQLite с адресом.

Это совсем не работает для меня. Я не хочу, чтобы Room рассматривал библиотеку POJO как встроенную в POJO, которую я пишу сам. В вашем примере MyWhiteBox нужен первичный ключ, чтобы сделать комнату счастливой, но я не хочу добавлять дополнительные поля в библиотеку POJO, я просто хочу использовать геттеры и сеттеры, которые есть в библиотеке POJO, для различных столбцов, включая первичный ключ, который у меня нет не хочу добавлять дополнительные столбцы.

satur9nine 23.07.2019 22:06

если ваш первичный ключ контролируется библиотекой/другим модулем, который не знает, что он используется в качестве первичного ключа базы данных, это приведет к очень связанному/глючному/недетерминированному (не чистому!) коду. Укусите пулю и обработайте свои собственные POJO для своей собственной базы данных. Или используйте аннотации в модуле библиотеки.

Blundell 23.07.2019 22:13

Если бы я мог каким-то образом добавить свой собственный идентификатор в POJO библиотеки, это было бы приемлемо (обертывание, создание подклассов или что-то еще)? Самое главное, мне нужен способ контролировать, какие поля хранятся или нет в POJO библиотеки, не имея возможности аннотировать POJO библиотеки.

satur9nine 23.07.2019 22:30
Ответ принят как подходящий

Вот решения, которые я придумал, ни одно из них не является полностью идеальным, но последнее достойно.

Плохой:

Эта опция просто наивно делает копию класса POJO библиотеки и предоставляет вспомогательные методы для преобразования POJO библиотеки в POJO комнаты и обратно. Довольно некрасиво, и поскольку это создает копии, которые могут расходиться и рассинхронизироваться, мне приходится писать много кода, копирующего туда и обратно в моем приложении.

@Entity(tableName = "items")
public class RoomItem {

  @PrimaryKey
  @NonNull
  public String id;
  public String name;
  public Long price

  public LibraryItem to() {
    LibraryItem li = new LibraryItem();
    li.setName(name);
    li.setPrice(price);
  }

  public static RoomItem from(LibraryItem li) {
    RoomItem ri = new RoomItem();
    ri.name = li.getName();
    ri.price = li.getPrice();
  }

}

Хорошо:

В этом случае RoomItem — это класс-оболочка с полями, которые используются только для информирования Room о столбцах и типах, кроме id в полях фактически ничего не хранится, поэтому они просто тратят место. Каждый раз, когда я хочу получить настоящий библиотечный объект для использования с библиотечными методами, я должен захватить обернутый экземпляр. По крайней мере, RoomItem и LibraryItem фактически отражают изменения.

@Entity(tableName = "items")
public class RoomItem {

  // I have to put these fields here even though they aren't used to store any
  // data just to make Room happy!
  @PrimaryKey
  @NonNull
  private String id = "";
  private String name;
  private Long price;

  @Ignore
  private final LibraryItem item = new LibraryItem();

  public RoomItem(String id, String name, Long price) {
    this.id = id;
    setName(name);
    setPrice(price);
  }

  @NonNull
  public String getId() {
    return id;
  }

  public void setId(@NonNull String id) {
    this.id = id;
  }

  public String getName() {
    return item.getName();
  }

  public void setName(String name) {
    item.setName(name);
  }

  public Long getPrice() {
    return item.getPrice();
  }

  public void setPrice(Long price) {
    item.setPrice(price);
  }

  public Library getLibraryItem() {
    return item;
  }
}

Лучше (2.2.0-alpha02 или новее)

Гугл исправил ошибку, о котором я сообщил в Room, и начиная с версии 2.2.0-alpha02 теперь можно использовать наследование. Поля в дочернем классе просто используются для информирования Room о том, что нужно сохранить, и когда minify включен, они фактически отбраковываются, поэтому память не тратится впустую. Это решение потребовало от меня немного знаний о внутренней структуре родительского класса, чтобы я мог игнорировать вещи внутри него, такие как privateParentField. Однако это лучше, чем пример «Хорошо», потому что теперь я могу передавать экземпляры RoomItem методам, которые принимают LibraryItem, и мне не нужно писать много шаблонного кода.

@Entity(tableName = "items", ignoredColumns = "privateParentField")
public class RoomItem extends LibraryItem {

  // I have to put these fields here even though they aren't used to store any
  // data just to make Room happy!
  @PrimaryKey
  @NonNull
  private String id;
  private String name;
  private Long price;

  public RoomItem(String id, String name, Long price) {
    setId(id);
    setName(name);
    setPrice(price);
  }

}

Я обновил ответ, включив в него последнее исправление от Google в 2.2.0-alpha02, о котором я сообщил в ответ на свои проблемы.

satur9nine 08.08.2019 20:05

Хорошо, вы упомянули два класса - POJO и ROOM? Я хотел бы сравнить их. Я получаю такую ​​ошибку: error: Field has non-unique column name..

deadfish 29.12.2020 16:41

@deadfish POJO — это аббревиатура от «простой старый класс Java». Обычно это означает обычный класс Java, который не расширяет другой класс. Room — это не класс, это название программной библиотеки, написанной Google, см. здесь: developer.android.com/training/data-storage/room

satur9nine 30.12.2020 04:02

Привет, я понимаю основы, но я хотел бы проверить упомянутые вами классы (или я что-то неправильно понял). Было бы здорово, если бы я мог использовать один класс вместо двух (pojo и room), когда я работаю с ними, особенно когда я загружаю pojo, а затем я должен вставить в базу данных. Может быть, у вас есть другой пример по сути?

deadfish 30.12.2020 09:51

Другие вопросы по теме