簡體   English   中英

在文本文件中單獨記錄 Room sqlite 數據庫操作

[英]Seperately log Room sqlite database operations in text file

我想在一個text file記錄所有數據庫insertdeleteupdate操作。

設想:

我使用房間本地數據庫和api,當用戶離線時,我想將操作記錄保存在text file因為同步后這些記錄將被刪除。 並且只是為了確保出現問題並且同步失敗,我仍然可以訪問text file . 或者有沒有其他推薦的方法?

如果您想要某種形式的文本表示,什么可能會驅動恢復,而不是復制數據庫,那么您可以使用TRIGGERS將數據記錄(記錄)到用於記錄的表中。

但是,目前 Room 不支持從注釋構建創建觸發器。 所以它們需要通過運行合適的代碼來構建和添加。 運行這樣的代碼可能是通過 CallBack 和 onCreate 回調。

這是一個簡單的(就像在單個表中一樣)示例,該示例將插入記錄到主表(表 1)中,並將它們記錄到 LogTable 中:-

首先是支持性的LogTable ,其中包括一些支持性的常量和方法:-

@Entity(tableName = LogTable.TABLE_NAME)
public class LogTable {
    public static final int ACTION_AFTER_INSERT = 11;
    public static final int ACTION_BEFORE_UPDATE = 1;
    public static final int ACTION_AFTER_UPDATE = 2;
    public static final int ACTION_BEFORE_DELETE = 3;
    public static final String SEPARATOR = "~";

    public static final String TABLE_NAME = "log";
    public static final String ID_COL = TABLE_NAME + BaseColumns._ID;
    public static final String TIMESTAMP_COL = "_timestamp";
    public static final String TABLENAME_COL = "_tablename";
    public static final String ACTION_COL = "_action";
    public static final String DATA_COL = "_data";

    @PrimaryKey
    @ColumnInfo(name = ID_COL)
    Long id;
    @ColumnInfo(name = TIMESTAMP_COL,defaultValue = "(strftime('%s','now'))")
    Long timestamp;
    @ColumnInfo(name = TABLENAME_COL)
    String tableName;
    @ColumnInfo(name = ACTION_COL)
    int action;
    @ColumnInfo(name = DATA_COL)
    String data;

    public static String actionAsString(int action) {
        switch (action) {
            case ACTION_AFTER_INSERT:
                return "INSERT";
            case ACTION_AFTER_UPDATE:
                return "AFTER UPDATE";
            case ACTION_BEFORE_UPDATE:
                return "BEFORE UPDATE";
            case ACTION_BEFORE_DELETE:
                return "DELETE";
            default:
                return "UNKNOWN";
        }
    }
}
  • 日志表旨在迎合任何表,因此表名。 數據列將包含可能可用的值列表。

主表Table1包括它的 INSERT 觸發器 SQL(您需要類似的 DELETE 和 UPDATE(可能之前和之后)):-

public class Table1 {
    public static final String TABLE_NAME = "t1";
    public static final String ID_COLUMN = TABLE_NAME + BaseColumns._ID;
    public static final String NAME_COLUMN = TABLE_NAME + "_name";
    public static final String OTHER_COLUMN = TABLE_NAME + "_other";

    public static final String LOGCOLVALUES =
            "'" + TABLE_NAME + "',"
            + LogTable.ACTION_AFTER_INSERT + ","
            + "'" + ID_COLUMN+  "='|| new." + ID_COLUMN + "||'" + LogTable.SEPARATOR
            +  NAME_COLUMN + "='|| new." + NAME_COLUMN + "||'" + LogTable.SEPARATOR
            + OTHER_COLUMN + "='|| new." + OTHER_COLUMN
            ;


    @PrimaryKey
    @ColumnInfo(name = ID_COLUMN)
    Long id;
    @ColumnInfo(name = NAME_COLUMN)
    String name;
    @ColumnInfo(name = OTHER_COLUMN)
    String other;

    public static final String INSERT_TRIGGER_SQL =
            "CREATE TRIGGER IF NOT EXISTS " + TABLE_NAME + "_insert " +
                    "AFTER INSERT ON " + Table1.TABLE_NAME +
                    " BEGIN " +
                    " INSERT INTO " + LogTable.TABLE_NAME +
                    "(" +
                    LogTable.TABLENAME_COL + "," +
                    LogTable.ACTION_COL + "," +
                    LogTable.DATA_COL +
                    ")" +
                    " VALUES(" +
                    LOGCOLVALUES +
                    "); END;";

}
  • LOGCOLVALUES 常量生成相對復雜的 VALUES 子句。
  • 新的。 指正在更新的數據。 注意到對於一個 UPDATE 新的。 指舊時更新的值。 指的是更新前的數據。 對於僅插入新的。 是適用的,對於 DELETE 僅舊的。

