【问题标题】:Clear table and reset autoincrement primary key清表并重置自增主键
【发布时间】:2019-10-03 05:16:25
【问题描述】:

我想从我的表中删除所有内容并重置自动增量主键。我这样做:

@Query("delete from sqlite_sequence where name='bin';")
void delete();


@Query("DELETE FROM bin")
void nukeTable();

@Query("UPDATE SQLITE_SEQUENCE SET seq = 1 WHERE name = 'bin';")
void resetPrimaryKey();

但它不起作用

【问题讨论】:

  • @majidghafouri 它不起作用
  • 你不能"reset autoincrement primary key" - 顺便说一句,你为什么要这样做?
  • @pskink 为什么?当我的表是自动增量的最大值时发生了什么?
  • 您的意思是在 2^64 次插入之后?例如,如果您的应用每秒插入 100 万次,那么它会在 584942 年后发生

标签: java android android-room


【解决方案1】:

您的问题是 ROOM 可以防止使用 SQLite 表,即那些以 sqlite_ 开头的表,因此您不能直接在 ROOM 中重置 sqlite_sequence。

以下是在 ROOM 中规避此问题并实现重置序列目标的两种方法。第三个选项(未显示)是关闭 ROOM 数据库,然后使用 SQLiteDatabase 实例访问数据库以重置序列。

选项 A

您可以通过 DROP 对表执行此操作,然后使用 SupportSQLiteDatabase 实例重新创建它。

以下是选项 A 的演示(但还包括对 Room 数据库的不必要关闭,以及在 Room 对象之外使用数据库,但是已通过从 sqlite_sequence 提取数据添加了该技术证明) .

注意

上面的代码只是该技术的演示,上面的代码在房间和非房间之间交换存在问题,这不是解决方案所必需的。

核心测试代码:-

    //Stage 1 load some data
    getRoomDB();
    mRowIdTestDao = mRTDB.rowIdTestDao();
    mRowIdTestDao.insertManyRowIdTests(
            new RowIdTest("A"),
            new RowIdTest("B"),
            new RowIdTest("C")
    );

    // Stage 2 close to room and dump sqlite_sequence
    //mRTDB.close();
    getNonRoomDB();
    DatabaseUtils.dumpCursor(mNotRoomdb.query("sqlite_sequence",null,null,null,null,null,null));
    mNotRoomdb.close();

    //Stage 3 clear sqlite_sequence by dropping the table using SupportSQLiteDatabase
    getRoomDB();
    mSuppDB.execSQL("DROP TABLE RowIdTest"); //<<<<<<<<<< DROP THE TABLE
    mSuppDB.execSQL("CREATE TABLE IF NOT EXISTS RowIdTest (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT)"); //<<<<<<<<<< CREATE THE TABLE

    // Stage 4 close to room and dump sqlite_sequence
    mRTDB.close();
    getNonRoomDB();
    DatabaseUtils.dumpCursor(mNotRoomdb.query("sqlite_sequence",null,null,null,null,null,null));
    mNotRoomdb.close();

    // Stage 5 check that all is OK again (add some rows)
    getRoomDB();
    mRowIdTestDao.insertManyRowIdTests(
            new RowIdTest("A"),
            new RowIdTest("B"),
            new RowIdTest("C")
    );

    DatabaseUtils.dumpCursor(mSuppDB.query("SELECT * FROM RowIdTest"));

被调用的方法:-

