[英]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.