Saya telah menambahkan satu ke banyak hubungan di Room menggunakan Relation . Saya mengacu posting ini untuk menulis kode berikut untuk relasi di Room.
Postingan tersebut memberi tahu cara membaca nilai dari database tetapi menyimpan entitas ke dalam database yang dihasilkan userId
kosong yang berarti tidak ada hubungan antara 2 tabel tersebut.
Saya tidak yakin apa yang adalah cara yang ideal untuk insert
sebuah User
danList of Pet
ke dalam database sementara memiliki userId
nilai.
1) Entitas Pengguna:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Entitas Hewan Peliharaan:
@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;
}
Sekarang untuk mengambil record dari DB kita menggunakan yang berikut ini DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
EDIT
Saya telah membuat masalah ini https://issuetracker.google.com/issues/62848977 di pelacak masalah. Mudah-mudahan mereka akan melakukan sesuatu tentang itu.
sumber
Jawaban:
Anda dapat melakukan ini dengan mengubah Dao Anda dari antarmuka ke kelas abstrak.
@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(); }
Anda juga dapat melangkah lebih jauh dengan membuat
User
benda memiliki ekstensi@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
dan kemudian Dao dapat memetakan
UserWithPets
ke Pengguna: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; }
Ini membuat Anda memiliki Dao penuh:
@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(); }
Anda mungkin ingin memiliki
insertAll(List<Pet>)
daninsertPetsForUser(User, List<Pet>)
metode di PetDAO sebagai gantinya ... bagaimana Anda mempartisi DAO Anda terserah Anda! :)Bagaimanapun, itu hanya pilihan lain. Membungkus DAO Anda di objek DataSource juga berfungsi.
sumber
Tidak ada solusi asli hingga pembaruan apa pun di Perpustakaan Kamar tetapi Anda dapat melakukannya dengan trik. Temukan yang disebutkan di bawah ini.
Cukup Buat Pengguna dengan Hewan Peliharaan (Abaikan hewan peliharaan). Tambahkan pengambil dan penyetel. Perhatikan bahwa kita harus mengatur Id kita secara manual nanti dan tidak dapat digunakan
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Buat Hewan Peliharaan.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
User Dao harus berupa kelas abstrak, bukan Interface. Lalu akhirnya di 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; }
Masalah Anda dapat diselesaikan dengan ini tanpa membuat UserWithPets POJO.
sumber
insertUser()
terlebih dahulu untuk mendapatkan auto-generateduserId
, kemudian menetapkan ituuserId
ke bidang userId di kelas Pet, kemudian mengulang keinsertPet()
.Karena Room tidak mengelola Relasi entitas, Anda harus mengatur
userId
sendiri setiap hewan peliharaan dan menyimpannya. Selama tidak terlalu banyak hewan peliharaan sekaligus, saya akan menggunakaninsertAll
metode untuk membuatnya tetap pendek.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Saya tidak berpikir ada cara yang lebih baik saat ini.
Untuk mempermudah penanganan, saya akan menggunakan abstraksi di lapisan di atas DAO:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
sumber
Stream
. Saya hanya berharap ada cara yang lebih baik untuk melakukannya.Saat ini tidak ada solusi asli untuk masalah ini. Saya telah membuat https://issuetracker.google.com/issues/62848977 ini di pelacak masalah Google dan Tim Komponen Arsitektur mengatakan mereka akan menambahkan solusi asli di atau setelah v1.0 dari perpustakaan Room.
Solusi Sementara:
Sementara itu Anda dapat menggunakan solusi yang disebutkan oleh tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
sumber
Sekarang di v2.1.0 Room tampaknya tidak cocok untuk model dengan relasi bertingkat. Diperlukan banyak kode boilerplate untuk memeliharanya. Misalnya, memasukkan daftar secara manual, membuat dan memetakan ID lokal.
Operasi pemetaan relasi ini dilakukan di luar kotak oleh Requery https://github.com/requery/requery Additionaly itu tidak memiliki masalah dengan memasukkan Enum dan memiliki beberapa konverter untuk jenis kompleks lainnya seperti URI.
sumber
Saya berhasil memasukkannya dengan benar dengan solusi yang relatif sederhana. Inilah entitas saya:
@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;
Saya menggunakan autoGenerate untuk nilai auto-increment (panjang digunakan dengan purpoes). Inilah solusi saya:
@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(); }
Saya mengalami masalah saat menautkan entitas, secara otomatis menghasilkan "resepId = 0" sepanjang waktu. Memasukkan entitas resep terlebih dahulu memperbaikinya untuk saya.
sumber