He agregado una relación a muchas en Room usando Relation . Me referí a esta publicación para escribir el siguiente código de relación en Room.
La publicación dice cómo leer los valores de la base de datos, pero almacenar las entidades en la base de datos resultó en userId
estar vacío, lo que significa que no hay relación entre las 2 tablas.
No estoy seguro de lo que es la forma ideal para insert
una User
y List of Pet
en la base de datos mientras que tener userId
valor.
1) Entidad de usuario:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Entidad de mascota:
@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;
}
Ahora, para obtener los registros de la base de datos, usamos lo siguiente DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
EDITAR
He creado este problema https://issuetracker.google.com/issues/62848977 en el rastreador de problemas. Con suerte, harán algo al respecto.
fuente
Respuestas:
Puede hacer esto cambiando su Dao de una interfaz a una clase abstracta.
@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(); }
También puede ir más allá haciendo que un
User
objeto tenga una@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
y luego el Dao puede mapear
UserWithPets
al Usuario: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; }
Esto te deja con el Dao completo:
@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(); }
Es posible que desee tener los métodos
insertAll(List<Pet>)
yinsertPetsForUser(User, List<Pet>)
en un PetDAO en su lugar ... ¡cómo particione sus DAO depende de usted! :)De todos modos, es solo otra opción. Envolver sus DAO en objetos DataSource también funciona.
fuente
No hay una solución nativa hasta cualquier actualización en la biblioteca de habitaciones, pero puede hacerlo con un truco. Encuentra abajo mencionado.
Simplemente cree un usuario con mascotas (ignore las mascotas). Agrega getter y setter. Tenga en cuenta que tenemos que configurar nuestro Id manualmente más tarde y no podemos usar
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Crea una mascota.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
UserDao debería ser una clase abstracta en lugar de una interfaz. Luego, finalmente en su 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; }
Su problema puede resolverse con esto sin crear UserWithPets POJO.
fuente
insertUser()
primero para generarlo automáticamenteuserId
, luego lo asignouserId
al campo userId en la clase Pet y luego lo hago en bucleinsertPet()
.Como Room no gestiona las Relaciones de las entidades, debes configurarlo
userId
tú mismo en cada mascota y guardarlas. Siempre que no haya demasiadas mascotas a la vez, usaría uninsertAll
método para que sea breve.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
No creo que haya una mejor manera en este momento.
Para facilitar el manejo, usaría una abstracción en la capa sobre los DAO:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
fuente
Stream
. Solo espero que haya una mejor manera de hacerlo.Actualmente no existe una solución nativa para este problema. Creé este https://issuetracker.google.com/issues/62848977 en el rastreador de problemas de Google y el Equipo de Componentes de Arquitectura dijo que agregarán una solución nativa en o después de la v1.0 de la biblioteca de Room.
Solución temporal:
Mientras tanto, puede utilizar la solución mencionada por tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
fuente
Ahora, en v2.1.0 Room parece no ser adecuado para modelos con relaciones anidadas. Necesitaba mucho código repetitivo para mantenerlos. Por ejemplo, inserción manual de listas, creación y mapeo de ID locales.
Estas operaciones de mapeo de relaciones las realiza Requery https://github.com/requery/requery. Además, no tiene problemas para insertar Enums y tiene algunos convertidores para otros tipos complejos como URI.
fuente
Me las arreglé para insertarlo correctamente con una solución relativamente simple. Aquí están mis entidades:
@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;
Estoy usando autoGenerate para el valor de incremento automático (long se usa con propósitos). Aquí está mi solución:
@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(); }
Tuve problemas para vincular las entidades, la generación automática produjo "RecetaId = 0" todo el tiempo. Al insertar la entidad de receta, primero me lo arregló.
fuente