web-dev-qa-db-ja.com

Android Room:Roomを使用して関係エンティティを挿入

Relation を使用して、Roomの多くの関係に1つ追加しました。 この投稿 を参照して、Roomでのリレーションについて次のコードを記述しました。

この投稿では、データベースから値を読み取る方法を示していますが、エンティティをデータベースに格納すると、userIdが空になります。つまり、2つのテーブル間にリレーションはありません。

insertの値を保持しながら、データベースにUseruserIdおよびList of Petを挿入する理想的な方法はわかりません。

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 問題追跡システムで。うまくいけば、彼らはそれについて何かをするでしょう。

46
Akshay Chordiya

これを行うには、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はUserWithPetsをユーザーにマッピングできます:

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

代わりに、PetDAOにinsertAll(List<Pet>)メソッドとinsertPetsForUser(User, List<Pet>)メソッドを含めることもできます。DAOをパーティション分割する方法はユーザー次第です。 :)

とにかく、それは別のオプションです。 DataSourceオブジェクトでのDAOのラップも機能します。

28

ルームライブラリを更新するまでネイティブソリューションはありませんが、これはトリックで行うことができます。下記をご覧ください。

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

これにより、UserWithPets POJOを作成せずに問題を解決できます。

22
Dipendra Sharma

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);
}
6
tknell

現在、この問題に対するネイティブソリューションはありません。私はこれを作成しました https://issuetracker.google.com/issues/62848977 Googleの課題トラッカーで、アーキテクチャコンポーネントチームは、ルームライブラリのv1.0以降にネイティブソリューションを追加すると述べました。

一時的な回避策:

一方、 tknell で言及されているソリューションを使用できます。

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

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

    petDao.insertAll(pets);
}
3
Akshay Chordiya

現在、v2.1.0では、Roomはネストされたリレーションを持つモデルには適さないようです。それらを維持するために多くの定型コードが必要でした。例えば。リストの手動挿入、ローカルIDの作成とマッピング。

このリレーションマッピング操作は、Requery https://github.com/requery/requery によりすぐに実行されます。さらに、Enumの挿入に関する問題はなく、URIなどの他の複雑なタイプのコンバーターもあります。

0
Serge