我使用Relation在Room中添加了一对多的关系。 我提到this post在Room中为关系编写以下代码。
该帖子讲述了如何从数据库中读取值,但将实体存储到数据库中导致userId
为空,这意味着两个表之间没有关系。
我不确定在insert
值User
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。希望他们能为此做些什么。
答案 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中进行任何更新之前,没有原生解决方案,但您可以通过技巧完成此操作。找到下面提到的。
只需创建一个带宠物的用户(忽略宠物)。
@Entity
public class User {
@PrimaryKey
public int id;
@Ignore
private List<Pet> petList;
}
创建宠物。
@Entity
public class Pet
{
@PrimaryKey
public int id;
public int userId;
public String name;
}
现在终于在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;
}
您可以通过此方法解决问题,而无需创建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)的转换器。