English 中文(简体)
SQLite Kotlin issue - no database
原标题:

I am trying to create boardgamegeek APIs based app in AndroidStudio, however from unknown reason I m getting my database created with no columns. Here s Logical log:

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

Class which implements database + the insertion method:

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()
    }
}

The insertion method is being called from another class, however that is not the case here since the table is being created empty from onCreate method. The following permissions have been added to androidmanifest: <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

I ve done several attempts to make it work which include:

  1. Manual database removal from emulator files
  2. DATABASE_VERSION increase from 1 to 2
  3. Droping string interpolation and replacing it with plain text
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. Following code reformat:
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

My another idea was that the programs cannot see that database, however creating it empty make me leave it.

Thanks in advance for any tips how to resolve my issue.

问题回答

You issue is not that there is no database, but rather that the database does not have the games table. This is likely because there is in fact a database (you may wish to skip to the end).

Demo

First a function to show the table(s) that exist:-

@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,"
Table is ${csr.getString(csr.getColumnIndex("name"))} SQL is ${csr.getString(csr.getColumnIndex("sql"))}" )
    }
    csr.close()
}

Using a derivation of your code, that bypasses your addDataFromXmlfunction and instead uses a fixed/single row insertion function that does not get data from a file, as per:-

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
}

The onCreate and onUpgrade funtions are slightly modified to include invoking the showSQLiteMaster function as per:-

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)
}

and :-

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)
}

An Activity (MainActivity) being:-

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

Then when no database exists and the version is 1, then the log includes:-

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)
  • This, as you can see, results in the onCreate method being invoked and that the table is created and that it does not disappear when inserting the row.

If run again, i.e. the database exists then the log includes:-

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)
  • i.e. onCreate has not be called as would be expected.

If the Version is increased to 2 then:-

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)
  • As can be seen onUpgrade has been invoked, which then invokes onCreate

The likely Issue/Possible Fix

From the above it would seem if something untoward is happening. However, what may well be happening is that AutoBackup has backed up a database that is devoid of the games table and is restoring the database. As such onCreate will not be called and the thus the table will not exist.

I would suggest modifying the manifest to include android:allowBackup="false" as per https://developer.android.com/guide/topics/data/autobackup#EnablingAutoBackup





相关问题
Android - ListView fling gesture triggers context menu

I m relatively new to Android development. I m developing an app with a ListView. I ve followed the info in #1338475 and have my app recognizing the fling gesture, but after the gesture is complete, ...

AsyncTask and error handling on Android

I m converting my code from using Handler to AsyncTask. The latter is great at what it does - asynchronous updates and handling of results in the main UI thread. What s unclear to me is how to handle ...

Android intent filter for a particular file extension?

I want to be able to download a file with a particular extension from the net, and have it passed to my application to deal with it, but I haven t been able to figure out the intent filter. The ...

Android & Web: What is the equivalent style for the web?

I am quite impressed by the workflow I follow when developing Android applications: Define a layout in an xml file and then write all the code in a code-behind style. Is there an equivalent style for ...

TiledLayer equivalent in Android [duplicate]

To draw landscapes, backgrounds with patterns etc, we used TiledLayer in J2ME. Is there an android counterpart for that. Does android provide an option to set such tiled patterns in the layout XML?

Using Repo with Msysgit

When following the Android Open Source Project instructions on installing repo for use with Git, after running the repo init command, I run into this error: /c/Users/Andrew Rabon/bin/repo: line ...

Android "single top" launch mode and onNewIntent method

I read in the Android documentation that by setting my Activity s launchMode property to singleTop OR by adding the FLAG_ACTIVITY_SINGLE_TOP flag to my Intent, that calling startActivity(intent) would ...

From Web Development to Android Development

I have pretty good skills in PHP , Mysql and Javascript for a junior developer. If I wanted to try my hand as Android Development do you think I might find it tough ? Also what new languages would I ...

热门标签