-1

So I'm creating a GitHub mobile where I can search for users and look at the profile. I'm trying to create a favorite feature where the user can be added to favorites database when clicking the button in the details page. I've already created the DatabaseHandler class and also have set the functions to add in the activity. Here is the code for the database handler:

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

companion object {
    private const val DATABASE_VERSION = 1
    private const val DATABASE_NAME = "FavoriteUsersDatabase"
    private const val TABLE_NAME = "FavoriteTable"

    private const val KEY_ID = "_id" //primary key -> unique for each record
    private const val KEY_USERNAME = "username"
}

//onCreate is called once the database is created
override fun onCreate(db: SQLiteDatabase?) {
    //creating table with fields -> using SQL command
    val CREATE_USER_TABLE = ("CREATE TABLE "+ TABLE_NAME+
            "("+ KEY_ID+" INTEGER PRIMARY KEY,"+
            KEY_USERNAME+" TEXT"+")")
    //execute the SQL command
    db?.execSQL(CREATE_USER_TABLE)
}

//onUpgrade is called when we upgrade our table
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
    db!!.execSQL("DROP TABLE IF EXISTS "+ TABLE_NAME)
    onCreate(db)
}

/**
 * Adds to database: CREATE
 */
fun addUserToFavorite(user: FavoriteModelClass): Long{
    //initialize the database that we want to write on
    val db = this.writableDatabase
    //contentValues carry the data that we want to put in the database
    val contentValues = ContentValues()
    //id is unecessary as it will automatically increment
    contentValues.put(KEY_USERNAME,user.username) //putting the username

    //success will return a long value
    val success = db.insert(TABLE_NAME,null,contentValues)
    db.close()
    return success
}

/**
 * Reading the data in database. Returns the user object: READ
 */
fun viewUserInFavorite(): ArrayList<FavoriteModelClass>{
    val userList: ArrayList<FavoriteModelClass> = ArrayList<FavoriteModelClass>()
    //selects ALL data, SQL command. Give me everything in the table!
    val selectQuery = "SELECT * FROM $TABLE_NAME"
    //initialize the database that we want to read on
    val db = this.readableDatabase
    //A SQL cursor retrieves data one row at a time
    var cursor: Cursor? = null

    try {
        cursor = db.rawQuery(selectQuery,null)
    } catch (e: SQLiteException){
        db.execSQL(selectQuery)
        return ArrayList()
    }

    //make variables to store data
    var id: Int
    var username: String

    //cursor moves to each entry one by one and retrieves data
    if (cursor.moveToFirst()){
        do {
            id = cursor.getInt(cursor.getColumnIndex(KEY_ID))
            username = cursor.getString(cursor.getColumnIndex(KEY_USERNAME))
            val user = FavoriteModelClass(id = id,username = username)
            userList.add(user)
        } while (cursor.moveToNext())
    }
    return userList
}

/**
 * Deletes a user from favorite: DELETE
 */
fun deleteUserInFavoriteByUsername(username: String): Int{
    //initialize the database that we want to delete on
    val db = this.writableDatabase
    //setup the container to show which user we want to delete
    val contentValues = ContentValues()
    contentValues.put(KEY_USERNAME,username)
    //Deleting Row where user is that
    val success = db.delete(TABLE_NAME, "$KEY_USERNAME=$username",null)
    db.close()
    return success
}

}

Here is the code in the activity:

    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityDetailUserBinding.inflate(layoutInflater)
    setContentView(binding.root)

    username = intent.getStringExtra(EXTRA_USERNAME).toString()
    binding.usernameSearchDetail.text = username

    setDetailData()

    val sectionsPagerAdapter = SectionsPagerAdapter(this)
    sectionsPagerAdapter.username = username
    val viewPager: ViewPager2 = binding.viewPager
    viewPager.adapter = sectionsPagerAdapter
    val tabs: TabLayout = binding.tabs
    TabLayoutMediator(tabs,viewPager){tab,position->
        tab.text = resources.getString(TAB_TITLES[position])
    }.attach()

    var statusFavorite = false
    favoriteIcon = binding.toolbar.buttonFavorite
    setStatusFavorite(statusFavorite)
    favoriteIcon.setOnClickListener{
        statusFavorite = !statusFavorite
        setStatusFavorite(statusFavorite)
        if (statusFavorite == true){
            addToFavorite()
        } else if (statusFavorite == false){
            deleteFromFavorite()
        }
    }
}

/**
 * Adds to favorite database
 */
private fun addToFavorite(){
    //initialize the database handler object
    val databaseHandler: DatabaseHandler = DatabaseHandler(this)
    val status = databaseHandler.addUserToFavorite(FavoriteModelClass(0,username))
    if (status>-1){
        Toast.makeText(this,"Added $username to favorites!",Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(this,"Failed to add $username to favorites!",Toast.LENGTH_SHORT).show()
    }
}

/**
 * Deletes user from favorite database
 */
private fun deleteFromFavorite(){
    val databaseHandler: DatabaseHandler = DatabaseHandler(this)
    val status = databaseHandler.deleteUserInFavoriteByUsername(username)
    if (status>-1){
        Toast.makeText(this,"Removed $username from favorites!",Toast.LENGTH_SHORT).show()
    } else {
        Toast.makeText(this,"Failed to remove $username from favorites!",Toast.LENGTH_SHORT).show()
    }
}

private fun setStatusFavorite(statusFavorite: Boolean) {
    if (statusFavorite){
        favoriteIcon.setImageResource(R.drawable.ic_favorite)
    } else {
        favoriteIcon.setImageResource(R.drawable.ic_baseline_favorite_border_24)
    }

}

I'm stuck in this. Here is my stack trace:

D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.dicoding.githubclone, PID: 10878
    android.database.sqlite.SQLiteException: no such column: brian (code 1 SQLITE_ERROR): , while compiling: DELETE FROM FavoriteTable WHERE username=brian
        at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
        at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1102)
        at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:667)
        at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
        at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:61)
        at android.database.sqlite.SQLiteStatement.<init>(SQLiteStatement.java:33)
        at android.database.sqlite.SQLiteDatabase.delete(SQLiteDatabase.java:1727)
        at com.dicoding.githubclone.database.DatabaseHandler.deleteUserInFavoriteByUsername(DatabaseHandler.kt:100)
        at com.dicoding.githubclone.activity.DetailUserActivity.deleteFromFavorite(DetailUserActivity.kt:93)
        at com.dicoding.githubclone.activity.DetailUserActivity.access$deleteFromFavorite(DetailUserActivity.kt:26)
        at com.dicoding.githubclone.activity.DetailUserActivity$onCreate$2.onClick(DetailUserActivity.kt:69)
        at android.view.View.performClick(View.java:7500)
        at android.view.View.performClickInternal(View.java:7472)
        at android.view.View.access$3600(View.java:824)
        at android.view.View$PerformClick.run(View.java:28657)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:239)
        at android.app.ActivityThread.main(ActivityThread.java:8107)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:626)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1015)

1 Answers1

1

Please post your stacktrace. From the code one obvious reason for crashing is:

db.delete(TABLE_NAME, "$KEY_USERNAME=$username",null)

$username is not quoted as a string literal here so it is treated as a column name, and you likely don't have a column name by the user name you want to delete.

In SQL you'd use 'single quotes' for string literals, but it's even better to use variables:

db.delete(TABLE_NAME, "$KEY_USERNAME=?", arrayOf(username))
laalto
  • 150,114
  • 66
  • 286
  • 303