自动增量在房间数据库中不起作用

问题描述 投票:0回答:1

我想自动递增 Room 中的某个实体表。我正在房间中插入桌子列表。

ShopMenuEntity.kt

@Entity(tableName = ShopMenuEntity.TABLE_NAME)
data class ShopMenuEntity(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = COLUMN_ID)
    var id: Int = 0,
    var level:Int? = null,
    @ColumnInfo(name = COLUMN_PARENT_ID)
    var parentId: Int = -1
) {
    companion object {

        const val TABLE_NAME = "shop_menu_table"
        const val COLUMN_ID = "menu_id"
        const val COLUMN_PARENT_ID = "parentId"

        fun mapHttpResponse(subMenu: NewShopMenuResponse,parentId: Int): ShopMenuEntity {
            return ShopMenuEntity(
              // How can I auto increment below statement?
                id = subMenu.id ?: -1,
                level = subMenu.level,
                parentId = parentId,
            )
        }
    }
}

id = subMenu.id?: -1
必须保存在房间

我也尝试过将其设置为0。但没有工作

我的实现的详细代码在另一个问题

android sql android-room primary-key
1个回答
0
投票

autoGenerate = true
包含
AUTOINCREMENT
关键字,这会导致 SQLite(Room 包装的数据库管理器)在没有给定值的情况下生成列的值。 它不会在对象中生成值,这似乎是您的期望。

  • 请注意,自动增量并不是导致值生成的原因。任何定义为 INTEGER(Kotlin Room 中的 Int Long 等)且作为主键的列,都会在没有提供值的情况下生成值。 AUTOINCRMENT 的作用是添加一个约束(规则),该约束表示该值必须大于分配给该列的任何值。实际上,它很少需要,而且效率很低,因为它利用额外的系统表(sqlite_sequence - 请参阅最后通过 App Inspection 运行的查询)来存储曾经分配的最高值。

Room,在插入时认为 0 表示没有值,因此不提供任何值,包括 -1 在内的任何其他整数值将导致尝试插入具有该列值的行。由于该列必须是主键,因此值必须是唯一的,因此如果插入了 -1 值,则不会插入值为 -1 的后续插入。

您似乎正在尝试确定/预测/假设生成的值。没有必要,如果您使用 Room 的

@Insert
那么生成的值将作为 Long 返回,或者在未插入行的情况下,没有错误,则返回值将为 -1。

虽然很有可能,生成的值将比曾经插入的最高值大 1,或者在第一个插入行 1 的情况下。但是,不能保证该值将大 1。

简而言之,您应该插入行,然后获取值(例如,利用从

@Insert
返回的结果),而不是尝试对值做出假设。


示范


使用以下代码:-

