Relation を使用して、Roomの多くの関係に1つ追加しました。 この投稿 を参照して、Roomでのリレーションについて次のコードを記述しました。
この投稿では、データベースから値を読み取る方法を示していますが、エンティティをデータベースに格納すると、userId
が空になります。つまり、2つのテーブル間にリレーションはありません。
insert
の値を保持しながら、データベースにUser
とuserId
および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 問題追跡システムで。うまくいけば、彼らはそれについて何かをするでしょう。
これを行うには、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のラップも機能します。
ルームライブラリを更新するまでネイティブソリューションはありませんが、これはトリックで行うことができます。下記をご覧ください。
ペットを持つユーザーを作成するだけです(ペットを無視します)。
@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を作成せずに問題を解決できます。
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);
}
現在、この問題に対するネイティブソリューションはありません。私はこれを作成しました 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);
}
現在、v2.1.0では、Roomはネストされたリレーションを持つモデルには適さないようです。それらを維持するために多くの定型コードが必要でした。例えば。リストの手動挿入、ローカルIDの作成とマッピング。
このリレーションマッピング操作は、Requery https://github.com/requery/requery によりすぐに実行されます。さらに、Enumの挿入に関する問題はなく、URIなどの他の複雑なタイプのコンバーターもあります。