(六)使用Room引用复杂数据

Room提供了在原始类型和盒装类型之间进行转换的功能,但不允许实体之间的对象引用。 本文档介绍了如何使用类型转换器以及Room不支持对象引用的原因。

一、使用类型转换器

有时,您的应用需要使用您希望将其值存储在单个数据库列中的自定义数据类型。 要为自定义类型添加此类支持,您需要提供TypeConverter,它将自定义类转换为Room可以保留的已知类型。

例如,如果我们想要保留Date的实例,我们可以编写以下TypeConverter来在数据库中存储等效的Unix时间戳:

public class Converters {
    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value);
    }

    @TypeConverter
    public static Long dateToTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}

前面的示例定义了2个函数,一个将Date对象转换为Long对象,另一个函数执行逆转换,从Long到Date。 由于Room已经知道如何持久化Long对象,因此它可以使用此转换器来持久化Date类型的值。

接下来,将@TypeConverters注释添加到AppDatabase类,以便Room可以使用您为该AppDatabase中的每个实体和DAO定义的转换器:

AppDatabase.java
@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

使用这些转换器,您可以在其他查询中使用自定义类型,就像使用基本类型一样,如以下代码段所示:

User.java
@Entity
public class User {
    ...
    private Date birthday;
}
UserDao.java
@Dao
public interface UserDao {
    ...
    @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
    List findUsersBornBetweenDates(Date from, Date to);
}

您还可以将@TypeConverters限制为不同的范围,包括单个实体,DAO和DAO方法。 有关更多详细信息,请参阅@TypeConverters批注的参考文档。

二、了解Room为何不允许对象引用

关键点:房间不允许实体类之间的对象引用。 相反,您必须明确请求您的应用所需的数据。
将数据库中的关系映射到相应的对象模型是一种常见的做法,并且在服务器端非常有效。即使程序在访问字段时加载字段,服务器仍然可以正常运行。

但是,在客户端,这种类型的延迟加载是不可行的,因为它通常发生在UI线程上,并且在UI线程中查询磁盘上的信息会产生严重的性能问题。 UI线程通常有大约16毫秒来计算和绘制活动的更新布局,因此即使查询只需要5毫秒,您的应用程序仍然可能会用完时间来绘制框架,从而导致明显的视觉故障。如果并行运行单独的事务,或者设备正在运行其他磁盘密集型任务,则查询可能需要更多时间才能完成。但是,如果不使用延迟加载,则应用程序会获取超出其需要的数据,从而产生内存消耗问题。

对象关系映射通常会将此决定留给开发人员,以便他们可以为应用程序的用例做最好的事情。开发人员通常决定在他们的应用程序和UI之间共享模型。但是,此解决方案不能很好地扩展,因为随着UI的变化,共享模型会产生开发人员难以预测和调试的问题。

例如,考虑一个加载Book对象列表的UI,每个书都有一个Author对象。您最初可能会设计查询以使用延迟加载,以便Book的实例使用getAuthor()方法返回作者。第一次调用getAuthor()调用查询数据库。一段时间后,您意识到您还需要在应用程序的UI中显示作者姓名。您可以轻松添加方法调用,如以下代码段所示:

authorNameTextView.setText(book.getAuthor().getName());

但是,这种看似无辜的更改会导致在主线程上查询Author表。

如果提前查询作者信息,则在不再需要该数据时,很难更改数据的加载方式。 例如,如果您的应用程序的UI不再需要显示作者信息,那么您的应用程序会有效地加载不再显示的数据,从而浪费宝贵的内存空间。 如果Author类引用另一个表(例如Books),则应用程序的效率会进一步降低。

要使用Room同时引用多个实体,您需要创建一个包含每个实体的POJO,然后编写一个连接相应表的查询。 这种结构良好的模型与Room强大的查询验证功能相结合,可让您的应用在加载数据时消耗更少的资源,从而提高应用的性能和用户体验。

你可能感兴趣的:((六)使用Room引用复杂数据)