對於上述觸發 SQL,當解決時,是:-

CREATE TRIGGER IF NOT EXISTS t1_insert AFTER INSERT ON t1 BEGIN  INSERT INTO log(_tablename,_action,_data) VALUES('t1',11,'t1_id='|| new.t1_id||'~t1_name='|| new.t1_name||'~t1_other='|| new.t1_other); END;

AllDao中用於示例的Dao很簡單:-

@Dao
abstract class AllDao {

    @Insert
    abstract long insert(Table1 table1);
    @Query("SELECT * FROM log")
    abstract List<LogTable> getAllLogs();
}

@Database 類TheDatabase ,其中在onCreate方法中構建的 TRIGGER 是:-

@Database(entities = {Table1.class,LogTable.class},version = 1)
abstract class TheDatabase extends RoomDatabase {

    abstract AllDao getAllDao();

    private static volatile TheDatabase instance = null;

    public static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context,TheDatabase.class,"my.db")
                    .addCallback(cb)
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }

    private static Callback cb = new Callback() {
        @Override
        public void onCreate(SupportSQLiteDatabase db) {
            super.onCreate(db);
            db.execSQL(Table1.INSERT_TRIGGER_SQL);
        }

        @Override
        public void onOpen(SupportSQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onDestructiveMigration(SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };
}
  • 顯然,如果有一個完整的實現,那么每個“主”表將有 3 個觸發器(更新、插入和刪除)。

將上述所有內容放入一個工作示例中的是MainActivity ,它插入 10 行,然后從日志中提取數據:-

public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    AllDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = TheDatabase.getInstance(this);
        dao = db.getAllDao();

        Table1 t1 = new Table1();
        for(int i=0;i < 10; i++) {
            t1.name = "Test"+i;
            t1.other = "TestOtherData"+i;
            dao.insert(t1);
        }
        for(LogTable l: dao.getAllLogs()) {
            Log.d(
                    "LOGTABLEINFO",
                    "ID = " + l.id +
                            " TS = " + l.timestamp +
                            " Table =" + l.tableName +
                            " Action = " + LogTable.actionAsString(l.action) +
                            " Values = " + l.data
            );
        }
    }
}

結果

運行(兩次)時,日志包含:-

2021-07-24 10:26:58.830 D/LOGTABLEINFO: ID = 1 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=1~t1_name=Test0~t1_other=TestOtherData0
2021-07-24 10:26:58.830 D/LOGTABLEINFO: ID = 2 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=2~t1_name=Test1~t1_other=TestOtherData1
2021-07-24 10:26:58.830 D/LOGTABLEINFO: ID = 3 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=3~t1_name=Test2~t1_other=TestOtherData2
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 4 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=4~t1_name=Test3~t1_other=TestOtherData3
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 5 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=5~t1_name=Test4~t1_other=TestOtherData4
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 6 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=6~t1_name=Test5~t1_other=TestOtherData5
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 7 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=7~t1_name=Test6~t1_other=TestOtherData6
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 8 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=8~t1_name=Test7~t1_other=TestOtherData7
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 9 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=9~t1_name=Test8~t1_other=TestOtherData8
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 10 TS = 1627084992 Table =t1 Action = INSERT Values = t1_id=10~t1_name=Test9~t1_other=TestOtherData9
2021-07-24 10:26:58.831 D/LOGTABLEINFO: ID = 11 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=11~t1_name=Test0~t1_other=TestOtherData0
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 12 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=12~t1_name=Test1~t1_other=TestOtherData1
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 13 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=13~t1_name=Test2~t1_other=TestOtherData2
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 14 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=14~t1_name=Test3~t1_other=TestOtherData3
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 15 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=15~t1_name=Test4~t1_other=TestOtherData4
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 16 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=16~t1_name=Test5~t1_other=TestOtherData5
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 17 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=17~t1_name=Test6~t1_other=TestOtherData6
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 18 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=18~t1_name=Test7~t1_other=TestOtherData7
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 19 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=19~t1_name=Test8~t1_other=TestOtherData8
2021-07-24 10:26:58.832 D/LOGTABLEINFO: ID = 20 TS = 1627086418 Table =t1 Action = INSERT Values = t1_id=20~t1_name=Test9~t1_other=TestOtherData9

