एंड्रॉइड रूम: रूम का उपयोग करके संबंध संस्थाएं डालें


90

मैं का उपयोग कर कमरे में कई रिश्ते के लिए एक जोड़ दिया है रिलेशन । मैंने इस पोस्ट को रूम में संबंध के लिए निम्नलिखित कोड लिखने के लिए संदर्भित किया है ।

पोस्ट बताता है कि डेटाबेस से मूल्यों को कैसे पढ़ा जाए लेकिन डेटाबेस में संस्थाओं को संग्रहीत करने के परिणामस्वरूप userIdरिक्त हो गया जिसका अर्थ है कि 2 तालिकाओं के बीच कोई संबंध नहीं है।

मुझे यकीन है कि करने के लिए आदर्श तरीका है क्या नहीं कर रहा हूँ 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:

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

अब DB से रिकॉर्ड प्राप्त करने के लिए हम निम्नलिखित का उपयोग करते हैं DAO:

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

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

संपादित करें

मैंने यह मुद्दा https://issuetracker.google.com/issues/62848977 समस्या ट्रैकर पर बनाया है । उम्मीद है कि वे इसके संबंध में कुछ करेंगे।


इसलिए वे कहते हैं कि "संबंध" 2.2 अद्यतन में प्राथमिकता है। वर्तमान संस्करण 4 दिसंबर 2018 से "रूम 2.1.0-alpha03" है
लेक Osinski

हाँ, मुद्दा ट्रैकर में उनकी टिप्पणी पढ़ें। यह समय लगने वाला है, इस बीच आप वर्कअराउंड का उपयोग कर सकते हैं
अक्षय चोरिया

जवाबों:


44

आप अपने डीएओ को एक इंटरफ़ेस से एक सार वर्ग में बदलकर ऐसा कर सकते हैं।

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

और फिर Dao 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>)तरीके रखना चाह सकते हैं ... आप अपने डीएओ को कैसे विभाजित करते हैं यह आपके ऊपर है! :)

वैसे भी, यह सिर्फ एक और विकल्प है। DataSource वस्तुओं में अपने DAO लपेटकर भी काम करता है।


इंटरफ़ेस के बजाय अमूर्त वर्ग में सुविधा पद्धति को रखने के लिए महान विचार।
अक्षय चोरिया

7
यदि हम पेटाओ में पालतू संबंधित विधियों को स्थानांतरित करने के लिए थे, तो हम उपयोगकर्ताडॉ के भीतर पेटाओ तरीकों का संदर्भ कैसे देंगे?
सबरबेन

4
आपके उत्तर के लिए धन्यवाद। लेकिन InsertPetsForUser (उपयोगकर्ता उपयोगकर्ता, सूची <पेट> पालतू) विधि में pet.setUserId (user.getId ()) कैसे करता है? चलिए मान लेते हैं कि आप किसी API से उपयोगकर्ता की वस्तु प्राप्त कर रहे हैं और इस समय इसका कोई id (प्रायमरी) नहीं है क्योंकि इसे RoomDb में सेव नहीं किया गया है, इसलिए user.getId () कॉल करने पर प्रत्येक के लिए केवल 0 का डिफ़ॉल्ट मान प्राप्त होगा। उपयोगकर्ता के रूप में इसे सहेजा जाना बाकी है। आप ऐसा कैसे संभालते हैं क्योंकि user.getId () हमेशा प्रत्येक उपयोगकर्ता के लिए 0 लौटाता है। धन्यवाद
Ikhiloya Imokhai

40

रूम लाइब्रेरी में किसी भी अपडेट तक कोई मूल समाधान नहीं है, लेकिन आप इसे एक चाल से कर सकते हैं। नीचे उल्लेखित खोजें।

  1. सिर्फ पेट्स के साथ एक यूजर बनाएं (पालतू जानवरों को इग्नोर करें)। गटर और सेटर जोड़ें। ध्यान दें कि हमें अपना Id मैन्युअल बाद में सेट करना होगा और उसका उपयोग नहीं किया जा सकता है autogenerate

    @Entity
    public class User {
          @PrimaryKey
          public int id; 
    
          @Ignore
          private List<Pet> petList;
    }
    
  2. एक पालतू पशु बनाएँ।

    @Entity 
    public class Pet 
    {
        @PrimaryKey
        public int id;     
        public int userId; 
        public String name;
    }
    
  3. 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;
    }
    

UserWithPets POJO बनाए बिना आपकी समस्या का समाधान किया जा सकता है।


2
मुझे यह पसंद है, क्योंकि यह UserWithPets POJO से बचा जाता है। डिफ़ॉल्ट तरीकों का उपयोग करके DAO एक इंटरफ़ेस भी हो सकता है। केवल मुझे लगता है कि इन्सर्टयूज़र () और इन्सर्टपेटिस्ट () सार्वजनिक तरीके हैं, लेकिन इसका इस्तेमाल किसी ग्राहक द्वारा नहीं किया जाना चाहिए। मैं इन तरीकों के नाम से पहले एक अंडरस्कोर लगाता हूं ताकि यह दिखाया जा सके कि उनका उपयोग नहीं किया जाना चाहिए, जैसा कि ऊपर दिखाया गया है।
गुगलुफ़

