Android Room - 如何在每次运行应用程序时重置自动生成的表主键

gho*_*osh 5 android auto-increment android-sqlite android-room

我正在使用 Room 来保存数据。
我有一个实体,它有一个模拟票证系统的自动生成(自动生成)主键。 在每个应用程序运行时,我需要这个键从 0 开始

实体:

@Entity
public class SequenceAction {

    @PrimaryKey(autoGenerate = true)
    private Integer sequenceId;
    private String actionType;
    private String extraInfo;
    //getters & setters
}
Run Code Online (Sandbox Code Playgroud)

初始化:

// init sequenceAction object
// run with executor(sequenceId is automatically set on insert to table):
AppDatabase.getInstance(getContext()).sequenceActionDao().save(sequenceAction);
Run Code Online (Sandbox Code Playgroud)

我尝试过的事情:

AppDatabase.getInstance(getApplicationContext()).clearAllTables(); 过去常常在退出时清除表,但这不会重置键起始索引,而是从上次运行时停止的位置开始。

我还没有找到使用 Room 执行此操作的方法,因此我正在尝试将 SimpleSQLiteQuery 传递给 Dao 中的 RawQuery 方法:

//Dao
@RawQuery()
Integer init(SimpleSQLiteQuery query);

//Passed query
new SimpleSQLiteQuery("...query...");
Run Code Online (Sandbox Code Playgroud)

我试过下一个查询:

  1. "ALTER TABLE SequenceAction AUTO_INCREMENT = 0"

我收到一个错误(我用“AUTOINCREMENT”试过这个,同样的错误):

android.database.sqlite.SQLiteException:在“AUTO_INCREMENT”附近:语法错误(代码 1):,编译时:ALTER TABLE SequenceAction AUTO_INCREMENT = 0

可能是因为,正如此问题/答案所述,SQLite 中没有自动增量关键字,而是声明为 INTEGER PRIMARY KEY 的列将自动自动增量。

  1. "delete from sqlite_sequence where name='SequenceAction'"

没有错误,但索引也不会重置。

  1. 正如这里所建议的:

    "UPDATE SQLITE_SEQUENCE SET seq = -1 WHERE name = 'SequenceAction'"

没有错误,但没有效果。

  1. "TRUNCATE TABLE 'SequenceAction';"

错误(可能是因为SQLite 不支持 TRUNCATE 命令):

android.database.sqlite.SQLiteException:在“TRUNCATE”附近:语法错误(代码1):,编译时:TRUNCATE TABLE 'SequenceAction';

  1. 所以......最后一次尝试: DELETE FROM SequenceAction

没有错误,没有影响。

Mik*_*keT 2

\n

但是,为了在退出时清除表,这不会重置键\n起始索引,而是从上次运行时停止的位置开始。

\n
\n\n

....

\n\n
\n

"delete from sqlite_sequence where name=\'Sequence Action\'" 没有错误\n但是,索引也没有重置。

\n
\n\n

您必须删除SequenceAction表中的所有行并从 sqlite_sequence 中删除相应的行。

\n\n

也就是说,当使用 AUTOINCRMENT 关键字时,会使用不同的算法。这大致如下:-

\n\n

查找 \n - a) sqlite_sequence 编号中表的值存储和\n - b) 最高 rowid 值中的最高值

\n\n

另一种方法是不使用AUTOINCREMENT关键字,而只使用关键字?? INTEGER PRIMARY KEY(其中 ?? 代表列名称)。

\n\n

您仍然会有一个唯一的 id,它是rowidcoulmn 的别名,但不能保证它总是会增加。AUTOINCREMENT确实保证唯一 id 递增,但不保证单调递增唯一 rowid。

\n\n
\n

在每次运行应用程序时,我都需要这个键从 0 开始。

\n
\n\n

然而,SQLite 会将第一个值设置为 1 而不是 0。

\n\n

下面的代码确实有效,正如您在 AUTOINCRMENT 中看到的那样(尽管有点破解):-

