繁体   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