補充

更進一步,這里有一個包含所有 4 個觸發器的示例,觸發器由LogTable Entity/Class 中的方法構建:-

表 1已被簡化,因為 LogTable 基於觸發器操作、表名和表的列構建觸發器。 就是現在:-

@Entity(tableName = TABLE_NAME)
public class Table1 {
    public static final String TABLE_NAME = "t1";
    public static final String ID_COLUMN = TABLE_NAME + BaseColumns._ID;
    public static final String NAME_COLUMN = TABLE_NAME + "_name";
    public static final String OTHER_COLUMN = TABLE_NAME + "_other";

    @PrimaryKey
    @ColumnInfo(name = ID_COLUMN)
    Long id;
    @ColumnInfo(name = NAME_COLUMN)
    String name;
    @ColumnInfo(name = OTHER_COLUMN)
    String other;

}

LogTable已更改為:-

@Entity(tableName = LogTable.TABLE_NAME)
public class LogTable {
    public static final int ACTION_AFTER_INSERT = 11;
    public static final int ACTION_BEFORE_UPDATE = 1;
    public static final int ACTION_AFTER_UPDATE = 2;
    public static final int ACTION_BEFORE_DELETE = 3;
    public static final String SEPARATOR = "~";

    public static final String TABLE_NAME = "log";
    public static final String ID_COL = TABLE_NAME + BaseColumns._ID;
    public static final String TIMESTAMP_COL = "_timestamp";
    public static final String TABLENAME_COL = "_tablename";
    public static final String ACTION_COL = "_action";
    public static final String DATA_COL = "_data";

    @PrimaryKey
    @ColumnInfo(name = ID_COL)
    Long id;
    @ColumnInfo(name = TIMESTAMP_COL,defaultValue = "(strftime('%s','now'))")
    Long timestamp;
    @ColumnInfo(name = TABLENAME_COL)
    String tableName;
    @ColumnInfo(name = ACTION_COL)
    int action;
    @ColumnInfo(name = DATA_COL)
    String data;

    public static String actionAsString(int action) {
        switch (action) {
            case ACTION_AFTER_INSERT:
                return "AFTER INSERT";
            case ACTION_AFTER_UPDATE:
                return "AFTER UPDATE";
            case ACTION_BEFORE_UPDATE:
                return "BEFORE UPDATE";
            case ACTION_BEFORE_DELETE:
                return "AFTER DELETE";
            default:
                return "UNKNOWN";
        }
    }
    public static String buildTriggerSQL(int action, String tableName, String[] columns) {
        return buildTriggerSQL(action,tableName,columns,false);
    }

    public static String buildTriggerSQL(int action, String tableName, String[] columns, boolean logResult) {

        // Prepare the appropriate old or new for extracting data
        String retrieve;
        switch (action) {
            case ACTION_AFTER_INSERT: case ACTION_AFTER_UPDATE:
                retrieve = "new.";
                break;
            default:
                retrieve = "old.";
        }
        StringBuilder sb = new StringBuilder().append("CREATE TRIGGER IF NOT EXISTS ");
        // TriggerName
        sb
                // Trigger Name
                .append("_")
                .append(tableName)
                .append("_")
                .append(actionAsString(action).replace(' ','_'))
                // Trigger Action ON clause
                .append(" ").append(actionAsString(action)).append(" ON  ").append(tableName)
                .append(" BEGIN INSERT INTO ").append(TABLE_NAME).append("(")
                // The Log Table columns to be set (ID and timestamp generated by defaults)
                .append(TABLENAME_COL).append(",").append(ACTION_COL).append(",").append(DATA_COL).append(") ")
                // The fixed VALUES to be inserted
                .append(" VALUES(")
                .append("'").append(tableName).append("',")
                .append(action).append(",")
        ;
        // Build the values to be extracted from the changed table for the log table's data column
        boolean afterFirst = false;
        for (String s: columns) {
            // if not first line add separator between the previous and next
            if (afterFirst) {
                sb.append("||'").append(SEPARATOR).append("'||");
            }
            sb.append("'").append(s).append("='|| ")
                    .append(retrieve).append(s);
            afterFirst = true;
        }
        if (logResult) Log.d("LOGTBL_BLDTRGSQL","Trigger SQL = \n\t" + sb.append("); END;").toString());
        return sb.append("); END;").toString();
    }
}
  • 觸發器被命名
  • _<the_tablename>_<the_action_with_spaces_replaced_by_underscores>

