用户代码:
@Entity
public class User {
@PrimaryKey(autoGenerate = true)
public Integer uid;
@ColumnInfo(name = "first_name")
public String firstName;
@ColumnInfo(name = "last_name")
public String lastName;
}
道:
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
"last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User user);
@Delete
void delete(User user);
}
活动:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DataBase db = Room.databaseBuilder(getApplicationContext(), DataBase.class, "DB")
.allowMainThreadQueries().build();
User user = new User();
user.firstName = "Ste";
user.lastName = "Kor";
UserDao userDao = db.userDao();
userDao.insertAll(user);
}
}
正如我从文档中了解到的,@PrimaryKey(autoGenerate = true)应该为uid生成一个新的标识符。但是当我添加一个新对象时,它被分配了ID 0,应用程序崩溃了。
如果你手动设置这个值,那么一切正常,但是我需要它自己设置
1条答案
按热度按时间vuktfyat1#
如果使用Integer,则Room期望uid为null以生成值。如果使用了一个值,比如0,那么该值将用于uid。
由于insertAll方法没有指定onConflict策略,因此默认策略是ABORT。因此,使用0,多次将导致由于UNIQUE约束冲突而崩溃(作为主键的值必须是唯一的)。
*IGNORE的onConflict策略(e.例如
@Insert(onConflict = OnConflictStrategy.IGNORE)
)不会导致崩溃,但不会插入该行。*REPLACE的onConflict策略(e.例如
@Insert(onConflict = OnConflictStrategy.REPLACE)
)将替换已经存在的行,方法是删除该行并插入另一行(与更新该行相反)@Insert
函数可以返回一个long/Long值。正如我从文档中了解到的,@PrimaryKey(autoGenerate = true)应该为uid生成一个新的标识符。
本文件具有误导性/虚假性。如果你使用
autoGenerate false
或者默认的@PrimaryKey
,那么当指定一个空的uid时,仍然会生成uid。但是,有一个细微的区别,即当autoGenerate为true时,SQLite**
AUTOINCREMENT
**关键字包含在表定义中。AUTOINCREMENT
(*autoGenerate = true
*)关键字添加了一个约束(规则),即生成的值必须大于任何使用的值(即使具有最高值的行已被删除)。SQLite需要一种方法来记录使用过的最高值,它通过使用一个名为sqlite_sequence的系统表来做到这一点,如果需要,SQLite将创建该表。sqlite_sequence表中的每个表都有一行具有AUTOINCREMENT,并且这是存储值的位置。访问和维护这个额外的表需要开销。事实上,SQLite文档说 * The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed. *
我建议最有效/正确的选择是用途:
并在插入新行时始终确保uid为空。
演示
也许考虑一下下面的内容,它展示了所述的大部分内容。注意,
autoGenerate
保留为默认值false
(建议在大多数情况下更好)因此,上面的User类一直在使用。
但是,UserDao界面已经扩展,以满足使用3个讨论/提到的冲突策略(ABORT,IGNORE和REPLACE):
为了方便使用上面的
@Database
注解抽象类TheDatabase:-最后,一些活动代码演示了各种插入函数:
主要活动:-
演示运行结果
当运行到日志的输出包括:-
使用应用程序检查,然后显示最终结果,以确认6行的最终结果,如下所示:
使用应用程序检查的新查询,可以看到sqlite_master的缺失,如下所示:-
autoGenerate true
,则资源昂贵的sqlite_sequence表可以被视为如下:要显示这个隐藏的别名rowid,则:-x1c4d 1x
备注
在Room中,仅通过注解支持rowid的表。但是,SQLite确实支持***WITHOUT ROWID表***,在这种情况下不能使用
AUTOINCREMENT
。AUTOINCREMENT
又名autoGenerate=true
,由于生成的行必须大于任何现有行的规则,如果生成的值允许的最大值(9223372036854775807),当没有AUTOINCREMENT
时,算法将尝试使用未使用的值(如果使用了负的rowid,则这可以是负值),因此不太可能导致SQLITE_FULL异常。对于当前的存储设备,不可能有这样的行数。但是,如果9223372036854775807被指定为一个值,即使只有几行,那么由于规则,如果使用
AUTOINCREMENT
,则会发生异常。例如:-
然后:-
事件日志显示 11:48 am Database Inspector:运行语句时出错:数据库或磁盘已满(代码13 SQLITE_FULL),即使只有7行
如果没有
AUTOINCREMENT
,则使用相同的2个查询会导致: