Android Persistence room: "Невозможно понять, как читать это поле из курсора"

Я пытаюсь создать связь между двумя таблицами базы данных, используя новую библиотеку Android Persistence Room Library. Я просмотрел документацию и попытался реализовать пример, найденный в https://developer.android.com/reference/android/arch/persistence/room/Relation.html:

 @Entity
 public class User {
 @PrimaryKey
     int id;
 }

 @Entity
 public class Pet {
     @PrimaryKey
     int id;
     int userId;
     String name;

 }

 @Dao
 public interface UserDao {
     @Query("SELECT * from User")
     public List<User> loadUser();
 }

 @Dao
 public interface PetDao {
     @Query("SELECT * from Pet")
     public List<Pet> loadUserAndPets();
 }


 public class UserAllPets {
     @Embedded
     public User user;
     @Relation(parentColumn = "user.id", entityColumn = "userId", entity = Pet.class)
     public List pets;
 }

 @Dao
 public interface UserPetDao {
     @Query("SELECT * from User")
     public List<UserAllPets> loadUserAndPets();
 }

Я получаю следующую ошибку

    ...error: Cannot figure out how to read this field from a cursor.

по отношению к:

 private java.util.List<?> pets;

Я хотел бы указать, что некоторые вещи в их документах действительно сбивают с толку. Например, отсутствие @PrimaryKey, а также тот факт, что класс User отсутствует аннотация @Entity, хотя он должен быть сущностью (как, как я вижу). Кто-нибудь сталкивался с одной и той же проблемой? Большое спасибо заранее

Ответы

Ответ 1

Документ действительно запутан. Попробуйте чуть ниже классов:

1) Пользовательский объект:

@Entity
public class User {
    @PrimaryKey
    public int id; // User id
}

2) Entity Pet:

@Entity
public class Pet {
    @PrimaryKey
    public int id;     // Pet id
    public int userId; // User id
    public String name;
}

введите описание изображения здесь

3) UserWithPets POJO:

// Note: No annotation required at this class definition.
public class UserWithPets {
   @Embedded
   public User user;

   @Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class)
   public List<Pet> pets; // or use simply 'List pets;'


   /* Alternatively you can use projection to fetch a specific column (i.e. only name of the pets) from related Pet table. You can uncomment and try below;

   @Relation(parentColumn = "id", entityColumn = "userId", entity = Pet.class, projection = "name")
   public List<String> pets; 
   */
}
  • parentColumn относится к столбцу Embedded User table id,
  • entityColumn относится к столбцу Pet table userId (User - Pet),
  • entity относится к таблице (Pet), которая имеет отношение к таблице User.

4) UserDao Dao:

@Dao
public interface UserDao {
    @Query("SELECT * FROM User")
    public List<UserWithPets> loadUsersWithPets();
}

Теперь попробуйте loadUsersWithPets(), который возвращает пользователей с их списком домашних животных.

Изменить: См. мой другой ответ для многих отношений.

Ответ 2

Я знал это немного не по теме, но я надеюсь, что мой ответ может предоставить действительную альтернативу. Я создаю библиотеку Kripton Persistente Library, библиотеку с открытым исходным кодом для платформы Android с целью единообразного решения задачи сохранения: SQlite, REST-сервис (через дооснащение), общие настройки и файл. Он использует обработчик аннотации для генерации кода и исключения кода шаблона.

Я адаптирую свои классы для работы с Kripton.

Объекты сохранения:

@BindType
public class Pet {
    public long id;

    @BindColumn(foreignKey = User.class)
    public long userId;

    public String name;    
}

@BindType
public class User {
    public long id;

    @BindColumn(enabled=false)
    public List<Pet> pets;
}

Объявление DAO и Datasource:

@BindDao(Pet.class)
public interface PetDao {
    @BindSqlSelect
    public List<Pet> loadPet();
}

@BindDao(User.class)
public interface UserDao {
    @BindSqlSelect
    public List<User> loadUser();
}

@BindDataSource(dao = { UserDao.class, PetDao.class }, fileName = "pet.db")
public interface PetUserDataSource {

}

Для пользователей Pet и User Kripton генерирует две таблицы со следующим DDL:

CREATE TABLE user (id INTEGER PRIMARY KEY AUTOINCREMENT);
CREATE TABLE pet (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, name TEXT, FOREIGN KEY(user_id) REFERENCES user(id));

И, наконец, код для вызова операции SQL на источнике данных:

...
BindPetUserDataSource dataSource=BindPetUserDataSource.instance();

dataSource.openReadOnlyDatabase();
dataSource.getUserDao().loadUser();
// to load a pet starting from a user just add userId to method
dataSource.getPetDao().loadPet();
dataSource.close();
...

Основное отличие заключается в том, что Kripton работает просто над объектами: отношения могут быть определены как внешний ключ, но я выбрал, чтобы избежать их управления.

Для получения дополнительной информации: