Dodałem jedną do wielu relacji w pokoju przy użyciu relacji . Odniosłem się do tego postu, aby napisać następujący kod dla relacji w Pokoju.
Post mówi, jak odczytać wartości z bazy danych, ale przechowywanie jednostek w bazie danych spowodowało, userId
że było puste, co oznacza, że nie ma związku między dwiema tabelami.
Nie jestem pewien, co jest idealnym sposobem na i do bazy danych, mając jednocześnie wartość.insert
User
List of Pet
userId
1) Podmiot użytkownika:
@Entity
public class User {
@PrimaryKey
public int id; // User id
}
2) Zwierzę domowe:
@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;
}
Teraz, aby pobrać rekordy z DB, używamy DAO
:
@Dao
public interface UserDao {
@Insert
fun insertUser(user: User)
@Query("SELECT * FROM User")
public List<UserWithPets> loadUsersWithPets();
}
EDYTOWAĆ
Utworzyłem ten problem https://issuetracker.google.com/issues/62848977 w narzędziu do śledzenia problemów. Miejmy nadzieję, że coś z tym zrobią.
android
android-room
android-architecture-components
Akshay Chordiya
źródło
źródło
Odpowiedzi:
Możesz to zrobić, zmieniając swoje Dao z interfejsu na klasę abstrakcyjną.
@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(); }
Możesz także pójść dalej, mając
User
obiekt z rozszerzeniem@Ignored List<Pet> pets
@Entity public class User { @PrimaryKey public int id; // User id @Ignored public List<Pet> pets }
a następnie Dao może mapować
UserWithPets
na użytkownika: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; }
To pozostawia ci pełne 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(); }
Zamiast tego możesz chcieć mieć metody
insertAll(List<Pet>)
iinsertPetsForUser(User, List<Pet>)
w PetDAO ... to, jak podzielisz swoje DAO, zależy od Ciebie! :)W każdym razie to tylko inna opcja. Pakowanie DAO w obiektach DataSource również działa.
źródło
Nie ma natywnego rozwiązania aż do jakiejkolwiek aktualizacji w bibliotece pokoi, ale możesz to zrobić podstępem. Znajdź poniżej wymienione.
Po prostu utwórz użytkownika ze zwierzętami (Ignoruj zwierzęta). Dodaj getter i setter. Zauważ, że musimy później ręcznie ustawić nasz identyfikator i nie możemy go użyć
autogenerate
.@Entity public class User { @PrimaryKey public int id; @Ignore private List<Pet> petList; }
Utwórz zwierzaka.
@Entity public class Pet { @PrimaryKey public int id; public int userId; public String name; }
Klasa User replace powinna być klasą abstrakcyjną, a nie interfejsem. Następnie w końcu w Twoim User√.
@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; }
Twój problem może zostać rozwiązany przez to bez tworzenia UserWithPets POJO.
źródło
insertUser()
najpierw dzwonię, aby uzyskać automatycznie wygenerowaneuserId
, a następnie przypisuję touserId
do pola userId w klasie Pet, a następnie zapętlam doinsertPet()
.Ponieważ Room nie zarządza relacjami między bytami, musisz samodzielnie ustawić
userId
na każdym zwierzaku i je zapisać. O ile nie ma zbyt wielu zwierząt na raz, użyłbyminsertAll
metody, aby była krótka.@Dao public interface PetDao { @Insert void insertAll(List<Pet> pets); }
Nie sądzę, że w tej chwili jest lepszy sposób.
Aby ułatwić obsługę, użyłbym abstrakcji w warstwie powyżej DAO:
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
źródło
Stream
. Mam tylko nadzieję, że jest na to lepszy sposób.Obecnie nie ma natywnego rozwiązania tego problemu. Utworzyłem to https://issuetracker.google.com/issues/62848977 w narzędziu Google do śledzenia problemów, a zespół Architecture Components powiedział, że doda natywne rozwiązanie w wersji 1.0 biblioteki Room lub później.
Tymczasowe obejście:
W międzyczasie możesz skorzystać z rozwiązania wspomnianego przez tknell .
public void insertPetsForUser(User user, List<Pet> pets){ for(Pet pet : pets){ pet.setUserId(user.getId()); } petDao.insertAll(pets); }
źródło
Teraz w wersji 2.1.0 Pokój wydaje się być nieodpowiedni dla modeli z zagnieżdżonymi relacjami. Do ich utrzymania potrzebne było mnóstwo gotowego kodu. Np. Ręczne wstawianie list, tworzenie i mapowanie lokalnych identyfikatorów.
Te operacje mapowania relacji są wykonywane po wyjęciu z pudełka przez Requery https://github.com/requery/requery Dodatkowo nie ma problemów z wstawianiem wyliczeń i ma kilka konwerterów dla innych złożonych typów, takich jak URI.
źródło
Udało mi się go poprawnie wstawić za pomocą stosunkowo prostego obejścia. Oto moje podmioty:
@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;
Używam autoGenerate do automatycznego zwiększania wartości (long jest używany z celami). Oto moje rozwiązanie:
@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(); }
Miałem problem z łączeniem encji, przez cały czas automatycznie generowałem produkowany "recipeId = 0". Wstawienie encji przepisu najpierw naprawiło to za mnie.
źródło