我正在使用Room来保存数据。 我有一个实体,它有一个自动生成的(autoGenerate)主键,模仿票务系统。
实体:
@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来做到这一点:
new SimpleSQLiteQuery("...query...");
在我的Dao中传递给RawQuery方法:
@RawQuery()
Integer init(SimpleSQLiteQuery query);
我已经尝试了下一个查询:
"ALTER TABLE SequenceAction AUTO_INCREMENT = 0"
我收到错误(我尝试使用' AUTOINCREMENT',同样的错误):
android.database.sqlite.SQLiteException:near" AUTO_INCREMENT&#34 ;:语法错误(代码1):,编译时:ALTER TABLE SequenceAction AUTO_INCREMENT = 0
可能因为,this question/answer状态,SQLite中没有自动增量关键字,而是声明为INTEGER PRIMARY KEY的列将自动自动增量。
"delete from sqlite_sequence where 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&#34 ;:语法错误(代码1):,编译时:TRUNCATE TABLE' SequenceAction';
DELETE FROM SequenceAction
没有错误,没有效果。
答案 0 :(得分:1)
为了在退出时清除表格,但这不会重置密钥 起始索引,而不是从最后一次运行中停止的位置开始。
...
"从sqlite_sequence中删除,其中name =' Sequence Action'"没错 但是,索引也没有重置。
您必须同时删除 SequenceAction 表中的所有行,并从sqlite_sequence中删除相应的行。
也就是说,当使用AUTOINCREMENT关键字时,则使用不同的算法。这与以下几行有关: -
找到最高值 - a)sqlite_sequence数字中表的值存储和 - b)最高的rowid值
另一种方法是不使用AUTOINCREMENT
关键字,而只使用?? INTEGER PRIMARY KEY
(其中??代表列名)。
你仍然会有一个唯一的id,它是rowid
coulmn的别名,但不能保证它会一直增加。 AUTOINCREMENT
确保增加唯一ID,但不保证单调增加的唯一rowid。
在每次运行应用程序时,我都需要此键从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
这导致: -
第一个返回的查询: -
和第二次回归: -
所以从本质上说你想要: -
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
;
然后只需从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();
}
&#34;我相信你必须在房间之前进入......&#34; - 你的意思是执行 在实例化Room之前清除正在运行的索引的SQL 数据库? - ghosh
不一定是在Room之前 在您尝试对其执行任何操作之前打开数据库。 添加了调用代码(在Overidden activities onStart()方法中) 有一些房间Db访问addSomeData后立即调用。 - 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();
答案 1 :(得分:1)
尝试了许多方法之后。终于这对我有用了!
public static void truncateTable(Context context, SupportSQLiteOpenHelper openHelper, String tableName) {
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(
context.getDatabasePath(openHelper.getDatabaseName()),
null
);
if (database != null) {
database.execSQL(String.format("DELETE FROM %s;", tableName));
database.execSQL("UPDATE sqlite_sequence SET seq = 0 WHERE name = ?;", new String[]{tableName});
}
}
实施:
truncateTable(getContext(), yourRoomDatabase.getOpenHelper(), "your_table_name");
答案 2 :(得分:0)
您可以创建in memory database而不是在磁盘上创建它。然后你每次都会以一块干净的石板开始。您可能希望根据BuildConfig.DEBUG
的值创建基于内存或磁盘的数据库。
答案 3 :(得分:0)
将MikeT的内容作为模型。
我认为这可行:
fun clearAndResetAllTables(): Boolean {
if (db == null) return false
// reset all auto-incrementalValues
val query = SimpleSQLiteQuery("DELETE FROM sqlite_sequence")
db!!.beginTransaction()
return try {
db!!.clearAllTables()
db!!.query(query)
db!!.setTransactionSuccessful()
true
} catch (e: Exception){
false
} finally {
db!!.endTransaction()
}
}