Android Room:使用Room插入关系实体

时间:2017-06-21 04:59:18

标签: android android-room android-architecture-components

我使用Relation在Room中添加了一对多的关系。 我提到this post在Room中为关系编写以下代码。

该帖子讲述了如何从数据库中读取值,但将实体存储到数据库中导致userId为空,这意味着两个表之间没有关系。

我不确定在insertUser List of Pet userId @Entity public class User { @PrimaryKey public int id; // User id } 进入数据库的理想方式是什么。

1)用户实体:

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

2)宠物实体:

// 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;
}

3)UserWithPets POJO:

DAO

现在要从数据库中获取记录,我们使用以下@Dao public interface UserDao { @Insert fun insertUser(user: User) @Query("SELECT * FROM User") public List<UserWithPets> loadUsersWithPets(); }

{{1}}

修改

我在问题跟踪器上创建了此问题https://issuetracker.google.com/issues/62848977。希望他们能为此做些什么。

6 个答案:

答案 0 :(得分:25)

您可以通过将Dao从界面更改为抽象类来完成此操作。

xdev2in

您还可以让@Dao public abstract class UserDao { public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } _insertAll(pets); } @Insert abstract void _insertAll(List<Pet> pets); //this could go in a PetDao instead... @Insert public abstract void insertUser(User user); @Query("SELECT * FROM User") abstract List<UserWithPets> loadUsersWithPets(); } 个对象拥有User

@Ignored List<Pet> pets

然后Dao可以将@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets } 映射到User:

UserWithPets

这将为您留下完整的Dao:

public List<User> getUsers() {
    List<UserWithPets> usersWithPets = loadUserWithPets();
    List<User> users = new ArrayList<User>(usersWithPets.size())
    for(UserWithPets userWithPets: usersWithPets) {
        userWithPets.user.pets = userWithPets.pets;
        users.add(userWithPets.user);
    }
    return users;
}

您可能希望在PetDAO中使用@Dao public abstract class UserDao { public void insertAll(List<User> users) { for(User user:users) { if(user.pets != null) { insertPetsForUser(user, user.pets); } } _insertAll(users); } private void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } _insertAll(pets); } public List<User> getUsersWithPetsEagerlyLoaded() { List<UserWithPets> usersWithPets = _loadUsersWithPets(); List<User> users = new ArrayList<User>(usersWithPets.size()) for(UserWithPets userWithPets: usersWithPets) { userWithPets.user.pets = userWithPets.pets; users.add(userWithPets.user); } return users; } //package private methods so that wrapper methods are used, Room allows for this, but not private methods, hence the underscores to put people off using them :) @Insert abstract void _insertAll(List<Pet> pets); @Insert abstract void _insertAll(List<User> users); @Query("SELECT * FROM User") abstract List<UserWithPets> _loadUsersWithPets(); } insertAll(List<Pet>)方法......您如何对DAO进行分区取决于您! :)

无论如何,这只是另一种选择。在DataSource对象中包装DAO也可以。

答案 1 :(得分:20)

在Room Library中进行任何更新之前,没有原生解决方案,但您可以通过技巧完成此操作。找到下面提到的。

  1. 只需创建一个带宠物的用户(忽略宠物)。

    @Entity
    public class User {
          @PrimaryKey
          public int id; 
    
          @Ignore
          private List<Pet> petList;
    }
    
  2. 创建宠物。

    @Entity 
    public class Pet 
    {
        @PrimaryKey
        public int id;     
        public int userId; 
        public String name;
    }
    
  3. 现在终于在UserDao。

    @Insert
    public abstract void insertUser(User user);
    
    @Insert
    public abstract void insertPetList(List<Pet> pets);
    
    @Query("SELECT * FROM User WHERE id =:id")
    public abstract User getUser(int id);
    
    @Query("SELECT * FROM Pet WHERE userId =:userId")
    public abstract List<Pet> getPetList(int userId);
    
    public void insertUserWithPet(User user) {
        List<Pet> pets = user.getPetList();
        for (int i = 0; i < pets.size(); i++) {
            pets.get(i).setUserId(user.getId());
        }
        insertPetList(pets);
        insertUser(user);
    }
    
    public User getUserWithPets(int id) {
        User user = getUser(id);
        List<Pet> pets = getPetList(id);
        user.setPetList(pets);
        return user;
    }
    
  4. 您可以通过此方法解决问题,而无需创建UserWithPets POJO。

答案 2 :(得分:6)

由于Room不管理实体的关系,您必须自己在每只宠物上设置userId并保存。只要一次没有太多宠物,我会使用insertAll方法来缩短它。

@Dao
public interface PetDao {
    @Insert
    void insertAll(List<Pet> pets);
}

我认为目前没有更好的方法。

为了使处理更容易,我在DAO上面的层中使用抽象:

public void insertPetsForUser(User user, List<Pet> pets){

    for(Pet pet : pets){
        pet.setUserId(user.getId());
    }

    petDao.insertAll(pets);
}

答案 3 :(得分:3)

目前此问题存在无本机解决方案。我在Google的问题跟踪器上创建了此https://issuetracker.google.com/issues/62848977,架构组件团队表示他们将在Room库的v1.0中或之后添加本机解决方案。

临时解决方法:

同时,您可以使用tknell提到的解决方案。

public void insertPetsForUser(User user, List<Pet> pets){

    for(Pet pet : pets){
        pet.setUserId(user.getId());
    }

    petDao.insertAll(pets);
}

答案 4 :(得分:2)

我设法用一个相对简单的解决方法正确地插入了它。这是我的实体:

   @Entity
public class Recipe {
    @PrimaryKey(autoGenerate = true)
    public long id;
    public String name;
    public String description;
    public String imageUrl;
    public int addedOn;
   }


  @Entity
public class Ingredient {
   @PrimaryKey(autoGenerate = true)
   public long id;
   public long recipeId; 
   public String name;
   public String quantity;
  }

public class RecipeWithIngredients {
   @Embedded
   public  Recipe recipe;
   @Relation(parentColumn = "id",entityColumn = "recipeId",entity = Ingredient.class)
   public List<Ingredient> ingredients;

我正在使用autoGenerate来获得自动增加的值(长期使用目的)。 这是我的解决方案:

  @Dao
public abstract class RecipeDao {

  public  void insert(RecipeWithIngredients recipeWithIngredients){
    long id=insertRecipe(recipeWithIngredients.getRecipe());
    recipeWithIngredients.getIngredients().forEach(i->i.setRecipeId(id));
    insertAll(recipeWithIngredients.getIngredients());
  }

public void delete(RecipeWithIngredients recipeWithIngredients){
    delete(recipeWithIngredients.getRecipe(),recipeWithIngredients.getIngredients());
  }

 @Insert
 abstract  void insertAll(List<Ingredient> ingredients);
 @Insert
 abstract long insertRecipe(Recipe recipe); //return type is the key here.

 @Transaction
 @Delete
 abstract void delete(Recipe recipe,List<Ingredient> ingredients);

 @Transaction
 @Query("SELECT * FROM Recipe")
 public abstract List<RecipeWithIngredients> loadAll();
 }

我在链接实体时遇到问题,一直自动生成生成的“ recipeId = 0”。插入配方实体首先为我修复了它。

答案 5 :(得分:0)

现在在v2.1.0,房间似乎不适合具有嵌套关系的模型。它需要很多样板代码来维护它们。例如。手动插入列表,创建和映射本地ID。

此关系映射操作由重新查询https://github.com/requery/requery开箱即用地完成,此外,它在插入Enums方面没有问题,并且具有一些用于其他复杂类型(如URI)的转换器。