我正试图在Android Studio中创建基于BoardGameGeek API的应用程序,然而由于未知的原因,我的数据库创建时没有列.以下是逻辑日志(log):

(1) no such table: games in "INSERT INTO games(release_year,bgg_id,game_title,thumbnail,original_title) VALUES (?,?,?,?,?)"

实现数据库+插入方法的类:

class DatabaseInit(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

    companion object {
        val DATABASE_VERSION = 2
        val DATABASE_NAME = "boardgames.db"
        // Tabela z informacjami o grach
        val TABLE_GAMES = "games"
        val COLUMN_GAME_TITLE = "game_title"
        val COLUMN_ORIGINAL_TITLE = "original_title"
        val COLUMN_RELEASE_YEAR = "release_year"
        val COLUMN_BGG_ID = "bgg_id"
        val COLUMN_TYPE = "type"
        val COLUMN_THUMBNAIL = "thumbnail"

    }

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL("DROP TABLE IF EXISTS `$TABLE_GAMES`")
        val CREATE_TABLE_GAMES = "CREATE TABLE $TABLE_GAMES(" +
                "$COLUMN_GAME_TITLE TEXT," +
                "$COLUMN_ORIGINAL_TITLE TEXT," +
                "$COLUMN_RELEASE_YEAR INTEGER," +
                "$COLUMN_BGG_ID INTEGER PRIMARY KEY," +
                "$COLUMN_TYPE INTEGER," +
                "$COLUMN_THUMBNAIL BLOB)"

        db.execSQL(CREATE_TABLE_GAMES)


    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL("DROP TABLE IF EXISTS $TABLE_GAMES")
        onCreate(db)
    }

    fun addDataFromXml(context: Context) {
        val xmlFile = File(context.filesDir, "XML/dane.xml")
        val xmlInputStream = FileInputStream(xmlFile)
        val xmlParser = Xml.newPullParser()
        xmlParser.setInput(xmlInputStream, null)


        var eventType = xmlParser.eventType
        var gameId = ""
        var gameType = -1
        var gameTitle = ""
        var releaseYear = ""
        var thumbnail = ""

        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG && xmlParser.name == "item") {
                gameId = xmlParser.getAttributeValue(null, "objectid")
                val subtype = xmlParser.getAttributeValue(null, "subtype")

                gameType = when (subtype) {
                    "boardgame" -> 1
                    "boardgameexpansion" -> 0
                    else -> -1
                }
            } else if (eventType == XmlPullParser.START_TAG && xmlParser.name == "name") {
                gameTitle = xmlParser.nextText()
            } else if (eventType == XmlPullParser.START_TAG && xmlParser.name == "yearpublished") {
                releaseYear = xmlParser.nextText()
            } else if (eventType == XmlPullParser.START_TAG && xmlParser.name == "thumbnail") {
                thumbnail = xmlParser.nextText()
            } else if (eventType == XmlPullParser.END_TAG && xmlParser.name == "item") {
                // Dodawanie danych do bazy danych
                val values = ContentValues().apply {
                    put(COLUMN_GAME_TITLE, gameTitle)
                    put(COLUMN_ORIGINAL_TITLE, "")
                    put(COLUMN_RELEASE_YEAR, releaseYear.toInt())
                    put(COLUMN_BGG_ID, gameId.toInt())
                    put(COLUMN_THUMBNAIL, thumbnail)
                }

                val db = writableDatabase
                db.insert(TABLE_GAMES, null, values)
                db.close()
            }

            eventType = xmlParser.next()
        }

        xmlInputStream.close()
    }
}

插入方法是从另一个类调用的,但这里不是这种情况,因为表是从onCreate方法空创建的. 已将以下权限添加到android list : <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

我已经做了几次try ,以使其发挥作用,包括:

  1. 从仿真器文件中手动删除数据库
  2. 数据库版本从%1增加到%2
  3. 删除字符串内插并将其替换为纯文本
val CREATE_TABLE_GAMES = "CREATE TABLE $TABLE_GAMES(" +
        "$COLUMN_GAME_TITLE TEXT," +
        "$COLUMN_ORIGINAL_TITLE TEXT," +
        "$COLUMN_RELEASE_YEAR INTEGER," +
        "$COLUMN_BGG_ID INTEGER PRIMARY KEY," +
        "$COLUMN_TYPE INTEGER," +
        "$COLUMN_THUMBNAIL BLOB)"
  1. 以下代码重新格式化:
class DatabaseInit(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

    companion object {
        const val DATABASE_VERSION = 1
        const val DATABASE_NAME = "boardgames.db"
        const val TABLE_GAMES = "games"
        const val COLUMN_GAME_TITLE = "game_title"
        const val COLUMN_ORIGINAL_TITLE = "original_title"
        const val COLUMN_RELEASE_YEAR = "release_year"
        const val COLUMN_BGG_ID = "bgg_id"
        const val COLUMN_TYPE = "type"
        const val COLUMN_THUMBNAIL = "thumbnail"
        private const val CREATE_TABLE_GAMES = "CREATE TABLE $TABLE_GAMES (" +
                "$COLUMN_GAME_TITLE TEXT," +
                "$COLUMN_ORIGINAL_TITLE TEXT," +
                "$COLUMN_RELEASE_YEAR INTEGER," +
                "$COLUMN_BGG_ID INTEGER PRIMARY KEY," +
                "$COLUMN_TYPE INTEGER," +
                "$COLUMN_THUMBNAIL BLOB)"
    }

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(CREATE_TABLE_GAMES)
    }
//remaining code without changes

我的另一个 idea 是,程序不能看到那个数据库,但是创建空数据库会让我离开它.

提前感谢任何提示,如何解决我的问题.

推荐答案

您的问题不是没有数据库,而是数据库没有games表.这很可能是因为实际上存在一个数据库(您可能希望跳到最后).

Demo

首先是一个显示存在的表(S)的函数:-

@SuppressLint("Range")
fun showSQLiteMaster(tagSuffix: String, db: SQLiteDatabase) {
    Log.d(TAG + tagSuffix,"Showing database schema")
    val csr = db.rawQuery("SELECT * FROM sqlite_master",null)
    while (csr.moveToNext()) {
        Log.d(TAG+tagSuffix,"\nTable is ${csr.getString(csr.getColumnIndex("name"))} SQL is ${csr.getString(csr.getColumnIndex("sql"))}" )
    }
    csr.close()
}

使用代码的派生,绕过您的addDataFromXml函数,而使用不从文件获取数据的固定/单行插入函数,如下所示:

fun addTest(context: Context) {
    Log.d(TAG,"AddTest Invoked. DB Version is ${writableDatabase.version}")
    val values = ContentValues().apply {
        put(COLUMN_GAME_TITLE, "TheGame001")
        put(COLUMN_ORIGINAL_TITLE, "")
        put(COLUMN_RELEASE_YEAR, 2020)
        put(COLUMN_BGG_ID, 1)
        put(COLUMN_THUMBNAIL, ByteArray(0))
    }
    val db = writableDatabase
    db.insert(TABLE_GAMES, null, values)
    showSQLiteMaster("_ADDTEST",db)
    //db.close() inadvisable to close the database as each time it is re-opened there is an overhead
}

onCreateonUpgrade函数稍作修改,以包括调用showSQLiteMaster函数,如下所示:

override fun onCreate(db: SQLiteDatabase) {
    Log.d(TAG,"Oncreate Invoked. Version is ${db.version}")
    db.execSQL("DROP TABLE IF EXISTS `$TABLE_GAMES`")
    val CREATE_TABLE_GAMES = "CREATE TABLE $TABLE_GAMES(" +
            "$COLUMN_GAME_TITLE TEXT," +
            "$COLUMN_ORIGINAL_TITLE TEXT," +
            "$COLUMN_RELEASE_YEAR INTEGER," +
            "$COLUMN_BGG_ID INTEGER PRIMARY KEY," +
            "$COLUMN_TYPE INTEGER," +
            "$COLUMN_THUMBNAIL BLOB)"
    db.execSQL(CREATE_TABLE_GAMES)
    showSQLiteMaster("_ONCRT", db)
}

及:-

override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
    Log.d(TAG,"OnUpgrade Invoked. FromVersion is ${oldVersion} ToVersion is ${newVersion} DBVersions is ${db.version}")
    db.execSQL("DROP TABLE IF EXISTS $TABLE_GAMES")
    onCreate(db)
}

活动(主要活动)是:-

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DatabaseInit(this).addTest(this)
    }
}

然后,当不存在数据库并且版本为1时,日志(log)包括:

2023-06-05 12:44:39.488 D/DBINFO: Oncreate Invoked. Version is 0
2023-06-05 12:44:39.489 D/DBINFO_ONCRT: Showing database schema
2023-06-05 12:44:39.490 D/DBINFO_ONCRT: Table is android_metadata SQL is CREATE TABLE android_metadata (locale TEXT)
2023-06-05 12:44:39.490 D/DBINFO_ONCRT: Table is games SQL is CREATE TABLE games(game_title TEXT,original_title TEXT,release_year INTEGER,bgg_id INTEGER PRIMARY KEY,type INTEGER,thumbnail BLOB)
2023-06-05 12:44:39.492 D/DBINFO: AddTest Invoked. DB Version is 1
2023-06-05 12:44:39.493 D/DBINFO_ADDTEST: Showing database schema
2023-06-05 12:44:39.493 D/DBINFO_ADDTEST: Table is android_metadata SQL is CREATE TABLE android_metadata (locale TEXT)
2023-06-05 12:44:39.493 D/DBINFO_ADDTEST: Table is games SQL is CREATE TABLE games(game_title TEXT,original_title TEXT,release_year INTEGER,bgg_id INTEGER PRIMARY KEY,type INTEGER,thumbnail BLOB)
  • 正如您所看到的,这会导致onCreate方法被调用,并且表被创建,并且在插入行时它不会消失.