\n\n
DROP TABLE IF EXISTS SequenceAction;\nDROP TRIGGER IF EXISTS use_zero_as_first_sequence;\nCREATE TABLE IF NOT EXISTS SequenceAction (id INTEGER PRIMARY KEY AUTOINCREMENT, otherdata TEXT);\nCREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence AFTER INSERT ON SequenceAction\n    BEGIN \n        UPDATE SequenceAction SET id = id - 1 WHERE id = new.id;\n    END\n;\nINSERT INTO SequenceAction VALUES(null,\'TEST1\'),(null,\'TEST2\'),(null,\'TEST3\');\nSELECT * FROM SequenceAction;\n-- RESET and RESTART FROM 0\nDELETE FROM SequenceAction;\nDELETE FROM sqlite_sequence WHERE name = \'SequenceAction\';\nINSERT INTO SequenceAction VALUES(null,\'TEST4\'),(null,\'TEST5\'),(null,\'TEST6\');\nSELECT * FROM SequenceAction\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 2 个 DROP 语句仅用于测试删除和重新定义。
  • \n
\n\n

这导致:-

\n\n

第一个查询返回:-

\n\n

在此输入图像描述

\n\n

第二次返回:-

\n\n

在此输入图像描述

\n\n

所以本质上你想要:-

\n\n
DELETE FROM SequenceAction;\nDELETE FROM sqlite_sequence WHERE name = \'SequenceAction\';\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您希望编号从 0 而不是 1 开始,还可以使用触发器。

\n\n

或者,如果您取消了自动增量,那么您可以使用稍微改变的触发器:-

\n\n
CREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence \n    AFTER INSERT ON SequenceAction \n    WHEN (SELECT count() FROM SequenceAction) = 1\n    BEGIN \n        UPDATE SequenceAction SET id = 0;\n    END\n;\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 这只是对第一个插入的行重新编号(然后该算法为后续插入添加 1)
  • \n
\n\n

然后只需从 SequenceAction 表中删除所有行即可重置编号。

\n\n
\n\n

使用房间的示例:-

\n\n

根据您的代码以及上面的示例,以下方法似乎有效:-

\n\n
private void resetSequenceAction() {\n    SQLiteDatabase dbx;\n    String sqlite_sequence_table = "sqlite_sequence";\n    long initial_sacount;\n    long post_sacount;\n    long initial_ssn =0;\n    long post_ssn = 0;\n    Cursor csr;\n\n    /*\n        Need to Create Database and table if it doesn\'t exist\n     */\n    File f = this.getDatabasePath(TestDatabase.DBNAME);\n    if (!f.exists()) {\n        File d = new File(this.getDatabasePath(TestDatabase.DBNAME).getParent());\n        d.mkdirs();\n        dbx = SQLiteDatabase.openOrCreateDatabase(f,null);\n        String crtsql = "CREATE TABLE IF NOT EXISTS " + SequenceAction.tablename + "(" +\n                SequenceAction.id_column + " INTEGER PRIMARY KEY AUTOINCREMENT," +\n                SequenceAction.actionType_column + " TEXT," +\n                SequenceAction.extraInfo_column + " TEXT" +\n                ")";\n        dbx.execSQL(crtsql);\n        /*\n           Might as well create the Trigger as well\n         */\n        String triggerSql = "CREATE TRIGGER IF NOT EXISTS user_zero_as_first_rowid AFTER INSERT ON " +\n                SequenceAction.tablename +\n                " BEGIN " +\n                " UPDATE " + SequenceAction.tablename +\n                " SET " +\n                SequenceAction.id_column + " = " + SequenceAction.id_column + " - 1 " +\n                " WHERE " + SequenceAction.id_column + " = new." + SequenceAction.id_column + ";" +\n                " END ";\n        dbx.execSQL(triggerSql);\n\n    } else {\n        dbx = SQLiteDatabase.openDatabase(this.getDatabasePath(TestDatabase.DBNAME).getPath(),null, Context.MODE_PRIVATE);\n    }\n\n    /*\n        Add trigger to set id\'s to 1 less than they were set to\n     */\n    initial_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);\n    /*\n        Delete all the rows at startup\n     */\n    String deleteAllSequenceIdRowsSql = "DELETE FROM " + SequenceAction.tablename;\n    dbx.execSQL(deleteAllSequenceIdRowsSql);\n    post_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);\n    /*\n        delete the sequence row from the sqlite_sequence table\n     */\n    csr = dbx.query(sqlite_sequence_table,\n            new String[]{"seq"},"name=?",\n            new String[]{SequenceAction.tablename},\n            null,null,null\n    );\n    if (csr.moveToFirst()) {\n        initial_ssn = csr.getLong(csr.getColumnIndex("seq"));\n    }\n    String deleteSqlLiteSequenceRow = "DELETE FROM " +\n            sqlite_sequence_table +\n            " WHERE name = \'" + SequenceAction.tablename + "\'";\n    dbx.execSQL(deleteSqlLiteSequenceRow);\n    csr = dbx.query(\n            sqlite_sequence_table,\n            new String[]{"seq"},\n            "name=?",\n            new String[]{SequenceAction.tablename},\n            null,null,null\n    );\n    if (csr.moveToFirst()) {\n        post_ssn = csr.getLong(csr.getColumnIndex("seq"));\n    }\n    csr.close();\n    Log.d("SEQACTSTATS",\n            "Initial Rowcount=" + String.valueOf(initial_sacount) +\n                    " Initial Seq#=" + String.valueOf(initial_ssn) +\n                    " Post Delete Rowcount =" + String.valueOf(post_sacount) +\n                    " Post Delete Seq#=" + String.valueOf(post_ssn)\n    );\n    dbx.close();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

初始运行的结果(即不存在数据库):-

\n\n
D/SEQACTSTATS: Initial Rowcount=0 Initial Seq#=0 Post Delete Rowcount =0 Post Delete Seq#=0\n
Run Code Online (Sandbox Code Playgroud)\n\n

从后续运行(添加 40 行后):-

\n\n
D/SEQACTSTATS: Initial Rowcount=40 Initial Seq#=40 Post Delete Rowcount =0 Post Delete Seq#=0\n
Run Code Online (Sandbox Code Playgroud)\n\n

