J'ai ajouté une à plusieurs relations dans Room en utilisant Relation . J'ai fait référence à ce post pour écrire le code suivant pour la relation dans la salle.
Le message explique comment lire les valeurs de la base de données, mais le stockage des entités dans la base de données a abouti userId
à être vide, ce qui signifie qu'il n'y a pas de relation entre les 2 tables.
Je ne sais pas quelle est la manière idéale à insert
une User
et List of Pet
dans la base de données tout en ayant la userId
valeur.
1) Entité utilisateur:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Entité pour animaux de compagnie:
@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;
}
Maintenant, pour récupérer les enregistrements de DB, nous utilisons ce qui suit DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
ÉDITER
J'ai créé ce problème https://issuetracker.google.com/issues/62848977 sur l'outil de suivi des problèmes. J'espère qu'ils feront quelque chose à ce sujet.
la source
Réponses:
Vous pouvez le faire en changeant votre Dao d'une interface à une classe abstraite.
@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(); }
Vous pouvez également aller plus loin en faisant avoir un
User
objet@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
puis le Dao peut mapper
UserWithPets
à l'utilisateur: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; }
Cela vous laisse avec le Dao complet:
@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(); }
Vous voudrez peut-être avoir les méthodes
insertAll(List<Pet>)
etinsertPetsForUser(User, List<Pet>)
dans un PetDAO à la place ... la façon dont vous partitionnez vos DAO dépend de vous! :)Quoi qu'il en soit, c'est juste une autre option. L'emballage de vos DAO dans des objets DataSource fonctionne également.
la source
Il n'y a pas de solution native jusqu'à une mise à jour dans la bibliothèque de la salle, mais vous pouvez le faire par une astuce. Trouvez ci-dessous mentionné.
Créez simplement un utilisateur avec des animaux domestiques (ignorez les animaux domestiques). Ajoutez un getter et un setter. Notez que nous devons définir nos identifiants manuellement plus tard et que nous ne pouvons pas utiliser
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Créez un animal de compagnie.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
UserDao doit être une classe abstraite au lieu d'une interface. Puis enfin dans votre 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; }
Votre problème peut être résolu par cela sans créer UserWithPets POJO.
la source
insertUser()
abord pour obtenir une génération automatiqueuserId
, puis je l' assigneuserId
au champ userId de la classe Pet, puis en boucleinsertPet()
.Comme Room ne gère pas les relations des entités, vous devez définir vous-même les
userId
sur chaque animal et les enregistrer. Tant qu'il n'y a pas trop d'animaux à la fois, j'utiliserais uneinsertAll
méthode pour faire court.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Je ne pense pas qu'il y ait de meilleur moyen pour le moment.
Pour faciliter la manipulation, j'utiliserais une abstraction dans la couche au-dessus des DAO:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
la source
Stream
. J'espère juste qu'il y a une meilleure façon de le faire.Il n'existe actuellement aucune solution native à ce problème. J'ai créé ce https://issuetracker.google.com/issues/62848977 sur le suivi des problèmes de Google et l'équipe des composants d'architecture a déclaré qu'elle ajouterait une solution native dans ou après la v1.0 de la bibliothèque Room.
Solution temporaire:
En attendant, vous pouvez utiliser la solution mentionnée par tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
la source
Maintenant, dans la v2.1.0, Room ne semble pas convenir aux modèles avec des relations imbriquées. Il fallait beaucoup de code standard pour les maintenir. Par exemple, insertion manuelle de listes, création et mappage d'identifiants locaux.
Ces opérations de mappage de relations sont effectuées hors de la boîte par Requery https://github.com/requery/requery En outre, il n'a pas de problèmes d'insertion d'énumérations et dispose de convertisseurs pour d'autres types complexes comme URI.
la source
J'ai réussi à l'insérer correctement avec une solution de contournement relativement simple. Voici mes entités:
@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;
J'utilise autoGenerate pour la valeur d'auto-incrémentation (long est utilisé avec un purpoes). Voici ma solution:
@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(); }
J'ai eu des problèmes pour relier les entités, générer automatiquement "recetteId = 0" produit tout le temps. L'insertion de l'entité de recette l'a d'abord corrigée pour moi.
la source