Я добавил отношения "один ко многим" в Room с помощью Relation . Я сослался на этот пост, чтобы написать следующий код для отношения в Room.
В сообщении рассказывается, как читать значения из базы данных, но сохранять объекты в базе данных, в результате чего userId
они пусты, что означает отсутствие связи между двумя таблицами.
Я не уверен , что это идеальный способ и в базу данных, имея значение.insert
User
List of Pet
userId
1) Пользовательский объект:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Домашнее животное:
@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;
}
Теперь для получения записей из БД мы используем следующее DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
РЕДАКТИРОВАТЬ
Я создал эту проблему https://issuetracker.google.com/issues/62848977 в системе отслеживания проблем. Надеюсь, они что-то с этим сделают.
android
android-room
android-architecture-components
Акшай Чордия
источник
источник
Ответы:
Вы можете сделать это, изменив свой Дао с интерфейса на абстрактный класс.
@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(); }
Вы также можете пойти дальше, если у
User
объекта будет@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
а затем Дао может отображаться
UserWithPets
на пользователя: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; }
Это оставляет вам полное Дао:
@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(); }
Вы можете иметь
insertAll(List<Pet>)
иinsertPetsForUser(User, List<Pet>)
методы в PetDAO вместо ... как вы разделите ваши объекты DAO до вас! :)Во всяком случае, это просто еще один вариант. Также работает упаковка ваших DAO в объекты DataSource.
источник
До каких-либо обновлений в библиотеке комнат нет собственного решения, но вы можете сделать это хитростью. Найдите ниже упомянутое.
Просто создайте пользователя с домашними животными (игнорируйте домашних животных). Добавьте геттер и сеттер. Обратите внимание, что мы должны установить наш идентификатор вручную позже и не можем использовать
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Создайте питомца.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
UserDao должен быть абстрактным классом, а не интерфейсом. Затем, наконец, в вашем 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; }
Таким образом, ваша проблема может быть решена без создания POJO UserWithPets.
источник
insertUser()
сначала вызываю, чтобы автоматически сгенерироватьuserId
, затем назначаю этоuserId
полю userId в классе Pet, а затем зацикливаюсь наinsertPet()
.Поскольку Room не управляет Отношениями сущностей, вы должны сами установить для
userId
каждого питомца и сохранить их. Пока домашних животных не слишком много одновременно, я бы использовалinsertAll
способ сделать это коротким.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Я не думаю, что на данный момент есть лучший способ.
Чтобы упростить обработку, я бы использовал абстракцию на уровне над DAO:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
источник
Stream
. Я просто надеюсь, что есть способ лучше.В настоящее время нет собственного решения этой проблемы. Я создал этот https://issuetracker.google.com/issues/62848977 в системе отслеживания проблем Google, и команда компонентов архитектуры заявила, что добавит собственное решение в библиотеку Room версии 1.0 или позже.
Временное решение:
Тем временем вы можете использовать решение, упомянутое tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
источник
Теперь в v2.1.0 Room кажется не подходящим для моделей с вложенными отношениями. Для их поддержки требовалось много шаблонного кода. Например, вставка списков вручную, создание и отображение локальных идентификаторов.
Эти операции сопоставления отношений выполняются из коробки Requery https://github.com/requery/requery. Дополнительно у него нет проблем со вставкой перечислений и есть некоторые преобразователи для других сложных типов, таких как URI.
источник
Мне удалось правильно вставить его с помощью относительно простого обходного пути. Вот мои сущности:
@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;
Я использую autoGenerate для автоматического увеличения значения (long используется с определенными целями). Вот мое решение:
@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(); }
У меня была проблема со связью сущностей, все время автоматически генерировалось "recipeId = 0". Вставка объекта рецепта сначала исправила это для меня.
источник