@Entity(tableName = ShopMenuEntity.TABLE_NAME)
data class ShopMenuEntity(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = COLUMN_ID)
    var id: Int = 0,
    var level:Int? = null,
    @ColumnInfo(name = COLUMN_PARENT_ID)
    var parentId: Int = -1
) {
    companion object {

        const val TABLE_NAME = "shop_menu_table"
        const val COLUMN_ID = "menu_id"
        const val COLUMN_PARENT_ID = "parentId"

        /*
        fun mapHttpResponse(subMenu: NewShopMenuResponse,parentId: Int): ShopMenuEntity {
            return ShopMenuEntity(
                // How can I auto increment below statement?
                id = subMenu.id ?: -1,
                level = subMenu.level,
                parentId = parentId,
            )
        }
         */
    }
}
  • 即您的代码减少了相当尴尬的功能(请参阅下面
    addSubShopMenuEntityAndItsParent
    界面中的
    ShopMenuDAO

:-

@Dao
interface ShopMenuDAO {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insert(shopMenuEntity: ShopMenuEntity): Long
    @Query("SELECT coalesece(max(${ShopMenuEntity.COLUMN_ID}),-1) FROM ${ShopMenuEntity.TABLE_NAME}")
    fun getLastInsertedShopMenuEntityId(): Int

    /* An example of how you could achieve what appears to be required */
   @Transaction /* Do everything with a single transaction */
   @Query("") /* fool Room into think this should be in a transaction */
    fun addSubShopMenuEntityAndItsParent(subShopMenuEntity: ShopMenuEntity, parentShopMenuEntity: ShopMenuEntity, parentId: Long?=null): ShopMenuEntity {
       var parentIdToUse = parentId
       if (parentId==null) {
           val insertedParentId = insert(parentShopMenuEntity)
           /* If not inserted then return ShopMenuEntity indicating parent not inserted i.e. id = -99 */
           if (insertedParentId == -99L) return ShopMenuEntity(-1)
           parentIdToUse = insertedParentId
       } else {
            /* Supplying the Parent ID so use that as parent (SHOULD) already exist */
            parentIdToUse = parentId;
       }
       subShopMenuEntity.parentId = parentIdToUse.toInt()
       val insertedSubId = insert(subShopMenuEntity)
       /* If not inserted then return ShopMenuEntity indicating that sub was not inserted i.e. -1 */
       if (insertedSubId == -1L) {
           return ShopMenuEntity(-1)
       }
       subShopMenuEntity.id = insertedSubId.toInt()
       return subShopMenuEntity
    }
}
  • 注意插入返回生成的 id(应该是 Long 而不是 Int)
  • 额外但不是真正需要的
    getLastInsertedShopMenuEntityId
    ,它应该返回最后插入的ID(警告如果插入指定ID的海湾,情况可能并不总是如此)
  • 最后一种插入具有父级的 ShopMenuEntity 的方法(可以插入或仅根据提供的parentId插入)
    • 请注意,这不一定完全全面,但旨在演示该概念

允许实际演示基本的

@Database
带注释的类:-

/* Basic @Database abstract class */
@Database(entities = [ShopMenuEntity::class], exportSchema = false, version = 1)
abstract class ShopDatabase: RoomDatabase() {
    abstract fun getShopMenuDAO(): ShopMenuDAO

    companion object {
        private var instance: ShopDatabase?=null

        fun getInstance(context: Context): ShopDatabase {
            if (instance==null) {
                instance = Room.databaseBuilder(context,ShopDatabase::class.java,"whatever.db")
                    .allowMainThreadQueries() /* use the main thread for brevity */
                    .build()
            }
            return instance as ShopDatabase
        }
    }
}

最后一些实际演示的活动代码:-

class MainActivity : AppCompatActivity() {

    lateinit var db: ShopDatabase
    lateinit var dao: ShopMenuDAO
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = ShopDatabase.getInstance(this)
        dao = db.getShopMenuDAO()

        /* Normal insert of a shop only */
        val s1Id = dao.insert(ShopMenuEntity(0,1))
        Log.d("DBINFO","The id of the last inserted shop was ${dao.getLastInsertedShopMenuEntityId()}")
        /* Insert of a shop with a parent, the parent's id being passed (the parent ShopMenuEntity is ignored) */
        val s2 = dao.addSubShopMenuEntityAndItsParent(ShopMenuEntity(0,100),ShopMenuEntity(-100),s1Id)
        if (s2.id < 0) Log.d("DBINFO","Oooops something not quite right as id of subShop is less than 0, it was ${s2.id}")
        Log.d("DBINFO","The id of the last inserted shop was ${dao.getLastInsertedShopMenuEntityId()}")
        /* Add a subShop along with the new parentShop */
        val s3 = dao.addSubShopMenuEntityAndItsParent(ShopMenuEntity(level = 3), ShopMenuEntity(99,0),null)
        if (s3.id < 0) Log.d("DBINFO","Oooops something not quite right as id of subShop is less than 0, it was ${s3.id}")
        Log.d("DBINFO","The id of the last inserted shop was ${dao.getLastInsertedShopMenuEntityId()}")


        for (sme in dao.getAllShopMenuEntities()) {
            Log.d("DBINFO","Shop ID is ${sme.id} Level is ${sme.level} Parent ID is ${sme.parentId}")
        }
    }
}

当上面的代码运行时(作为一个新的应用程序,它不是设计来重新运行的),那么日志包含:-

2023-08-04 10:46:44.820 D/DBINFO: The id of the last inserted shop was 1
2023-08-04 10:46:44.825 D/DBINFO: The id of the last inserted shop was 2
2023-08-04 10:46:44.845 D/DBINFO: The id of the last inserted shop was 100
2023-08-04 10:46:44.847 D/DBINFO: Shop ID is 1 Level is 1 Parent ID is -1
2023-08-04 10:46:44.847 D/DBINFO: Shop ID is 2 Level is 100 Parent ID is 1
2023-08-04 10:46:44.847 D/DBINFO: Shop ID is 99 Level is 0 Parent ID is -1
2023-08-04 10:46:44.847 D/DBINFO: Shop ID is 100 Level is 3 Parent ID is 99

可以看出:-

  1. ID 1、2 和 100 已生成,并且 ID 99 已指定并相应设置(不建议指定 id,而是生成它们)。

  2. 父母已相应设置,即

    1. ID 1 有 -1(无父级),ID 99 也同样
    2. ID 2 有父级 1(通过
      @Insert
      获取的父级 ID)
    3. ID 99 与其子级一起插入,该子级具有生成的 ID 100,并且 99 作为其父级。两者都通过
      addSubShopMenuEntityAndItsParent
      函数指定为新插入(这可能相当于您的
      mapHttpResponse
      函数)。

如果使用App Inspection,则以上 4 行将按以下方式反映:-

此外,通过“打开新查询选项卡”使用查询

SELECT * FROM sqlite_sequence;
,然后您会得到:-

100的最高分配ID已存储在sqlite_sequence表中(因为已使用AUTOINCRMENT)。因此,当生成 menu_id 列的值时,它将是 1 + 最高的 menu_id 值和表中保存在 sqlite_sequence 中的值(sqlite_sequence 表的名称列)中的较高者。

您可能希望参考 https://www.sqlite.org/autoinc.html 了解有关自动增量的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.