ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android Room – 在每次应用运行时重置自动生成的密钥

2019-09-10 16:27:24  阅读:265  来源: 互联网

标签:android-room android android-sqlite auto-increment


我正在使用Room来保存数据.
我有一个实体,它有一个自动生成的主键,模仿票证系统.

实体:

@Entity
public class SequenceAction {

    @PrimaryKey(autoGenerate = true)
    private Integer sequenceId;
    private String actionType;
    private String extraInfo;
    //getters&setters
}

初始化:

sequenceAction = new SequenceAction();
sequenceAction.setActionType(COLLECT_ALL);
sequenceAction.setExtraInfo("id = " + ids.get(i));
//run this line with executer(sequenceId is automatically set on insert to table):
AppDatabase.getInstance(getContext()).sequenceActionDao().save(sequenceAction);

在每个应用程序运行时,我需要此键从0开始.

我使用AppDatabase.getInstance(getApplicationContext()).clearAllTables();为了在退出时清除表,但是,这不会重置密钥起始索引,而是从最后一次运行时停止的位置开始.

我还没有找到使用Room这样做的方法所以我试图使用SimpleSQLiteQuery来做到这一点:

新的SimpleSQLiteQuery(“…查询…”);

在我的Dao中传递给RawQuery方法:

@RawQuery()
 整数init(SimpleSQLiteQuery查询);

我已经尝试了下一个查询:

>“ALTER TABLE SequenceAction AUTO_INCREMENT = 0”

我收到一个错误(我尝试使用’AUTOINCREMENT’,同样的错误):

android.database.sqlite.SQLiteException: near “AUTO_INCREMENT”: syntax error (code 1): , while compiling: ALTER TABLE SequenceAction AUTO_INCREMENT = 0

可能是因为,正如this question/answer所述,SQLite中没有自动增量关键字,而是一个声明为INTEGER PRIMARY KEY的列将自动自动增量.

>“从sqlite_sequence中删除name =’SequenceAction’”

没有错误,但索引也没有重置.

>按照建议here

“UPDATE SQLITE_SEQUENCE SET seq = -1 WHERE name =’SequenceAction’”

没有错误但没有效果.

>“TRUNCATE TABLE’SequenceAction’;”

错误(可能是因为SQLite doesn’t support the TRUNCATE command):

android.database.sqlite.SQLiteException: near “TRUNCATE”: syntax error (code 1): , while compiling: TRUNCATE TABLE ‘SequenceAction’;

>所以……最后一次尝试:DELETE FROM SequenceAction

没有错误,没有效果.

解决方法:

In order to clear the tables on exit but, this does not reset the key
starting index, instead it starts where it left off on the last run.

….

“delete from sqlite_sequence where name=’Sequence Action'” No error
but, the index is not reset either.

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

也就是说,当使用AUTOINCREMENT关键字时,则使用不同的算法.这与以下几行有关: –

找到最高值
  – a)sqlite_sequence数字中表的值存储和
  – b)最高的rowid值

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

你仍然会有一个唯一的id,它是rowid coulmn的别名,但不能保证它会一直增加. AUTOINCREMENT确保增加唯一ID,但不保证单调增加的唯一rowid.

On every application run I need this key to start from 0.

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

以下工作正常,正如您所看到的AUTOINCREMENT(虽然有点像黑客): –

DROP TABLE IF EXISTS SequenceAction;
DROP TRIGGER IF EXISTS use_zero_as_first_sequence;
CREATE TABLE IF NOT EXISTS SequenceAction (id INTEGER PRIMARY KEY AUTOINCREMENT, otherdata TEXT);
CREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence AFTER INSERT ON SequenceAction
    BEGIN 
        UPDATE SequenceAction SET id = id - 1 WHERE id = new.id;
    END
;
INSERT INTO SequenceAction VALUES(null,'TEST1'),(null,'TEST2'),(null,'TEST3');
SELECT * FROM SequenceAction;
-- RESET and RESTART FROM 0
DELETE FROM SequenceAction;
DELETE FROM sqlite_sequence WHERE name = 'SequenceAction';
INSERT INTO SequenceAction VALUES(null,'TEST4'),(null,'TEST5'),(null,'TEST6');
SELECT * FROM SequenceAction