如果再次运行,即数据库存在,则日志(log)包括:

2023-06-05 12:48:36.318 D/DBINFO: AddTest Invoked. DB Version is 1
2023-06-05 12:48:36.320 D/DBINFO_ADDTEST: Showing database schema
2023-06-05 12:48:36.322 D/DBINFO_ADDTEST: Table is android_metadata SQL is CREATE TABLE android_metadata (locale TEXT)
2023-06-05 12:48:36.322 D/DBINFO_ADDTEST: Table is games SQL is CREATE TABLE games(game_title TEXT,original_title TEXT,release_year INTEGER,bgg_id INTEGER PRIMARY KEY,type INTEGER,thumbnail BLOB)
  • 即没有如预期的那样调用onCreate.

如果版本增加到2,则:-

2023-06-05 12:50:37.349 D/DBINFO: OnUpgrade Invoked. FromVersion is 1 ToVersion is 2 DBVersions is 1
2023-06-05 12:50:37.350 D/DBINFO: Oncreate Invoked. Version is 1
2023-06-05 12:50:37.350 D/DBINFO_ONCRT: Showing database schema
2023-06-05 12:50:37.351 D/DBINFO_ONCRT: Table is android_metadata SQL is CREATE TABLE android_metadata (locale TEXT)
2023-06-05 12:50:37.351 D/DBINFO_ONCRT: Table is games SQL is CREATE TABLE games(game_title TEXT,original_title TEXT,release_year INTEGER,bgg_id INTEGER PRIMARY KEY,type INTEGER,thumbnail BLOB)
2023-06-05 12:50:37.352 D/DBINFO: AddTest Invoked. DB Version is 2
2023-06-05 12:50:37.353 D/DBINFO_ADDTEST: Showing database schema
2023-06-05 12:50:37.354 D/DBINFO_ADDTEST: Table is android_metadata SQL is CREATE TABLE android_metadata (locale TEXT)
2023-06-05 12:50:37.354 D/DBINFO_ADDTEST: Table is games SQL is CREATE TABLE games(game_title TEXT,original_title TEXT,release_year INTEGER,bgg_id INTEGER PRIMARY KEY,type INTEGER,thumbnail BLOB)
  • 如图所示,已经调用了onUpgrade,然后它调用onCreate

The likely Issue/Possible Fix

从上面看,似乎发生了什么不愉快的事情.然而,很可能发生的情况是,AutoBackup已经备份了一个没有游戏表的数据库,并且正在恢复该数据库.因此,onCreate将不被调用,因此该表将不存在.

我建议将manifest修改为按https://developer.android.com/guide/topics/data/autobackup#EnablingAutoBackup包括android:allowBackup="false"

您必须删除现有数据库(通过文件资源管理器或通过卸载应用程序).

Android相关问答推荐

Composable不会以LocalConext.Current作为活动呈现

Android可绘制边框删除底线

有没有什么方法可以让Beeware在安卓手机上显示图片?

当提供非状态对象时,compose 如何进行重组

kotlin中&&和and的区别

闪屏 API 无法在 Android 12 上运行(API 31、32)

Android Jetpack Compose全宽度抽屉优化

将 React Native 应用程序背景带到前台

需要在按钮 onclick 上从 string.xml 获取值. @Composable 调用只能在@Composable 函数的上下文中发生

在 compose 中做可变状态堆栈

只能从同一个库组内调用成功(引用groupId=androidx.work from groupId=My Composable)

在 compose android 中创建一个圆形按钮和居中文本

从活动共享视图模型以使用 hilt 组合函数

viewModel 的可变值状态不起作用

react 从输入中找到路径'lib/arm64-v8a/libfbjni.so'的本机2个文件

如何在行/列/卡片 compose 中添加左边框

未解决的参考:getIntentSender / try 在 Jetpack Compose 中获取电话号码时

等到上一个事件完成 Rx

Lombok 插件在最新版本的 Android Studio (Bumblebee) 中不起作用

Android Room Database:如何处理实体中的 Arraylist?