Ich habe eine zu vielen Beziehungen in Room mithilfe von Relation hinzugefügt . Ich habe auf diesen Beitrag verwiesen, um den folgenden Code für die Beziehung in Raum zu schreiben.
Der Beitrag erklärt, wie die Werte aus der Datenbank gelesen werden, aber das Speichern der Entitäten in der Datenbank führte userId
dazu, dass sie leer waren, was bedeutet, dass zwischen den beiden Tabellen keine Beziehung besteht.
Ich bin mir nicht sicher, was der ideale Weg zu insert
einer User
und List of Pet
in die Datenbank ist, während ich userId
Wert habe.
1) Benutzerentität:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Haustierentität:
@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;
}
Um die Datensätze aus der Datenbank abzurufen, verwenden wir Folgendes DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
BEARBEITEN
Ich habe dieses Problem https://issuetracker.google.com/issues/62848977 im Issue-Tracker erstellt. Hoffentlich werden sie etwas dagegen unternehmen.
quelle
Antworten:
Sie können dies tun, indem Sie Ihr Dao von einer Schnittstelle in eine abstrakte Klasse ändern.
@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(); }
Sie können auch weiter gehen, indem Sie ein
User
Objekt haben@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
und dann kann das Dao
UserWithPets
dem Benutzer zugeordnet werden: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; }
Damit haben Sie das volle 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(); }
Vielleicht möchten Sie die Methoden
insertAll(List<Pet>)
undinsertPetsForUser(User, List<Pet>)
stattdessen in einem PetDAO haben ... wie Sie Ihre DAOs partitionieren, liegt bei Ihnen! :) :)Auf jeden Fall ist es nur eine andere Option. Das Umschließen Ihrer DAOs in DataSource-Objekte funktioniert ebenfalls.
quelle
Bis zu einem Update in der Raumbibliothek gibt es keine native Lösung, aber Sie können dies mit einem Trick tun. Finden Sie unten erwähnt.
Erstellen Sie einfach einen Benutzer mit Haustieren (Haustiere ignorieren). Getter und Setter hinzufügen. Beachten Sie, dass wir unsere IDs später manuell einstellen müssen und nicht verwenden können
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Erstellen Sie ein Haustier.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
Das UserDao sollte eine abstrakte Klasse anstelle einer Schnittstelle sein. Dann endlich in deinem 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; }
Ihr Problem kann dadurch gelöst werden, ohne UserWithPets POJO zu erstellen.
quelle
insertUser()
zuerst auf, um eine automatische Generierung zu erhaltenuserId
, und ordne diese dannuserId
dem Feld userId in der Pet-Klasse zuinsertPet()
.Da Room die Beziehungen der Entitäten nicht verwaltet, müssen Sie die
userId
für jedes Haustier selbst festlegen und speichern. Solange es nicht zu viele Haustiere gleichzeitig gibt, würde ich eineinsertAll
Methode verwenden, um es kurz zu halten.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Ich glaube nicht, dass es im Moment einen besseren Weg gibt.
Um die Handhabung zu vereinfachen, würde ich eine Abstraktion in der Ebene über den DAOs verwenden:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
quelle
Stream
. Ich hoffe nur, dass es einen besseren Weg gibt, dies zu tun.Derzeit gibt es keine native Lösung für dieses Problem. Ich habe diese https://issuetracker.google.com/issues/62848977 im Issue-Tracker von Google erstellt, und das Architecture Components Team hat angekündigt, eine native Lösung in oder nach Version 1.0 der Room-Bibliothek hinzuzufügen.
Temporäre Problemumgehung:
In der Zwischenzeit können Sie die von tknell erwähnte Lösung verwenden .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
quelle
Ab Version 2.1.0 scheint Room nicht für Modelle mit verschachtelten Beziehungen geeignet zu sein. Es brauchte viel Boilerplate-Code, um sie zu warten. ZB manuelles Einfügen von Listen, Erstellen und Zuordnen lokaler IDs.
Diese Relation-Mapping-Vorgänge werden von Requery https://github.com/requery/requery ausgeführt. Außerdem gibt es keine Probleme beim Einfügen von Enums und einige Konverter für andere komplexe Typen wie URI.
quelle
Ich habe es geschafft, es mit einer relativ einfachen Problemumgehung richtig einzufügen. Hier sind meine Entitäten:
@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;
Ich verwende autoGenerate für den Wert der automatischen Inkrementierung (long wird mit einem Zweck verwendet). Hier ist meine Lösung:
@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(); }
Ich hatte ein Problem beim Verknüpfen der Entitäten. Die automatische Generierung erzeugte ständig die "Rezept-ID = 0". Das Einfügen der Rezept-Entität hat es zunächst für mich behoben.
quelle