Eu adicionei um relacionamento de muitos na Room usando Relation . Eu me referi a esta postagem para escrever o seguinte código para relacionamento na Room.
A postagem informa como ler os valores do banco de dados, mas armazenar as entidades no banco de dados resultou em userId
um vazio, o que significa que não há relação entre as 2 tabelas.
Eu não tenho certeza qual é o caminho ideal para insert
um User
e List of Pet
para o banco de dados, enquanto o userId
valor.
1) Entidade do usuário:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Entidade Pet:
@Entity
public class Pet {
@PrimaryKey
public int id; // Pet id
public int userId; // User id
public String name;
}
3) POJO UserWithPets:
// 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;
}
Agora, para buscar os registros do banco de dados, usamos o seguinte DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
EDITAR
Eu criei esse problema https://issuetracker.google.com/issues/62848977 no rastreador de problemas. Esperançosamente, eles farão algo a respeito.
android
android-room
android-architecture-components
Akshay Chordiya
fonte
fonte
Respostas:
Você pode fazer isso alterando seu Dao de uma interface para uma classe abstrata.
@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(); }
Você também pode ir mais longe fazendo com que um
User
objeto tenha um@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
e então o Dao pode mapear
UserWithPets
para o usuário: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; }
Isso deixa você com o 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(); }
Em vez disso, você pode querer ter os métodos
insertAll(List<Pet>)
einsertPetsForUser(User, List<Pet>)
em um PetDAO ... como você particiona seus DAOs depende de você! :)De qualquer forma, é apenas outra opção. Encapsular seus DAOs em objetos DataSource também funciona.
fonte
Não há solução nativa até qualquer atualização na Biblioteca de quartos, mas você pode fazer isso com um truque. Encontre abaixo mencionado.
Basta criar um usuário com animais de estimação (ignorar animais de estimação). Adicione getter e setter. Observe que temos que definir nosso Id manualmente mais tarde e não podemos usar
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Crie um animal de estimação.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
O UserDao deve ser uma classe abstrata em vez de uma Interface. Então, finalmente, em seu 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; }
Seu problema pode ser resolvido sem criar UserWithPets POJO.
fonte
insertUser()
primeiro para ser gerado automaticamenteuserId
, depois atribuo issouserId
ao campo userId na classe Pet e, em seguida, faço um loop parainsertPet()
.Como a Room não gerencia as relações das entidades, você mesmo deve definir o
userId
em cada animal de estimação e salvá-los. Contanto que não haja muitos animais de estimação ao mesmo tempo, eu usaria uminsertAll
método para mantê-lo curto.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Não acho que haja maneira melhor no momento.
Para tornar o manuseio mais fácil, eu usaria uma abstração na camada acima dos DAOs:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
fonte
Stream
. Só espero que haja uma maneira melhor de fazer isso.Atualmente não existe uma solução nativa para este problema. Eu criei isso https://issuetracker.google.com/issues/62848977 no rastreador de problemas do Google e a equipe de componentes de arquitetura disse que vai adicionar uma solução nativa na ou após a v1.0 da biblioteca Room.
Solução temporária:
Enquanto isso, você pode usar a solução mencionada por tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
fonte
Agora na v2.1.0 Room parece não ser adequado para modelos com relações aninhadas. Era necessário muito código clichê para mantê-los. Ex: inserção manual de listas, criação e mapeamento de IDs locais.
Essas operações de mapeamento de relações são feitas imediatamente por Requery https://github.com/requery/requery. Além disso, ele não tem problemas com a inserção de Enums e possui alguns conversores para outros tipos complexos como URI.
fonte
Consegui inseri-lo corretamente com uma solução alternativa relativamente simples. Aqui estão minhas 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;
Estou usando autoGenerate para valor de incremento automático (long é usado com um propósito). Aqui está minha solução:
@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(); }
Tive problemas para vincular as entidades, gerar automaticamente "recipeId = 0" produzido o tempo todo. Inserir a entidade de receita primeiro consertou para mim.
fonte