web-dev-qa-db-fra.com

Android Pièce: Insérer des entités de relation à l'aide de Pièce

J'ai ajouté une relation sur plusieurs dans Room en utilisant Relation . Je me suis référé à ce post pour écrire le code suivant pour la relation dans Room.

La publication explique comment lire les valeurs de la base de données, mais le stockage des entités dans la base de données a pour résultat que userId est vide, ce qui signifie qu'il n'y a pas de relation entre les 2 tables.

Je ne sais pas quel est le moyen idéal pour insert a User et List of Pet dans la base de données tout en ayant la valeur userId.

1) Entité utilisateur:

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

2) Entité pour animaux de compagnie:

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

Maintenant, pour extraire les enregistrements de la base de données, nous utilisons le suivant DAO:

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

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

[~ # ~] éditer [~ # ~]

J'ai créé ce problème https://issuetracker.google.com/issues/62848977 dans l'outil de suivi du problème. J'espère qu'ils feront quelque chose à ce sujet.

46
Akshay Chordiya

Vous pouvez le faire en changeant votre Dao d'une interface à une classe abstraite.

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

Vous pouvez également aller plus loin en ayant un objet User un @Ignored List<Pet> pets

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

    @Ignored
    public List<Pet> pets
}

puis le Dao peut mapper UserWithPets à l'utilisateur:

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

Cela vous laisse avec le Dao complet:

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

Vous voudrez peut-être avoir les méthodes insertAll(List<Pet>) et insertPetsForUser(User, List<Pet>) dans un PetDAO ... la façon dont vous partitionnez vos DAO dépend de vous! :)

Quoi qu'il en soit, c'est juste une autre option. Envelopper vos DAO dans des objets DataSource fonctionne également.

28

Il n'y a pas de solution native avant une mise à jour dans Room Library, mais vous pouvez le faire par un truc. Trouvez ci-dessous mentionné.

  1. Il suffit de créer un utilisateur avec des animaux domestiques (ignorer les animaux domestiques).

    @Entity
    public class User {
          @PrimaryKey
          public int id; 
    
          @Ignore
          private List<Pet> petList;
    }
    
  2. Créer un animal de compagnie.

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

Ceci permet de résoudre votre problème sans créer de POJO UserWithPets.

22
Dipendra Sharma

Comme Room ne gère pas les relations des entités, vous devez définir vous-même le userId et le sauvegarder. Tant qu'il n'y a pas trop d'animaux à la fois, j'utiliserais une méthode insertAll pour rester bref.

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

Je ne pense pas qu'il existe un meilleur moyen pour le moment.

Pour faciliter la manipulation, j'utiliserais une abstraction dans la couche au-dessus des DAO:

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

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

    petDao.insertAll(pets);
}
6
tknell

Actuellement, il n'y a pas de solution native à ce problème. J'ai créé ceci https://issuetracker.google.com/issues/62848977 sur le suivi des problèmes de Google et l'équipe des composants d'architecture a déclaré qu'ils ajouteraient une solution native dans ou après la v1.0 de la bibliothèque Room.

Solution de contournement temporaire:

En attendant, vous pouvez utiliser la solution mentionnée par tknell .

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

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

    petDao.insertAll(pets);
}
3
Akshay Chordiya

Maintenant, à la v2.1.0, Room ne semble pas convenir aux modèles avec des relations imbriquées. Il avait besoin de beaucoup de code passe-partout pour les maintenir. Par exemple. insertion manuelle de listes, création et mappage d'identifiants locaux.

Ces opérations de mappage des relations sont effectuées prématurément par Requery https://github.com/requery/requery De plus, l'insertion d'énums n'est pas problématique et certains convertisseurs sont utilisés pour d'autres types complexes comme URI.

0
Serge