AllDao已被修改為包括更新和刪除以及getById :-

@Dao
abstract class AllDao {

    @Insert
    abstract long insert(Table1 table1);
    @Query("SELECT * FROM log")
    abstract List<LogTable> getAllLogs();
    @Update
    abstract int update(Table1 table1);
    @Delete
    abstract int delete(Table1 table1);
    @Query("SELECT * FROM t1 WHERE t1_id =:id")
    abstract Table1 getTable1ById(long id);
}

TheDatabase現在構建了所有 4 個觸發器,並且是:-

@Database(entities = {Table1.class,LogTable.class},version = 1)
abstract class TheDatabase extends RoomDatabase {

    abstract AllDao getAllDao();

    private static volatile TheDatabase instance = null;

    public static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context,TheDatabase.class,"my.db")
                    .addCallback(cb)
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }

    private static Callback cb = new Callback() {
        @Override
        public void onCreate(SupportSQLiteDatabase db) {
            super.onCreate(db);
            String[] table1Columns = new String[]{Table1.ID_COLUMN,Table1.NAME_COLUMN,Table1.OTHER_COLUMN};
            db.execSQL(LogTable.buildTriggerSQL(LogTable.ACTION_AFTER_INSERT,Table1.TABLE_NAME,table1Columns));
            db.execSQL(LogTable.buildTriggerSQL(LogTable.ACTION_BEFORE_UPDATE,Table1.TABLE_NAME,table1Columns));
            db.execSQL(LogTable.buildTriggerSQL(LogTable.ACTION_AFTER_UPDATE,Table1.TABLE_NAME,table1Columns));
            db.execSQL(LogTable.buildTriggerSQL(LogTable.ACTION_BEFORE_DELETE,Table1.TABLE_NAME,table1Columns));
        }

        @Override
        public void onOpen(SupportSQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onDestructiveMigration(SupportSQLiteDatabase db) {
            super.onDestructiveMigration(db);
        }
    };
}

最后MainActivity包括更新和刪除:-

public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    AllDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = TheDatabase.getInstance(this);
        dao = db.getAllDao();

        Table1 t1 = new Table1();
        for(int i=0;i < 10; i++) {
            t1.name = "Test"+i;
            t1.other = "TestOtherData"+i;
            dao.insert(t1);
        }
        t1.id = 1L;
        t1.other = "Updated Other Data";
        dao.update(t1);
        dao.delete(dao.getTable1ById(5L));
        for(LogTable l: dao.getAllLogs()) {
            Log.d(
                    "LOGTABLEINFO",
                    "ID = " + l.id +
                            " TS = " + l.timestamp +
                            " Table =" + l.tableName +
                            " Action = " + LogTable.actionAsString(l.action) +
                            " Values = " + l.data
            );
        }
    }
}

結果:-

2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 1 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=1~t1_name=Test0~t1_other=TestOtherData0
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 2 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=2~t1_name=Test1~t1_other=TestOtherData1
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 3 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=3~t1_name=Test2~t1_other=TestOtherData2
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 4 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=4~t1_name=Test3~t1_other=TestOtherData3
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 5 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=5~t1_name=Test4~t1_other=TestOtherData4
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 6 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=6~t1_name=Test5~t1_other=TestOtherData5
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 7 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=7~t1_name=Test6~t1_other=TestOtherData6
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 8 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=8~t1_name=Test7~t1_other=TestOtherData7
2021-07-25 13:53:33.800 D/LOGTABLEINFO: ID = 9 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=9~t1_name=Test8~t1_other=TestOtherData8
2021-07-25 13:53:33.801 D/LOGTABLEINFO: ID = 10 TS = 1627185213 Table =t1 Action = AFTER INSERT Values = t1_id=10~t1_name=Test9~t1_other=TestOtherData9
2021-07-25 13:53:33.801 D/LOGTABLEINFO: ID = 11 TS = 1627185213 Table =t1 Action = BEFORE UPDATE Values = t1_id=1~t1_name=Test0~t1_other=TestOtherData0
2021-07-25 13:53:33.801 D/LOGTABLEINFO: ID = 12 TS = 1627185213 Table =t1 Action = AFTER UPDATE Values = t1_id=1~t1_name=Test9~t1_other=Updated Other Data
2021-07-25 13:53:33.801 D/LOGTABLEINFO: ID = 13 TS = 1627185213 Table =t1 Action = AFTER DELETE Values = t1_id=5~t1_name=Test4~t1_other=TestOtherData4

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM