Android Odası: Room'u kullanarak ilişki varlıkları ekleyin


90

İlişkiyi kullanarak Room'daki birçok ilişkiye bir tane ekledim . Ben anılacaktır bu yazıOdadaki ilişki için aşağıdaki kodu yazmak başvurdum.

Gönderi, değerlerin veritabanından nasıl okunacağını anlatır, ancak varlıkları veritabanına depolamak userId boş bu da 2 tablo arasında ilişki olmadığı anlamına gelir.

Ben ideal yolu emin ne değilim insertbir Userve List of Petyerken veritabanınauserId değer.

1) Kullanıcı Varlığı:

@Entity
public class User {
    @PrimaryKey
    public int id; // User id
}

2) Evcil Hayvan Varlığı:

@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;
}

Şimdi kayıtları DB'den almak için aşağıdakileri kullanıyoruz DAO:

@Dao
public interface UserDao {
    @Insert
    fun insertUser(user: User)

    @Query("SELECT * FROM User")
    public List<UserWithPets> loadUsersWithPets();
}

DÜZENLE

Bu sorunu https://issuetracker.google.com/issues/62848977 sorun izleyicide oluşturdum. Umarım bununla ilgili bir şeyler yaparlar.


Yani 2.2 güncellemesinde "ilişkiler" önceliktir diyorlar. 4 Aralık 2018 tarihli "Room 2.1.0-alpha03" güncel sürümüdür.
Lech Osiński

Evet, sorun takipçisindeki yorumlarını okuyun. Bu zaman alacak, bu arada geçici çözümleri kullanabilirsiniz
Akshay Chordiya

Yanıtlar:


44

Bunu, Dao'nuzu bir arayüzden soyut bir sınıfa dönüştürerek yapabilirsiniz.

@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();
}

Bir Usernesneye sahip olarak da daha ileri gidebilirsiniz.@Ignored List<Pet> pets

@Entity
public class User {
    @PrimaryKey
    public int id; // User id

    @Ignored
    public List<Pet> pets
}

ve ardından Dao, UserWithPetsKullanıcıyla eşleşebilir :

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;
}

Bu sizi tam Dao ile baş başa bırakır:

@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();
}

Bunun yerine bir PetDAO'da insertAll(List<Pet>)ve insertPetsForUser(User, List<Pet>)yöntemlerine sahip olmak isteyebilirsiniz ... DAO'larınızı nasıl bölümlere ayıracağınız size kalmış! :)

Her neyse, bu sadece başka bir seçenek. DAO'larınızı DataSource nesnelerine sarmak da işe yarar.


Kolaylık yöntemini arayüz yerine soyut sınıfa koymak harika bir fikir.
Akshay Chordiya

7
Pet ile ilgili yöntemleri Petdao'ya taşıyacak olsaydık, Userdao'nun içindeki Petdao yöntemlerine nasıl başvururduk?
sbearben

4
Cevabınız için teşekkürler. Fakat insertPetsForUser (User user, List <Pet> Pets) metodundaki pet.setUserId (user.getId ()) userId'yi nasıl ayarlar? Kullanıcı nesnesini bir API'den aldığınızı ve bu noktada RoomDb'ye kaydedilmediğinden bu noktada kimliği (primaryKey) olmadığını varsayalım, bu nedenle user.getId () 'yi çağırmak her biri için yalnızca varsayılan bir 0 değeri verecektir. henüz kaydedilmediği için kullanıcı. User.getId () her bir kullanıcı için her zaman 0 döndürdüğü için bunu nasıl halledersiniz. Teşekkürler
Ikhiloya Imokhai

40

Oda Kitaplığındaki herhangi bir güncellemeye kadar yerel bir çözüm yoktur, ancak bunu bir hile ile yapabilirsiniz. Aşağıda belirtilen bulun.

  1. Sadece Evcil Hayvanlarla Bir Kullanıcı Oluşturun (Evcil hayvanları yok sayın). Alıcı ve ayarlayıcı ekleyin. Kimliklerimizi daha sonra manuel olarak ayarlamamız gerektiğine ve kullanamayacağımıza dikkat edin autogenerate.

    @Entity
    public class User {
          @PrimaryKey
          public int id; 
    
          @Ignore
          private List<Pet> petList;
    }
    
  2. Bir Pet oluşturun.

    @Entity 
    public class Pet 
    {
        @PrimaryKey
        public int id;     
        public int userId; 
        public String name;
    }
    
  3. User Dao, Arabirim yerine soyut bir sınıf olmalıdır. Sonra nihayet Kullanıcıdao'nuzda.

    @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;
    }
    

UserWithPets POJO oluşturmadan probleminiz çözülebilir.


2
Bunu beğendim çünkü UserWithPets POJO'dan kaçınıyor. Varsayılan yöntemleri kullanarak DAO bir arayüz de olabilir. Gördüğüm tek dezavantajı, insertUser () ve insertPetList () 'in genel yöntemler olduğu, ancak bir istemci tarafından kullanılmaması gerektiğidir. Yukarıda gösterildiği gibi kullanılmaması gerektiğini göstermek için bu yöntemlerin adının önüne bir alt çizgi koyuyorum.
guglhupf