RoomDatabase.Callback getSuppDb = new RoomDatabase.Callback() {

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

private void getRoomDB() {
    mRTDB = Room.databaseBuilder(this,RoomTestingDatabase.class,"rtdb.db")
            .allowMainThreadQueries()
            .addCallback(getSuppDb)
            .build();
}

private void getNonRoomDB() {
    mNotRoomdb = SQLiteDatabase.openDatabase(
            this.getDatabasePath("rtdb.db").getPath(),
            null,
            SQLiteDatabase.OPEN_READWRITE
    );
}

日志结果

2019-10-03 17:13:25.506 W/SQLiteConnection: Could not change the database journal mode of '/data/user/0/art.roomtesting/databases/rtdb.db' from 'wal' to 'TRUNCATE' because the database is locked.  This usually means that there are other open connections to the database which prevents the database from enabling or disabling write-ahead logging mode.  Proceeding without changing the journal mode.
2019-10-03 17:13:25.508 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@add2875
2019-10-03 17:13:25.508 I/System.out: 0 {
2019-10-03 17:13:25.508 I/System.out:    name=RowIdTest
2019-10-03 17:13:25.508 I/System.out:    seq=6
2019-10-03 17:13:25.508 I/System.out: }
2019-10-03 17:13:25.509 I/System.out: <<<<<
2019-10-03 17:13:25.511 E/SQLiteLog: (5) statement aborts at 1: [PRAGMA journal_mode=TRUNCATE] database is locked
2019-10-03 17:13:25.511 W/SQLiteConnection: Could not change the database journal mode of '/data/user/0/art.roomtesting/databases/rtdb.db' from 'wal' to 'TRUNCATE' because the database is locked.  This usually means that there are other open connections to the database which prevents the database from enabling or disabling write-ahead logging mode.  Proceeding without changing the journal mode.
2019-10-03 17:13:25.512 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@38ae98
2019-10-03 17:13:25.513 I/System.out: <<<<<
2019-10-03 17:13:25.518 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@f873957
2019-10-03 17:13:25.518 I/System.out: 0 {
2019-10-03 17:13:25.518 I/System.out:    id=1
2019-10-03 17:13:25.518 I/System.out:    name=A
2019-10-03 17:13:25.518 I/System.out: }
2019-10-03 17:13:25.518 I/System.out: 1 {
2019-10-03 17:13:25.518 I/System.out:    id=2
2019-10-03 17:13:25.518 I/System.out:    name=B
2019-10-03 17:13:25.518 I/System.out: }
2019-10-03 17:13:25.518 I/System.out: 2 {
2019-10-03 17:13:25.518 I/System.out:    id=3
2019-10-03 17:13:25.519 I/System.out:    name=C
2019-10-03 17:13:25.519 I/System.out: }
2019-10-03 17:13:25.519 I/System.out: <<<<<

选项 B

另一种选择是使用 AUTOINCREMENT 创建表,因此 rowid 仅基于表中的行生成。我相信关于不使用 AUTOINCREMENT 存在一些混淆(或一种)方法是 NOT code autoGenerate = true 并使用 Long 而不是 long(或 Integer not int 类型,不建议)。

考虑上面使用的 RowTestId 表/实体,它是:-

@Entity
public class RowIdTest {

    @PrimaryKey(autoGenerate = true)
    private long id;
    private String name;


    public RowIdTest() {};

    @Ignore
    public RowIdTest(String name){
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

等效的非 AUTOINCREMENT 版本是:-

@Entity
public class AltRowIdTest {

    @PrimaryKey
    private Long id; //<<<<<<<<<< Long not long
    private String name;


    public AltRowIdTest() {};

    @Ignore
    public AltRowIdTest(String name){
        this.name = name;
    }

    public Long getId() { //<<<<<<<<<< Long not long
        return id;
    }

    public void setId(Long id) { //<<<<<<<<<< Long not long
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

还要考虑:-

    @Query("DELETE FROM AltRowIdTest")
    int clearAllAltRowIdTestRows();

然后是以下代码:-

    //Option B
    mAltRowIdTestDao.insertManyRowIdTests(new AltRowIdTest("X"),new AltRowIdTest("Y"),new AltRowIdTest("Z"));
    DatabaseUtils.dumpCursor(mSuppDB.query("SELECT * FROM AltRowIdTest"));
    mAltRowIdTestDao.clearAllAltRowIdTestRows();
    mAltRowIdTestDao.insertManyRowIdTests(new AltRowIdTest("X"),new AltRowIdTest("Y"),new AltRowIdTest("Z"));
    DatabaseUtils.dumpCursor(mSuppDB.query("SELECT * FROM AltRowIdTest"));

日志中的结果是:-

2019-10-03 17:49:31.783 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@561c44
2019-10-03 17:49:31.785 I/System.out: 0 {
2019-10-03 17:49:31.785 I/System.out:    id=1
2019-10-03 17:49:31.785 I/System.out:    name=A
2019-10-03 17:49:31.785 I/System.out: }
2019-10-03 17:49:31.785 I/System.out: 1 {
2019-10-03 17:49:31.785 I/System.out:    id=2
2019-10-03 17:49:31.785 I/System.out:    name=B
2019-10-03 17:49:31.785 I/System.out: }
2019-10-03 17:49:31.785 I/System.out: 2 {
2019-10-03 17:49:31.785 I/System.out:    id=3
2019-10-03 17:49:31.785 I/System.out:    name=C
2019-10-03 17:49:31.786 I/System.out: }
2019-10-03 17:49:31.786 I/System.out: <<<<<


2019-10-03 17:49:31.793 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@f369662
2019-10-03 17:49:31.794 I/System.out: 0 {
2019-10-03 17:49:31.794 I/System.out:    id=1
2019-10-03 17:49:31.794 I/System.out:    name=X
2019-10-03 17:49:31.794 I/System.out: }
2019-10-03 17:49:31.794 I/System.out: 1 {
2019-10-03 17:49:31.794 I/System.out:    id=2
2019-10-03 17:49:31.794 I/System.out:    name=Y
2019-10-03 17:49:31.794 I/System.out: }
2019-10-03 17:49:31.794 I/System.out: 2 {
2019-10-03 17:49:31.794 I/System.out:    id=3
2019-10-03 17:49:31.794 I/System.out:    name=Z
2019-10-03 17:49:31.794 I/System.out: }
2019-10-03 17:49:31.795 I/System.out: <<<<<

即id 列在删除所有行后从 1 重新开始,或者与选项 A 不同,如果删除了末尾的行,它将重新使用释放的 id(如果未删除最后一行,则不会使用机器人)。此外,与 AUTOINCREMENT 不同,如果达到最高 id 值 (9223372036854775807)(原因 id 应始终为 long 或 Long,而不是 int 或 Integer)可能不会导致 SQLITE_FULL 异常,因为 SQLite 将尝试使用随机未使用的数字(您也可以通过使用负值将可用范围几乎翻倍)

【讨论】:

    【解决方案2】:

    好吧,你可以用两个查询来做到这一点:

    delete from your_table;    
    

    UPDATE SQLIT_SEQUENCE SET seq = 0 WHERE name='bin'
    

    这样就可以了。 ID 将重新启动。

    【讨论】:

    • 当我这样做时,我只清除我的表,但不重置主键
    • 尝试编辑答案,设置 0 并再次检查@kpokrywja
    • 我必须在哪里放置这个命令。在 DAO 界面中,无法识别命令 (UPDATE SQLIT_SEQUENCE SET seq = 0 WHERE name='bin')。
    猜你喜欢
    • 2023-03-04
    • 2014-06-24
    • 2010-10-18
    相关资源
    最近更新 更多