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

Ali*_*lin 10 android-room android-livedata android-viewmodel android-architecture-components android-jetpack

我们来看一个基本的例子

用于存储用户的表

@Entity (tableName="users") 
class UsersEntity(
     @PrimaryKey val id
     var name:String,
      ...
) 
Run Code Online (Sandbox Code Playgroud)

用于存储角色的表

@Entity (tableName="roles") 
class RolesEntity(
     @PrimaryKey val id
     var name:String,
      ...
) 
Run Code Online (Sandbox Code Playgroud)

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

@Entity (tableName="roles") 
class UserRoles(
     @PrimaryKey val id
     var userId:String,
     var roleId:String
) 
Run Code Online (Sandbox Code Playgroud)

我在视图中需要的pojo类

class user(
    var id:String,
    var name:String,
     .... other fields
     var roles: List<Role>
)
Run Code Online (Sandbox Code Playgroud)

在我,我ViewModel怎么能把user结果传递给LiveData已经List<Role>填满?

从一般方面来看,我可以:

  • UserDao.getUserById(id)返回LiveData从用户表,并RoleDao.getRolesForUserId(id)返回LiveData与用户的角色列表.然后在我的片段,我所能做的viewModel.getUserById().observe{}viewModel.getRolesForUserId().observe{}.但这基本上意味着拥有2名观察员,而且我非常有信心这不是可行的方法.
  • 可能其他方式是能够以某种方式在我的存储库或viewmodel中混合它们,以便它返回我需要的东西.我会调查一下MediatorLiveData

XII*_*-th 1

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


查看型号

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最好将数据源实现封装在Repository类中,并通过 DI 注入,如本段所示

基于本文中的多对多段落的数据库示例


实体

用户

@Entity(tableName = "users")
public class UsersEntity {
    @PrimaryKey
    public int id;
    public String name;
}
Run Code Online (Sandbox Code Playgroud)

角色

@Entity(tableName = "roles")
public class RolesEntity {
    @PrimaryKey
    public int id;
    public String name;
}
Run Code Online (Sandbox Code Playgroud)

用户角色

这个实体需要特别注意,因为我们需要声明外键来使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;
}
Run Code Online (Sandbox Code Playgroud)

用户道

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

角色刀

@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);
}
Run Code Online (Sandbox Code Playgroud)

数据库

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