1
Birisi bunu bir Faaliyette doğru şekilde nasıl uygulayacağını gösterebilir mi? Kimlikleri düzgün bir şekilde nasıl oluşturacağımı bilmediğimi biliyorum.
Philipp

@Philipp Kimliklerin nasıl oluşturulacağıyla uğraşıyorum, bir çözüm buldunuz mu?
sochas

1
Cevabınız için teşekkürler @Philipp, ben de aynı şekilde yaptım :)
sochas

2
Teşekkürler @Philipp Esnekliği için cevabınızı beğendim! Oto üreten kimlikleri için benim durumumda ben çağrı insertUser()otomatik olarak oluşturulmuş olsun öncelikle userId, o atamak userIdardından döngü, Hayvan sınıfında userId alanına insertPet().
Robert

11

Oda varlıkların İlişkilerini yönetmediğinden, userIdher evcil hayvanı kendiniz ayarlamanız ve kaydetmeniz gerekir. Aynı anda çok fazla evcil hayvan olmadığı sürece, insertAllkısa tutmak için bir yöntem kullanırım.

@Dao
public interface PetDao {
    @Insert
    void insertAll(List<Pet> pets);
}

Şu anda daha iyi bir yol olduğunu sanmıyorum.

İşlemeyi kolaylaştırmak için, DAO'ların üzerindeki katmanda bir soyutlama kullanırdım:

public void insertPetsForUser(User user, List<Pet> pets){

    for(Pet pet : pets){
        pet.setUserId(user.getId());
    }

    petDao.insertAll(pets);
}

Ben de aynısını kullanmayı denedim Stream. Umarım bunu yapmanın daha iyi bir yolu vardır.
Akshay Chordiya

Belgeler, referansları kasıtlı olarak kitaplığın dışında bıraktıklarını söylediği için daha iyi bir yol olduğunu sanmıyorum: developer.android.com/topic/libraries/architecture/…
tknell

Sorun izleyicide bu sorunu issuetracker.google.com/issues/62848977 oluşturdum . Umarım bununla ilgili bir şeyler yaparlar.
Akshay Chordiya

1
Gerçek bir yerel çözüm değil, ancak DAO'lar için arabirimler kullanmak zorunda değilsiniz, soyut sınıfları da kullanabilirsiniz ... yani bir sarmalayıcı sınıfına sahip olmak istemiyorsanız, uygunluk yöntemleri DAO'nun içinde oturabilir. Daha fazla bilgi için cevabımı görün ...
Kieran Macdonald-Hall

6

Şu anda bu soruna yerel bir çözüm yok . Bu https://issuetracker.google.com/issues/62848977'yi Google'ın sorun izleyicisinde oluşturdum ve Mimari Bileşenler Ekibi, Oda kitaplığının v1.0'ına veya sonrasında yerel bir çözüm ekleyeceklerini söyledi.

Geçici Çözüm:

Bu arada tknell'in bahsettiği çözümü kullanabilirsiniz .

public void insertPetsForUser(User user, List<Pet> pets){

    for(Pet pet : pets){
        pet.setUserId(user.getId());
    }

    petDao.insertAll(pets);
}

Diğer çözümlere baktığımda, bunu yapmanın yolunun, dao oluştururken arayüz yerine soyut bir sınıf kullanmak ve bu soyut sınıfların bir kısmına benzer somut yöntemler eklemek olduğunu görüyorum. Ama yine de bu yöntemlerde çocuk dao örneğinin nasıl alınacağını anlayamıyorum? Örneğin. user Dao'nun içinde evcil hayvanın örneğini nasıl elde edebilirsiniz?
Purush Pawar

5

Şimdi v2.1.0'da Oda, iç içe ilişkiler içeren modeller için uygun görünmüyor. Onları korumak için çok sayıda standart kod gerekiyordu. Örneğin, listelerin manuel olarak eklenmesi, yerel kimliklerin oluşturulması ve haritalanması.

Bu ilişki eşleştirme işlemleri, Gereksinim https://github.com/requery/requery tarafından kutunun dışında yapılır.Ayrıca, Enums ekleme ile ilgili sorunları yoktur ve URI gibi diğer karmaşık türler için bazı dönüştürücülere sahiptir.


2

Nispeten basit bir çözümle düzgün bir şekilde yerleştirmeyi başardım. İşte varlıklarım:

   @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;

Otomatik artış değeri için autoGenerate kullanıyorum (uzun bir amaç için kullanılır). İşte benim çözümüm:

  @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();
 }

Varlıkları bağlamada sorun yaşadım, her zaman otomatik olarak üretilen "recipeId = 0" oluştur. Reçete varlığının eklenmesi önce benim için düzeltildi.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.