1
क्या कोई यह दिखा सकता है कि किसी गतिविधि में इसे ठीक से कैसे लागू किया जाए? सही पता है कि मैं नहीं जानता कि कैसे ठीक से Ids उत्पन्न करने के लिए।
फिलिप

@Philipp मैं आईडी बनाने के लिए काम कर रहा हूं, क्या आपको कोई समाधान मिला?
सोसाइटी

1
आपके उत्तर के लिए
धन्यवाद

2
धन्यवाद @Philipp मुझे इसके लचीलेपन के लिए आपका जवाब पसंद है! ऑटो जेनरेटिंग आईडी के लिए, मेरे मामले में मैं insertUser()ऑटो-जेनरेट होने के लिए पहले कॉल करता हूं userId, फिर userIdपेट क्लास में यूजरआईडी फील्ड को असाइन करता हूं insertPet()
रॉबर्ट

11

जैसा कि कक्ष संस्थाओं के संबंधों का प्रबंधन नहीं करता है, आपको 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। मुझे उम्मीद है कि इसे करने का एक बेहतर तरीका है।
अक्षय चोरिया

मुझे नहीं लगता कि एक बेहतर तरीका है, जैसा कि दस्तावेज़ीकरण कहता है कि उन्होंने जानबूझकर लाइब्रेरी के संदर्भ छोड़ दिए: डेवलपर.
android.com/topic/lbooks/altecture/…

मैंने इस मुद्दे को ट्रैकर पर जारी करने वाला। URLissues/62848977 बनाया है । उम्मीद है कि वे इसके संबंध में कुछ करेंगे।
अक्षय चोरिया

1
वास्तव में एक देशी समाधान नहीं है, लेकिन आपको DAO के लिए इंटरफेस का उपयोग करने की आवश्यकता नहीं है, आप अमूर्त कक्षाओं का भी उपयोग कर सकते हैं ... जिसका अर्थ है कि यदि आप एक आवरण वर्ग नहीं रखना चाहते हैं, तो सुविधा विधियाँ DAO में ही बैठ सकती हैं। अधिक जानकारी के लिए मेरा उत्तर देखें ...
कीरन मैकडोनाल्ड-हॉल

6

वर्तमान में इस समस्या का कोई देशी समाधान नहीं है । मैंने यह https://issuetracker.google.com/issues/62848977 Google के इश्यू ट्रैकर और आर्किटेक्चर कंपोनेंट्स टीम पर बनाया है, उन्होंने कहा कि वे v1.0 या लाइब्रेरी लाइब्रेरी के बाद एक मूल समाधान जोड़ देंगे।

अस्थायी समाधान:

इस बीच आप tknell द्वारा वर्णित समाधान का उपयोग कर सकते हैं ।

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

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

    petDao.insertAll(pets);
}

अन्य समाधानों को देखते हुए, मैं इस बारे में जाने का तरीका देख रहा हूं कि डाओ को बनाते समय एक अंतरफलक के बजाय एक अमूर्त वर्ग का उपयोग किया जा रहा है और उन अमूर्त वर्गों के समान ठोस तरीकों को जोड़ रहा है। लेकिन, मुझे अभी भी समझ नहीं आ रहा है कि इन तरीकों के अंदर चाइल्ड डाओ उदाहरण कैसे प्राप्त करें? जैसे। आप उपयोगकर्ताडॉ के अंदर पेटाओ उदाहरण कैसे प्राप्त कर सकते हैं?
पुरुष पवार

5

अब v2.1.0 पर कमरा नेस्टेड संबंधों वाले मॉडल के लिए उपयुक्त नहीं है। इसे बनाए रखने के लिए इसे बहुत सारे बॉयलरप्लेट कोड की आवश्यकता थी। उदाहरण के लिए स्थानीय आईडी की सूची बनाना, बनाना और मैप करना।

यह संबंध-मानचित्रण संचालन बॉक्स के बाहर किया जाता है। आवश्यक https://github.com/requery/requery अतिरिक्त द्वारा इसमें Enums डालने के मुद्दे नहीं हैं और URI जैसे अन्य जटिल प्रकारों के लिए कुछ कन्वर्टर्स हैं।


2

मैं इसे अपेक्षाकृत सरल वर्कअराउंड के साथ ठीक से सम्मिलित करने में कामयाब रहा। यहाँ मेरी इकाइयाँ हैं:

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

मैं स्वतः वृद्धि मूल्य के लिए ऑटो-जेनरेट का उपयोग कर रहा हूं (लंबे समय तक एक पर्स के साथ प्रयोग किया जाता है)। यहाँ मेरा समाधान है:

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

मुझे इकाइयों को जोड़ने में समस्या थी, ऑटो ने "रेसिपी = 0" का उत्पादन हर समय किया। नुस्खा इकाई को सम्मिलित करते हुए सबसे पहले यह मेरे लिए तय किया।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.