[android] Android Room : Room을 사용하여 관계 엔티티 삽입

Relation을 사용하여 Room에 일대 다 관계를 추가했습니다 . 이 게시물 을 참조하여 Room의 관계에 대한 다음 코드를 작성했습니다.

게시물은 데이터베이스에서 값을 읽는 방법을 알려 주지만 엔티티를 데이터베이스에 저장 userId하면 두 테이블간에 관계가 없음을 의미합니다.

나는 확실히 할 수있는 이상적인 방법이 무엇인지 모르겠어요 및 하면서 데이터베이스에 값입니다.insertUserList of PetuserId

1) 사용자 엔티티 :

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

2) 애완 동물 개체 :

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

이제 DB에서 레코드를 가져 오기 위해 다음을 사용합니다 DAO.

@Dao
public interface UserDao {
    @Insert
    fun insertUser(user: User)

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

편집하다

이슈 트래커 에이 이슈 https://issuetracker.google.com/issues/62848977 을 만들었습니다 . 바라건대 그들은 그것에 대해 뭔가를 할 것입니다.



답변

Dao를 인터페이스에서 추상 클래스로 변경하여이를 수행 할 수 있습니다.

@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

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

    @Ignored
    public List<Pet> pets
}

그런 다음 Dao는 UserWithPetsUser에 매핑 할 수 있습니다 .

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

이것은 당신에게 완전한 Dao를 남깁니다.

@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>)insertPetsForUser(User, List<Pet>)대신 PetDAO의 방법을 … 당신이 당신의 DAO를 분할 방법까지입니다! 🙂

어쨌든, 그것은 다른 선택 일뿐입니다. DAO를 DataSource 개체로 래핑하는 것도 작동합니다.


답변

Room Library에서 업데이트 할 때까지는 기본 솔루션이 없지만 트릭으로이를 수행 할 수 있습니다. 아래에서 언급 한 내용을 찾으십시오.

  1. 애완 동물이있는 사용자 만들기 (애완 동물 무시). getter 및 setter를 추가하십시오. 나중에 ID를 수동으로 설정해야하며 autogenerate.

    @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는 인터페이스 대신 추상 클래스 여야합니다. 그런 다음 마지막으로 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를 만들지 않고도 이것으로 문제를 해결할 수 있습니다.


답변

룸은 엔티티의 관계를 관리하지 않기 때문에 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);
}


답변

현재이 문제에 대한 기본 해결책없습니다 . 이 https://issuetracker.google.com/issues/62848977 을 Google의 문제 추적기에 만들었고 아키텍처 구성 요소 팀은 Room 라이브러리 v1.0 또는 그 이후에 기본 솔루션을 추가 할 것이라고 말했습니다.

임시 해결 방법 :

한편 tknell에서 언급 한 솔루션을 사용할 수 있습니다 .

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

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

    petDao.insertAll(pets);
}


답변

이제 v2.1.0에서 Room은 중첩 된 관계가있는 모델에 적합하지 않은 것 같습니다. 이를 유지하려면 많은 상용구 코드가 필요했습니다. 예 : 목록의 수동 삽입, 로컬 ID 생성 및 매핑.

이 관계 매핑 작업은 Requery https://github.com/requery/requery에 의해 즉시 수행됩니다. 또한 Enum 삽입에 문제가 없으며 URI와 같은 다른 복잡한 유형에 대한 일부 변환기가 있습니다.


답변

비교적 간단한 해결 방법으로 제대로 삽입 할 수있었습니다. 내 엔티티는 다음과 같습니다.

   @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를 사용하고 있습니다 (long은 purpoes와 함께 사용됨). 내 해결책은 다음과 같습니다.

  @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″이 항상 발생했습니다. 레시피 엔티티를 먼저 삽입하면 문제가 해결되었습니다.


답변