13

我在房間中使用Relation添加了一對多關係。 我參考this post來寫下下面的關係在房間中的代碼。Android室:使用房間插入關係實體

該帖子告訴如何從數據庫中讀取值,但將實體存儲到數據庫中導致userId爲空,這意味着2個表之間沒有關係。

我不確定什麼是insert a UserList 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)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; 
} 

現在來從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的問題跟蹤器。希望他們會爲此做點事情。

回答

5

由於房間不管理實體的關係,您必須自己設置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); 
} 
+0

我用'Stream'試了同樣的東西。我只是希望有更好的方法來做到這一點。 –

+0

我不認爲有更好的方法,因爲文檔說他們故意將引用從庫中留出:https://developer.android.com/topic/libraries/architecture/room.html#no-object-引用 – tknell

+0

我在問題跟蹤器上創建了此問題https://issuetracker.google.com/issues/62848977。希望他們會爲此做點事情。 –

1

目前還沒有本土的解決方案這個問題。我在Google的問題跟蹤器上創建了這個https://issuetracker.google.com/issues/62848977,架構組件小組表示他們將在室庫1.0版本或之後添加本地解決方案。

臨時的解決方法:

同時您也可以使用tknell提到的解決方案。

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

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

    petDao.insertAll(pets); 
} 
9

您可以通過將您的Dao從接口更改爲抽象類來實現。

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

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

您可能希望在一個PetDAO中使用insertAll(List<Pet>)insertPetsForUser(User, List<Pet>)方法,而不是......您如何分區您的DAO取決於您! :)

無論如何,這只是另一種選擇。在DataSource對象中包裝DAO也是可行的。

+0

將便捷方法放入抽象類而不是界面的好主意。 –

2

直到房間庫中的任何更新都沒有本地解決方案,但您可以通過一個竅門來做到這一點。找到下面提到的。

  1. 只是用寵物創建一個用戶(忽略寵物)。

    @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中。

    @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來解決。

+0

我喜歡這個,因爲它避免了UserWithPets POJO。通過使用默認方法,DAO也可以成爲一個接口。我看到的唯一缺點是insertUser()和insertPetList()是公共方法,但不應該被客戶端使用。我在這些方法的名稱前加了一個下劃線,以表明它們不應該被使用,如上所示。 – guglhupf