【问题标题】:Is it possible to enable/ disable transformation on selected Room Database Dao column?是否可以在选定的 Room Database Dao 列上启用/禁用转换?
【发布时间】:2019-09-06 16:14:06
【问题描述】:

目前,我们有以下数据库表

@Entity(
        tableName = "note"
)
public class Note {

    @ColumnInfo(name = "body")
    private String body;

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

正文字符串的长度,可以是0到一个很大的数字。

在某些情况下,我们需要

  1. 将所有笔记加载到内存中。
  2. LiveData 能够通知观察者,如果 SQLite note 表中有任何更改。
  3. 我们只需要body 的前256 个字符。我们不需要完整的body。为 all 笔记加载整个 body 字符串可能会导致 OutOfMemoryException。

我们有以下 Room Database Dao

@Dao
public abstract class NoteDao {
    @Query("SELECT * FROM note")
    public abstract LiveData<List<Note>> getAllNotes();
}

getAllNotes 能够满足要求 (1) 和 (2),但不能满足 (3)。


以下getAllNotesWithShortBody 是一个失败的解决方案。

@Dao
public abstract class NoteDao {
    @Query("SELECT * FROM note")
    public abstract LiveData<List<Note>> getAllNotes();

    @Query("SELECT * FROM note")
    public abstract List<Note> getAllNotesSync();

    public LiveData<List<Note>> getAllNotesWithShortBody() {
        MutableLiveData<List<Note>> notesLiveData = new MutableLiveData<>();

        //
        // Problem 1: Still can cause OutOfMemoryException by loading 
        // List of notes with complete body string.
        //
        List<Note> notes = getAllNotesSync();

        for (Note note : notes) {
            String body = note.getBody();

            // Extract first 256 characters from body string.
            body = body.substring(0, Math.min(body.length(), 256));

            note.setBody(body);
        }

        notesLiveData.postValue(notes);

        //
        // Problem 2: The returned LiveData unable to inform observers, 
        // if there's any changes made in the SQLite `note` table.
        //
        return notesLiveData;
    }
}

我想知道,有没有办法告诉Room database Dao:在以LiveData返回Notes列表之前,请对每个Note的正文列进行转换,将字符串修剪为最多256个字符? em>


查看 Room Dao 生成的源代码

如果我们看一下 Room Dao 生成的源代码

@Override
public LiveData<List<Note>> getAllNotes() {
  final String _sql = "SELECT * FROM note";
  final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
  ...
  ...
              final String _tmpBody;
              _tmpBody = _cursor.getString(_cursorIndexOfBody);
              _tmpPlainNote.setBody(_tmpBody);

如果有办法在运行时提供转换功能,那就太好了,这样我们就可以拥有

              final String _tmpBody;
              _tmpBody = transform_function(_cursor.getString(_cursorIndexOfBody));
              _tmpPlainNote.setBody(_tmpBody);

p/s 目前请不要反推荐 Paging 库,因为我们的某些功能需要内存中的整个笔记列表(带有修剪的正文字符串)。


【问题讨论】:

  • Sqlite 确实有一个substr 函数。我想你可以试试See this .. 我不确定它是如何处理溢出长度的。
  • 棘手的是,如何在 Room Dao 中使用 substr 并将其作为 LiveData 返回?
  • @Query("SELECT substr(body,1,256) FROM note")

标签: android android-room


【解决方案1】:

您可以使用 SUBSTR,SQLite's built-in functions 之一。

您的@Entity 中需要一个主键。假设你称它为id,你可以编写如下SQL。

@Query("SELECT id, SUBSTR(body, 0, 257) AS body FROM note")
public abstract LiveData<List<Note>> getAllNotes();

这将返回修剪为 256 个字符的 body。

话虽如此,您应该考虑对行进行分段。如果你有太多行,它们最终会在某个时候耗尽你的内存。使用分页是一种方法。您还可以使用 LIMIT 和 OFFSET 手动遍历行段。

【讨论】:

  • Argh...我不知道我可以为 Room 构建一个完整的对象。要试试这个。我们此时避免分页,因为我们发现如果不将整个 List 保存在内存中,很难实现以下操作:插入新笔记是正确的自定义排序顺序,然后快速滚动 recyclerview 到新插入的笔记位置。我们希望这些 UI 发生,甚至在 SQLite 完成编写之前 - 以获得快速和流畅的用户体验。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-22
  • 2020-09-04
  • 2013-08-06
  • 2022-10-17
相关资源
最近更新 更多