首页 > 解决方案 > 是否可以在选定的 Room Database Dao 列上启用/禁用转换?

问题描述

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

@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. 如果SQLite表LiveData中有任何更改,它能够通知观察者。note
  3. 我们只需要body. 我们不需要整个body. 为所有音符加载整个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 数据库 Dao:在返回 List of Notes as 之前LiveData,请对每个 Note 的 body 列进行转换,将字符串修剪为最多 256 个字符?


查看 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 库,因为我们的某些功能需要内存中的整个笔记列表(带有修剪的正文字符串)。


标签: androidandroid-room

解决方案


您可以使用 SUBSTR,这是SQLite 的内置函数之一。

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

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

这会将body修剪后的字符返回到 256 个字符。

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


推荐阅读