[英]Seperately log Room sqlite database operations in text file
我想在一个text file
记录所有数据库insert
、 delete
、 update
操作。
设想:
我使用房间本地数据库和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;";
}
对于上述触发 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);
}
};
}
将上述所有内容放入一个工作示例中的是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.