> 2 DROP语句仅用于测试删除和重新定义.

这导致: –

第一个查询返回: –

enter image description here

第二次回归: –

enter image description here

所以从本质上说你想要: –

DELETE FROM SequenceAction;
DELETE FROM sqlite_sequence WHERE name = 'SequenceAction';

如果您希望编号从0开始而不是1,也可以触发.

或者,如果您取消了AUTOINCREMENT,那么您可以使用略微更改的触发器: –

CREATE TRIGGER IF NOT EXISTS use_zero_as_first_sequence 
    AFTER INSERT ON SequenceAction 
    WHEN (SELECT count() FROM SequenceAction) = 1
    BEGIN 
        UPDATE SequenceAction SET id = 0;
    END
;

>这只是重新编号第一个插入的行(算法然后为后续插入添加1)

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

使用房间的例子: –

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

private void resetSequenceAction() {
    SQLiteDatabase dbx;
    String sqlite_sequence_table = "sqlite_sequence";
    long initial_sacount;
    long post_sacount;
    long initial_ssn =0;
    long post_ssn = 0;
    Cursor csr;

    /*
        Need to Create Database and table if it doesn't exist
     */
    File f = this.getDatabasePath(TestDatabase.DBNAME);
    if (!f.exists()) {
        File d = new File(this.getDatabasePath(TestDatabase.DBNAME).getParent());
        d.mkdirs();
        dbx = SQLiteDatabase.openOrCreateDatabase(f,null);
        String crtsql = "CREATE TABLE IF NOT EXISTS " + SequenceAction.tablename + "(" +
                SequenceAction.id_column + " INTEGER PRIMARY KEY AUTOINCREMENT," +
                SequenceAction.actionType_column + " TEXT," +
                SequenceAction.extraInfo_column + " TEXT" +
                ")";
        dbx.execSQL(crtsql);
        /*
           Might as well create the Trigger as well
         */
        String triggerSql = "CREATE TRIGGER IF NOT EXISTS user_zero_as_first_rowid AFTER INSERT ON " +
                SequenceAction.tablename +
                " BEGIN " +
                " UPDATE " + SequenceAction.tablename +
                " SET " +
                SequenceAction.id_column + " = " + SequenceAction.id_column + " - 1 " +
                " WHERE " + SequenceAction.id_column + " = new." + SequenceAction.id_column + ";" +
                " END ";
        dbx.execSQL(triggerSql);

    } else {
        dbx = SQLiteDatabase.openDatabase(this.getDatabasePath(TestDatabase.DBNAME).getPath(),null, Context.MODE_PRIVATE);
    }

    /*
        Add trigger to set id's to 1 less than they were set to
     */
    initial_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);
    /*
        Delete all the rows at startup
     */
    String deleteAllSequenceIdRowsSql = "DELETE FROM " + SequenceAction.tablename;
    dbx.execSQL(deleteAllSequenceIdRowsSql);
    post_sacount = DatabaseUtils.queryNumEntries(dbx,SequenceAction.tablename);
    /*
        delete the sequence row from the sqlite_sequence table
     */
    csr = dbx.query(sqlite_sequence_table,
            new String[]{"seq"},"name=?",
            new String[]{SequenceAction.tablename},
            null,null,null
    );
    if (csr.moveToFirst()) {
        initial_ssn = csr.getLong(csr.getColumnIndex("seq"));
    }
    String deleteSqlLiteSequenceRow = "DELETE FROM " +
            sqlite_sequence_table +
            " WHERE name = '" + SequenceAction.tablename + "'";
    dbx.execSQL(deleteSqlLiteSequenceRow);
    csr = dbx.query(
            sqlite_sequence_table,
            new String[]{"seq"},
            "name=?",
            new String[]{SequenceAction.tablename},
            null,null,null
    );
    if (csr.moveToFirst()) {
        post_ssn = csr.getLong(csr.getColumnIndex("seq"));
    }
    csr.close();
    Log.d("SEQACTSTATS",
            "Initial Rowcount=" + String.valueOf(initial_sacount) +
                    " Initial Seq#=" + String.valueOf(initial_ssn) +
                    " Post Delete Rowcount =" + String.valueOf(post_sacount) +
                    " Post Delete Seq#=" + String.valueOf(post_ssn)
    );
    dbx.close();
}