添加一个方法来列出所有行,如下所示:-

\n\n
private void listAllRows() {\n    new Thread(new Runnable() {\n        @Override\n        public void run() {\n            salist = mTestDB.SequenceActionDaoAccess().getAll();\n            getSequenceActionList(salist);\n        }\n    }).start();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

随着 :-

\n\n
@Override\npublic void getSequenceActionList(List<SequenceAction> sequenceActionList) {\n    for (SequenceAction sa: sequenceActionList) {\n        Log.d("SA","ID=" + String.valueOf(sa.getSequenceId()) + " AT=" + sa.getActionType() + " EI=" + sa.getExtraInfo());\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

结果(第一行即ID=0 AT=X0 EI=Y0第一行的ID列为0):-

\n\n
06-17 02:56:47.867 5526-5554/rt_mjt.roomtest D/SA: ID=0 AT=X0 EI=Y0\n    ID=1 AT=X0 EI=Y0\n    ID=2 AT=X0 EI=Y0\n    ID=3 AT=X0 EI=Y0\n    ID=4 AT=X1 EI=Y1\n    ID=5 AT=X1 EI=Y1\n    ID=6 AT=X1 EI=Y1\n    ID=7 AT=X1 EI=Y1\n06-17 02:56:47.868 5526-5554/rt_mjt.roomtest D/SA: ID=8 AT=X2 EI=Y2\n    ID=9 AT=X2 EI=Y2\n    ID=10 AT=X2 EI=Y2\n    ID=11 AT=X2 EI=Y2\n    ID=12 AT=X3 EI=Y3\n    ID=13 AT=X3 EI=Y3\n    ID=14 AT=X3 EI=Y3\n    ID=15 AT=X3 EI=Y3\n    ID=16 AT=X4 EI=Y4\n06-17 02:56:47.869 5526-5554/rt_mjt.roomtest D/SA: ID=17 AT=X4 EI=Y4\n    ID=18 AT=X4 EI=Y4\n    ID=19 AT=X4 EI=Y4\n    ID=20 AT=X5 EI=Y5\n    ID=21 AT=X5 EI=Y5\n    ID=22 AT=X5 EI=Y5\n    ID=23 AT=X5 EI=Y5\n    ID=24 AT=X6 EI=Y6\n    ID=25 AT=X6 EI=Y6\n    ID=26 AT=X6 EI=Y6\n    ID=27 AT=X6 EI=Y6\n06-17 02:56:47.870 5526-5554/rt_mjt.roomtest D/SA: ID=28 AT=X7 EI=Y7\n    ID=29 AT=X7 EI=Y7\n    ID=30 AT=X7 EI=Y7\n    ID=31 AT=X7 EI=Y7\n    ID=32 AT=X8 EI=Y8\n    ID=33 AT=X8 EI=Y8\n    ID=34 AT=X8 EI=Y8\n    ID=35 AT=X8 EI=Y8\n    ID=36 AT=X9 EI=Y9\n    ID=37 AT=X9 EI=Y9\n    ID=38 AT=X9 EI=Y9\n    ID=39 AT=X9 EI=Y9\n
Run Code Online (Sandbox Code Playgroud)\n\n
    \n
  • 请注意,由于多个线程在没有控制/排序的情况下运行,结果可能会很奇怪。
  • \n
\n\n

使用的方法addSomeData是:-

\n\n
private void addSomeData() {\n    new Thread(new Runnable() {\n        @Override\n        public void run() {\n            SequenceAction sa = new SequenceAction();\n            for (int i=0; i < 10; i++) {\n                sa.setSequenceId(0);\n                sa.setActionType("X" + String.valueOf(i));\n                sa.setExtraInfo("Y" + String.valueOf(i));\n                mTestDB.SequenceActionDaoAccess().insertSingleRow(sa);\n            }\n        }\n    }) .start();\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

添加评论:-

\n\n
\n

“我相信你必须在 Room 之前进入...” - 你的意思是在实例化 Room 数据库之前执行\n 清除正在运行的索引的 SQL 吗?- 戈什

\n\n

不一定,但在 Room\n 打开数据库之前,也就是在您尝试对其执行任何操作之前。\n 添加了调用代码(在 Overidden 活动的 onStart() 方法中)\n 在使用某些 Room Db 访问 addSomeData 之后立即调用。\xe2\x80\x93\n 麦克T

\n
\n\n

下面是在 RoomDatabase 实例化之后、用于访问/打开数据库之前调用 resetSequenceAction 方法的示例(addSomeData 打开已实例化的数据库并插入 10 行):-

\n\n
@Override\nprotected void onStart() {\n    super.onStart();\n    mTestDB = Room.databaseBuilder(this,TestDatabase.class,TestDatabase.DBNAME).build(); //<<<< Room DB instantiated\n    resetSequenceAction(); //<<<< reset the sequence (adding trigger if needed)\n    addSomeData(); // This will be the first access open\n    addSomeData();\n    addSomeData();\n    addSomeData();\n    listAllRows();\n
Run Code Online (Sandbox Code Playgroud)\n