从Android Room Architecture Component中的多对多结构获取LiveData

时间:2018-07-31 13:05:35

标签: android-room android-architecture-components android-livedata android-viewmodel android-jetpack

让我们举一个基本的例子

用于存储用户的表

@Entity (tableName="users") 
class UsersEntity(
     @PrimaryKey val id
     var name:String,
      ...
) 

用于存储角色的表

@Entity (tableName="roles") 
class RolesEntity(
     @PrimaryKey val id
     var name:String,
      ...
) 

用于存储用户和角色之间多对多关系的表

@Entity (tableName="roles") 
class UserRoles(
     @PrimaryKey val id
     var userId:String,
     var roleId:String
) 

我在视图中需要的pojo类

class user(
    var id:String,
    var name:String,
     .... other fields
     var roles: List<Role>
)

在我的ViewModel中,如何将user也填满LiveData来传递List<Role>结果?

从一般的角度来看,我可以:

  • UserDao.getUserById(id),它从users表中返回LiveData,还有RoleDao.getRolesForUserId(id),它返回LiveData,其中包含用户的角色列表。然后在片段中,我可以做viewModel.getUserById().observe{}viewModel.getRolesForUserId().observe{}。但这基本上意味着要有2位观察员,我非常有信心这不是要走的路。
  • 可能的另一种方式是能够以某种方式在我的存储库或视图模型中混合它们,以便它返回我需要的东西。我将研究MediatorLiveData

2 个答案:

答案 0 :(得分:3)

使用用户及其角色创建其他模型,并使用@Embedded@Relation批注。

例如:

public class UserModel {
  @Embedded
  UserEntity user;
  @Relation(parentColumn = "id", entityColumn = "userId", entity = UserRoles.class)
  List<UserRoleModel> userRoles;
}

public class UserRoleModel {
  @Embedded
  UserRoles userRole;
  @Relation(parentColumn = "roleId", entityColumn = "id")
  List<RoleEntity> roles; // Only 1 item, but Room wants it to be a list.
}

您可以从此处使用UserModel

答案 1 :(得分:1)

在一个屏幕中可以有多个不同的数据流。
一方面,我们可以讨论在不更改用户本身的情况下更改用户角色列表,另一方面可以在不更新角色列表的情况下更改用户名。使用多个数据流的额外好处是,您可以在加载用户角色时显示用户数据。
我想,您可以使用用户和角色来避免同步问题。您可以像下面的示例中那样实现平稳的数据传递(从数据库到视图):


查看模型

public class UserRolesViewModel extends ViewModel {
    private final MutableLiveData<Integer> mSelectedUser;
    private final LiveData<UsersEntity> mUserData;
    private final LiveData<List<RolesEntity>> mRolesData;
    private DataBase mDatabase;

    public UserRolesViewModel() {
        mSelectedUser = new MutableLiveData<>();
        // create data flow for user and roles synchronized by mSelectedUser 
        mUserData = Transformations.switchMap(mSelectedUser, mDatabase.getUserDao()::getUser);
        mRolesData = Transformations.switchMap(mSelectedUser, mDatabase.getRolesDao()::getUserRoles);
    }

    public void setDatabase(DataBase dataBase) {
        mDatabase = dataBase;
    }

    @MainThread
    public void setSelectedUser(Integer userId) {
        if (mDatabase == null)
            throw new IllegalStateException("You need setup database before select user");
        mSelectedUser.setValue(userId);
    }

    public LiveData<UsersEntity> getUserData() {
        return mUserData;
    }

    public LiveData<List<RolesEntity>> getRolesData() {
        return mRolesData;
    }
}

最好将数据源实现封装在Repository类中,然后像this paragraph一样通过DI注入。

基于this article

多对多段落的数据库示例

实体

用户

@Entity(tableName = "users")
public class UsersEntity {
    @PrimaryKey
    public int id;
    public String name;
}

角色

@Entity(tableName = "roles")
public class RolesEntity {
    @PrimaryKey
    public int id;
    public String name;
}

用户角色

这个实体需要特别注意,因为我们需要声明外键来使 joun 操作更有意义

@Entity(tableName = "user_roles", primaryKeys = {"user_id", "role_id"}, foreignKeys = {
    @ForeignKey(entity = UsersEntity.class, parentColumns = "id", childColumns = "user_id"),
    @ForeignKey(entity = RolesEntity.class, parentColumns = "id", childColumns = "role_id")
})
public class UserRolesEntity {
    @ColumnInfo(name = "user_id")
    public int userId;
    @ColumnInfo(name = "role_id")
    public int roleId;
}

Dao

用户dao

@Dao
public interface UserDao {
    @Query("SELECT * from users WHERE id = :userId")
    LiveData<UsersEntity> getUser(int userId);
}

Roles dao

@Dao
public interface RolesDao {
    @Query("SELECT * FROM roles INNER JOIN user_roles ON roles.id=user_roles.role_id WHERE user_roles.user_id = :userId")
    LiveData<List<RolesEntity>> getUserRoles(int userId);
}

数据库

@Database(entities = {UsersEntity.class, RolesEntity.class, UserRolesEntity.class}, version = 1)
public abstract class DataBase extends RoomDatabase {
    public abstract UserDao getUserDao();
    public abstract RolesDao getRolesDao();
}