初始运行的结果(即没有DB存在): –

D/SEQACTSTATS: Initial Rowcount=0 Initial Seq#=0 Post Delete Rowcount =0 Post Delete Seq#=0

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

D/SEQACTSTATS: Initial Rowcount=40 Initial Seq#=40 Post Delete Rowcount =0 Post Delete Seq#=0

添加一个列出所有行的方法,如下: –

private void listAllRows() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            salist = mTestDB.SequenceActionDaoAccess().getAll();
            getSequenceActionList(salist);
        }
    }).start();
}

随着 :-

@Override
public void getSequenceActionList(List<SequenceAction> sequenceActionList) {
    for (SequenceAction sa: sequenceActionList) {
        Log.d("SA","ID=" + String.valueOf(sa.getSequenceId()) + " AT=" + sa.getActionType() + " EI=" + sa.getExtraInfo());
    }
}

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

06-17 02:56:47.867 5526-5554/rt_mjt.roomtest D/SA: ID=0 AT=X0 EI=Y0
    ID=1 AT=X0 EI=Y0
    ID=2 AT=X0 EI=Y0
    ID=3 AT=X0 EI=Y0
    ID=4 AT=X1 EI=Y1
    ID=5 AT=X1 EI=Y1
    ID=6 AT=X1 EI=Y1
    ID=7 AT=X1 EI=Y1
06-17 02:56:47.868 5526-5554/rt_mjt.roomtest D/SA: ID=8 AT=X2 EI=Y2
    ID=9 AT=X2 EI=Y2
    ID=10 AT=X2 EI=Y2
    ID=11 AT=X2 EI=Y2
    ID=12 AT=X3 EI=Y3
    ID=13 AT=X3 EI=Y3
    ID=14 AT=X3 EI=Y3
    ID=15 AT=X3 EI=Y3
    ID=16 AT=X4 EI=Y4
06-17 02:56:47.869 5526-5554/rt_mjt.roomtest D/SA: ID=17 AT=X4 EI=Y4
    ID=18 AT=X4 EI=Y4
    ID=19 AT=X4 EI=Y4
    ID=20 AT=X5 EI=Y5
    ID=21 AT=X5 EI=Y5
    ID=22 AT=X5 EI=Y5
    ID=23 AT=X5 EI=Y5
    ID=24 AT=X6 EI=Y6
    ID=25 AT=X6 EI=Y6
    ID=26 AT=X6 EI=Y6
    ID=27 AT=X6 EI=Y6
06-17 02:56:47.870 5526-5554/rt_mjt.roomtest D/SA: ID=28 AT=X7 EI=Y7
    ID=29 AT=X7 EI=Y7
    ID=30 AT=X7 EI=Y7
    ID=31 AT=X7 EI=Y7
    ID=32 AT=X8 EI=Y8
    ID=33 AT=X8 EI=Y8
    ID=34 AT=X8 EI=Y8
    ID=35 AT=X8 EI=Y8
    ID=36 AT=X9 EI=Y9
    ID=37 AT=X9 EI=Y9
    ID=38 AT=X9 EI=Y9
    ID=39 AT=X9 EI=Y9

>注意结果可能很奇怪,因为多个线程在没有控制/排序的情况下运行.

使用的addSomeData方法是: –

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

补充评论: –

“I believe you have to get in before Room…” – do you mean execute
the SQL that clears the running index before instantiating the Room
database? – ghosh

not necessarily but before Room
opens the database which is before you try to do anything with it.
Have added invoking code (in Overidden activities onStart() method )
with some Room Db access to addSomeData is called immediately after. –
MikeT

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

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

标签:android-room,android,android-sqlite,auto-increment
来源: https://codeday.me/bug/20190910/1799698.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有