ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Android] SQLite, SQLiteOpenHelper, Local DB ์ดํ•ด
    Android 2021. 11. 30. 09:46
    ๋ฐ˜์‘ํ˜•

    SQLite๋ž€?

      Android ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด์„œ ์•ฑ์„ ์‚ฌ์šฉํ•˜๊ณ  ์ข…๋ฃŒํ•˜๋”๋ผ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์† ์ €์žฅ๋˜์–ด์•ผํ•  ํ•„์š”์„ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด SharedPreference๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ๊ฒ ์ง€๋งŒ, key์™€ value์˜ ๊ฐ’์œผ๋กœ๋งŒ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ํž˜๋“ค๊ณ , ๋งŽ์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ๋Š” ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ณ  ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ๋Š” ๊ฐ€๋ฒผ์šด ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ธ SQLite๊ฐ€ ๋„ค์ดํ‹ฐ๋ธŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. SQLite์˜ ์ฃผ์š” ํŠน์ง•์€ ๋ฐ์ดํ„ฐ ์กฐํšŒ๊ฐ€ ๋น ๋ฅด๊ณ , ํ‘œ์ค€ SQL ์„ ์ง€์›ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ฃผ์š” ๊ธฐ๋Šฅ์ธ C(Create), R(Read), U(Update), D(Delete)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

     

     

      SQLite๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ Todo List๋ฅผ ์ž‘์„ฑํ•˜๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์•„ํ‚คํ…์ฒ˜์™€ ๋””์ž์ธ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ๋™์ž‘๋งŒ ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ˆ์ œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ดค์Šต๋‹ˆ๋‹ค.

     

     

     

    ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งŒ๋“ค๊ธฐ

      ๋จผ์ € ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ API๋Š” ContextWrapper ํด๋ž˜์Šค์— ์žˆ๋Š” openOrCreateDatabase ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.

    https://developer.android.com/reference/android/content/ContextWrapper#peekWallpaper()

     DB ํŒŒ์ผ ์ด๋ฆ„, ํŒŒ์ผ ๋ชจ๋“œ, Cursor ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” Factory๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์•„ SQLiteDatabase๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ํ•ด๋‹น API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด name์œผ๋กœ ์ง€์ •๋œ ํŒŒ์ผ๋ช…์˜ db ํŒŒ์ผ์ด ์ƒ๊ธฐ๊ณ  SQLiteDatabas ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์—ด๊ฑฐ๋‚˜ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

     

     

    https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase#execSQL(java.lang.String)

      SQLiteDatabase๊ฐ์ฒด์—์„œ ์ค‘์š”ํ•œ ๋ฉ”์†Œ๋“œ๋กœ execSQL๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. execSQL์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ๋งŒ๋“  ํ›„์— ํ‘œ์ค€ SQL๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋ฆฌํ„ด ๊ฐ’์ด void์ด๊ธฐ ๋•Œ๋ฌธ์— SELECT ๊ตฌ๋ฌธ๊ณผ ๊ฐ™์€ ๋ฐ˜ํ™˜ ๊ฐ’์ด ์กด์žฌํ•˜๋Š” SQL๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

     

     

     

    ํ…Œ์ด๋ธ” ์ƒ์„ฑ/์‚ญ์ œ

    โ€ป ์Šคํ‚ค๋งˆ

      ํ…Œ์ด๋ธ” ์ƒ์„ฑํ•˜๊ธฐ์— ์•ž์„œ ์Šคํ‚ค๋งˆ ์šฉ์–ด์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์Šคํ‚ค๋งˆ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ๋Š” ํ…Œ์ด๋ธ”์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•œ ๊ฒƒ์ด๊ณ  ์‚ฌ์ „์  ์ •์˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž๋ฃŒ์˜ ๊ตฌ์กฐ, ์ž๋ฃŒ์˜ ํ‘œํ˜„ ๋ฐฉ๋ฒ•, ์ž๋ฃŒ ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ํ˜•์‹ ์–ธ์–ด๋กœ ์ •์˜ํ•œ ๊ตฌ์กฐ๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋‚ด์—๋Š” ์—ฌ๋Ÿฌ ํ…Œ์ด๋ธ”์ด ์กด์žฌํ•˜๊ณ  ๊ฐ ํ…Œ์ด๋ธ”์—๋Š” ๋ฐ์ดํ„ฐ์˜ ํ˜•์‹, ๊ฐฏ์ˆ˜ ,Primary key ๊ฐ€ ๋ชจ๋‘ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ์ •์˜ํ•œ ๊ฒƒ์ด ์Šคํ‚ค๋งˆ๋ผ๊ณ  ์ƒ๊ฐํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. 

     

     

      ์•ˆ๋“œ๋กœ์ด๋“œ ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ์Šคํ‚ค๋งˆ๋ฅผ ์ž˜ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ Contract ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  BaseColumns ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„์ฒด๋ฅผ ๋งŒ๋“ค์–ด ๊ธฐ๋ณธ ํ‚ค ํ•„๋“œ๋ฅผ ์ƒ์†๋ฐ›๋Š” ๊ฒƒ์„ ๊ฐ€์ด๋“œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 

    object TodoContract {
        const val DATABASE_NAME = "Todo.db"
    
        object TodoEntry : BaseColumns {
            const val TABLE_NAME = "TODO"
            const val COLUMN_TITLE = "title"
            const val COLUMN_DESCRIPTION = "description"
            const val COLUMN_DATE = "date"
    
            const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
                    "$_ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    "$COLUMN_TITLE TEXT, " +
                    "$COLUMN_DESCRIPTION TEXT, " +
                    "$COLUMN_DATE TEXT)"
    
            const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME"
        }
    }

     TodoContract ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  TodoEntry๋Š” ํ•˜๋‚˜์˜ ํ…Œ์ด๋ธ” ์ •์˜ํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ ์ƒ์ˆ˜๋“ค์„ ์„ ์–ธํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์ฒ˜๋Ÿผ ์Šคํ‚ค๋งˆ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ ˆ์ด์•„์›ƒ ์—ญํ• ์„ ํ•˜๋Š” Contract ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฐ ํ…Œ์ด๋ธ”๋งˆ๋‹ค Entry ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋™์ผ ํŒจํ‚ค์ง€์˜ ๋ชจ๋“  ํด๋ž˜์Šค์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. 

    private fun createTable() {
            Thread {
                db.execSQL(CREATE_QUERY)
            }.start()
    }

     TodoEntry์— ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๋Š” SQL๋ฌธ์„ ์„ ์–ธํ•˜๊ณ  ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ๋งŒ๋“ค์–ด์„œ execSQL ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ๋ช…๋ น์–ด๋Š” ๋ฉ”์ธ์Šค๋ ˆ๋“œ์—์„œ ์ฟผ๋ฆฌ๋ฅผ ํ•ด๋„ ๋ฌธ์ œ๋Š” ์—†์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ DB ๋ช…๋ น์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ด๋ธ” ์ด๋ฆ„์€ ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ณ„ ์—†๊ณ  ๋„์–ด์“ฐ๊ธฐ๋งŒ ์กฐ์‹ฌํ•˜์—ฌ syntax error๊ฐ€ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

     

      View - Tool Windows - Devie File Expolorer์„ ํ†ตํ•ด db ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. /data/data/package_name ํด๋”์— ๋“ค์–ด๊ฐ€๋ฉด databases ํด๋”๊ฐ€ ์žˆ๊ณ  ํ•ด๋‹น ํด๋”์— openOrCreateDatabase ๋ฉ”์†Œ๋“œ๋กœ ์ƒ์„ฑํ•œ DB ํŒŒ์ผ์ด ์žˆ์Šต๋‹ˆ๋‹ค. DB ํŒŒ์ผ์„ ์ฝ๊ธฐ ์œ„ํ•ด์„œ๋Š” ์˜ค๋ฅธ์ชฝ ํด๋ฆญ ํ›„ Save as ๋ˆŒ๋Ÿฌ ์ €์žฅํ•˜๊ณ  DB Browser for SQLite ํˆด์„ ์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

     

     

    Downloads - DB Browser for SQLite

    (Please consider sponsoring us on Patreon ๐Ÿ˜„) Windows Our latest release (3.12.2) for Windows: Windows PortableApp Note - If for any reason the standard Windows release does not work (e.g. gives an error), try a nightly build (below). Nightly builds ofte

    sqlitebrowser.org

      ์„ค์น˜ ํ›„ ์‹คํ–‰ํ•˜์—ฌ ํ•ด๋‹น DB ํŒŒ์ผ์„ ์—ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด DB ๋‚ด ํ…Œ์ด๋ธ”์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ๋ณด๊ธฐ ํƒญ์—์„œ๋Š” ํ…Œ์ด๋ธ” ๋‚ด ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋“ค๋„ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์‚ญ์ œ๋„ ๋™์ผํ•˜๊ฒŒ DROP TABLE ์„ ์‚ฌ์šฉํ•˜์—ฌ execSQL ๋ฉ”์†Œ๋“œ๋กœ ์‹คํ–‰ํ•˜๋ฉด ํ…Œ์ด๋ธ” ์‚ญ์ œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

     

     

     

    ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ

     private fun bindViews() = with(binding) {
     	...   
        
            Thread {
                var sql = "INSERT INTO $TABLE_NAME " +
                        "($COLUMN_TITLE, $COLUMN_DESCRIPTION, $COLUMN_DATE)" +
                        " VALUES " +
                        "('$title', '$description', '$date')"
    
                Log.d(TAG, sql)
                db.execSQL(sql)
                updateRecyclerView()
            }.start()
            
            ...
     }

      floatingButton์„ ํด๋ฆญํ•˜๋ฉด Dialog๊ฐ€ ๋‚˜ํƒ€๋‚˜๊ณ  3๊ฐœ์˜ EditText๋กœ Title, Description, Date ๋ฌธ์ž๋ฅผ ์ž…๋ ฅ๋ฐ›๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ์ €์žฅ์„ ํด๋ฆญํ•˜๋ฉด ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ INSERT DB ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค. INSERT ๊ตฌ๋ฌธ์—์„œ ๋ฐ์ดํ„ฐ์— ํ•ด๋‹น๋˜๋Š” VALUES๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ ๋”ฐ์˜ดํ‘œ๋กœ ๊ตฌ๋ณ„ํ•˜์—ฌ ๊ฐ ์ปฌ๋Ÿผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋งค์นญ์‹œ์ผœ์ค˜์•ผ syntax error๊ฐ€ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

     

     DB ํŒŒ์ผ์— ์ •์ƒ์ ์œผ๋กœ ์ž‘์„ฑ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. primary key์— ํ•ด๋‹นํ•˜๋Š” _id ํ•„๋“œ๋Š” autoincrement ํ•ด๋‘์–ด 1๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์—ฌ ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค 1์”ฉ ์ฆ๊ฐ€ํ•˜์—ฌ ์ถ”๊ฐ€๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

     

     

     

    ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ

    private fun updateTodoData(position: Int) {
    	...
        
            Thread {
                var sql = "UPDATE $TABLE_NAME SET " +
                        "$COLUMN_TITLE = '$title', " +
                        "$COLUMN_DESCRIPTION = '$description', " +
                        "$COLUMN_DATE = '$date' " +
                        "WHERE $_ID = $position"
                Log.d(TAG, sql)
                db.execSQL(sql)
                updateRecyclerView()
            }.start()
            
            ...
        }

     Update๋„ ๋™์ผํ•˜๊ฒŒ Dialog๊ฐ€ ๋‚˜ํƒ€๋‚˜์„œ Title, Description, Date๋ฅผ ๋ฐ›์•„์„œ ๋ณ€๊ฒฝํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.  primary key๊ฐ€ _ID์ด๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ฑด๋ฌธ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์€ ID๋กœ ํ•„ํ„ฐ๋ฅผ ๊ฑธ์–ด SQL๋ฌธ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ ๋ฐ์ดํ„ฐ๊ฐ€ Title = Android, Description = SQLite, Date = 2021-12-02 ๋กœ ์ €์žฅ๋˜์–ด ์žˆ์œผ๋‹ˆ, SQLite์—์„œ Room์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์—…๋ฐ์ดํŠธ ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

     

     

     

     

    ๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜๊ธฐ

        private fun updateRecyclerView() {
            if(db == null) return
    
            todoAdapter.clear()
            Thread {
                val list = mutableListOf<Todo>()
                val sql = "select * from ${TABLE_NAME}"
                val cursor = db.rawQuery(sql, null)
                while (cursor.moveToNext()) {
                    list.add(
                        Todo(
                            cursor.getString(1),
                            cursor.getString(2),
                            cursor.getString(3)
                        )
                    )
                }
                todoAdapter.addAll(list)
                runOnUiThread {
                    todoAdapter.notifyDataSetChanged()
                }
            }.start()
        }

      ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” SELECT๋กœ ์‹œ์ž‘ํ•˜๋Š” SQL๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ์ด์ „์—๋Š” execSQL ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ๋ฐ์ดํ„ฐ ์กฐํšŒ๋Š” ๊ฒฐ๊ณผ๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— rawQuery๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

    https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase#rawQuery(java.lang.String,%20java.lang.String[])

      rawQuery ๋ฉ”์†Œ๋“œ์˜ ๋ฆฌํ„ด ๊ฐ’์ธ Cursor ๊ตฌํ˜„์ฒด๋Š” ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ์— ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๋Š” ์ ‘๊ทผ ๊ถŒํ•œ์„ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์ฆ‰, Cursor ๋ฅผ ํ†ตํ•ด์„œ ์ด๋ฏธ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฒ˜์Œ์—๋Š” ์•„๋ฌด๋Ÿฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€ ์•Š๊ณ  moveToNext ๋ฉ”์†Œ๋“œ๋กœ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•˜์—ฌ ๊ฐ’์„ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„ ์˜ˆ์‹œ์—์„œ moveToNext ๊ฒฐ๊ณผ๊ฐ€ false ์ „๊นŒ์ง€ ์‹คํ–‰ํ•˜์—ฌ Todo list์— ์ถ”๊ฐ€ํ•˜๋„๋ก ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

     

     

    ๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๊ธฐ

        private fun deleteTodoData(id: Int) {
            Thread {
                var sql = "DELETE FROM $TABLE_NAME WHERE _id = ${id}"
                db?.execSQL(sql)
                updateRecyclerView()
            }.start()
        }

      ๋ฐ์ดํ„ฐ ์‚ญ์ œ ๋˜ํ•œ DELETE ์ฟผ๋ฆฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ WHERE ์— ํ•ด๋‹นํ•˜๋Š” ์กฐ๊ฑด์„ ์ถฉ์กฑ์‹œํ‚ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

    ๋ฐ˜์‘ํ˜•

     

    SQLiteOpenHelper

      SQLiteDatabase ๋Š” SQLite์— ์ ‘๊ทผํ•˜๋Š” ํด๋ž˜์Šค๋กœ, SQL ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๊ณ  DB๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์˜ˆ์‹œ์—์„œ๋Š” SQLiteDatabase๋ฅผ openOrCreate ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ, ์‚ฌ์‹ค์ƒ ์ง์ ‘ ์‚ฌ์šฉํ•˜๊ณ  ์ ‘๊ทผํ•˜๋Š” ์ผ์€ ํ”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ธฐ์กด์˜ ์ƒ์„ฑํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€๋กœ ์ธํ•œ ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝํ•˜๋Š” ์ผ์ด ์žˆ์„ ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ๋„์™€์ฃผ๋Š” ํด๋ž˜์Šค๊ฐ€  SQLiteOpenHelper๋กœ DB ์ƒ์„ฑ๊ณผ DB์˜ ๋ฒ„์ „๊ด€๋ฆฌ๋ฅผ ์•Œ์•„์„œ ํ•ด์ฃผ๋Š” ํ—ฌํผ(Helper)ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค.

     

    https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper#SQLiteOpenHelper(android.content.Context,%20java.lang.String,%20android.database.sqlite.SQLiteDatabase.CursorFactory,%20int)

     ์ƒ์„ฑ์ž๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์œผ๋ฉฐ, ์„ธ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” Cursor ๊ตฌํ˜„์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” Factory๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.  ๊ธฐ๋ณธ ๊ตฌํ˜„์ฒด์ธ SQLiteCursor๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด null ๊ฐ’์„ ์ „๋‹ฌํ•˜๋ฉด ๋˜๊ณ , ์ƒˆ๋กœ์šด Factory๋ฅผ ์ƒ์„ฑํ•ด์„œ ์ „๋‹ฌํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ ๋Œ€๋ถ€๋ถ„ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

    ๋„ค ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฒ„์ „์„ ์ž…๋ ฅํ•˜๊ฒŒ ๋˜๊ณ , ๋ฒ„์ „์„ ์—…๋ฐ์ดํŠธํ•  ๊ฒฝ์šฐ์— ํ•ด๋‹น ํŒŒ๋ผ๋ฏธํ„ฐ์—๋Š” ๊ธฐ์กด์˜ ๋ฒ„์ „๋ณด๋‹ค ๋†’๊ฒŒ ๋Œ€์ž…์„ ํ•˜์—ฌ ๋ฒ„์ „ ๋ณ€๊ฒฝ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    class TodoSQLHelper private constructor(context: Context
    ) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    
        override fun onCreate(db: SQLiteDatabase?) {
            db?.execSQL(CREATE_QUERY)
        }
    
        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
            // TODO("Not yet implemented")
        }
    
        companion object {
            const val DATABASE_VERSION = 1
            const val DATABASE_NAME = "Todo.db"
            var instance : TodoSQLHelper? = null
    
            fun getInstance(context: Context) : TodoSQLHelper{
                return if(instance == null) TodoSQLHelper(context.applicationContext) else instance!!
            }
        }
    }

      DB์˜ ์ƒ์„ฑ ์‹œ์ ์€ SQLiteOpenHelper ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ๋  ๋•Œ๋กœ ์ฐฉ๊ฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ํ›„ getReadableDatabase, getWritalbeDatabase ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ DB๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. SQLiteOpenHelper ์ธ์Šคํ„ด์Šค ์ด๋ฏธ ์ƒ์„ฑ์„ ํ–ˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ํ•œ ๋ฒˆ๋„ ์ƒ์„ฑํ•œ ์ ์ด ์—†๋‹ค๋ฉด ์ƒˆ๋กœ ์ƒ์„ฑ ํ›„ onCreate ๋˜๋Š” onUpgrade ์ฝœ๋ฐฑ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

     

      SQLiteOpenHelper ๋Š” ์ถ”์ƒํด๋ž˜์Šค์ด๋ฉฐ ํ…œํ”Œ๋ฆฟ ๋ฉ”์†Œ๋“œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด ๋†“์€ ๊ฒƒ์œผ๋กœ, ์œ„ ์ฝ”๋“œ์—์„œ ํด๋ž˜์Šค๋ฅผ ์ƒ์†ํ•ด์„œ ๋งŒ๋“  ํด๋ž˜์Šค๊ฐ€ TodoSQLHelper ์ž…๋‹ˆ๋‹ค.  onCreate, onUpgrade ๋“ฑ ์ฝœ๋ฐฑ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ํ•˜์—ฌ ์›ํ•˜๋Š” SQL๋ฌธ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„ ์˜ˆ์‹œ์—์„œ๋Š” ํ—ฌํผํด๋ž˜์Šค๋กœ DB๊ฐ€ ์ƒ์„ฑ ์‹œ TODO ํ…Œ์ด๋ธ”์ด ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            ...
    
            Thread {
                db = TodoSQLHelper.getInstance(this).writableDatabase
            }.start()
    
            ...
        }

      ๋˜ํ•œ DB ํ—ฌํผ๋Š” ์•ฑ ์ „์ฒด์—์„œ ๊ณตํ†ต์œผ๋กœ ํ•œ ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. getInstance ๋ฉ”์†Œ๋“œ์—์„œ Context๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„ ์‹ค์ œ ํด๋ž˜์Šค ์ƒ์„ฑ์€ applicationContext๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ ์ „์ฒด์—์„œ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋งŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ์‹ค์ œ DB ๋Š” SQLiteOpenHelper ํด๋ž˜์Šค์˜ getWritableDatabase, getReableDatabase ๋ฉ”์†Œ๋“œ์˜ ๋ฐ˜ํ™˜ ๊ฐ’์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ง ๊ทธ๋Œ€๋กœ Read-Only DB๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋˜๋Š”๋ฐ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  DB๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ์—…๊ทธ๋ ˆ์ด๋“œ ์ž‘์—…์€ ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ์•„๋‹Œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

     

     

    SQLiteOpenHelper  |  Android Developers

     

    developer.android.com

     

     

     

    ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ํ•˜๊ธฐ

     private fun bindViews() = with(binding) {
     	...   
        
            Thread {
               val values = ContentValues().apply {
                    put(COLUMN_TITLE, title)
                    put(COLUMN_DESCRIPTION, description)
                    put(COLUMN_DATE, date)
               }
               db?.insert(TABLE_NAME, null, values)
               updateRecyclerView()
            }.start()
            
            ...
     }

      SQLiteOpenHelper ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋”๋ผ๋„ ์œ„์—์„œ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ SQL๋ฌธ์„ ์ž‘์„ฑํ•œ ํ›„ execSQL ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, SQLiteDatabase ํด๋ž˜์Šค ๋‚ด์—๋Š” ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€๋ฅผ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์–ด ์†Œ๊ฐœํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ „์— ์ถ”๊ฐ€ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ContentValues์— Key์™€ Value์˜ ํ˜•ํƒœ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Key๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•  ํ…Œ์ด๋ธ”์˜ Colume ๋ช…์„ ์ ์€ ํ›„ ํ•ด๋‹น ์ปฌ๋Ÿผ์— ๋Œ€์ž…ํ•  ๋ฐ์ดํ„ฐ๋ฅผ putํ•˜๋„๋ก ํ•˜์—ฌ ์ƒ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.  ์ดํ›„ insert ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

     

    ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ

        private fun updateTodoData(position: Int) {
            ...
            Thread {
                val values = ContentValues().apply {
                    put(COLUMN_TITLE, title)
                    put(COLUMN_DESCRIPTION, description)
                    put(COLUMN_DATE, date)
                }
                val selection = "$_ID = ?"
                val selectionArg = arrayOf("$position")
                db?.update(TABLE_NAME, values, selection, selectionArg)
                updateRecyclerView()
            }.start()
    
            ...
        }

      ์—…๋ฐ์ดํŠธ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ execSQL ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๋˜์ง€๋งŒ ContentValues๋กœ ์—…๋ฐ์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ •์˜ํ•œ ํ›„ update ๋ฉ”์†Œ๋“œ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ selection์€ WHERE ๋ฌธ์— ํ•ด๋‹นํ•˜์—ฌ ๋ณ€๊ฒฝํ•ด์•ผํ•  ์ปฌ๋Ÿผ ๋ช…์„ ์„ ํƒํ•˜๊ฒŒ ๋˜๊ณ  selectionArg๋Š” ์„ ํƒ๋œ ์ปฌ๋Ÿผ ๋ช…์˜ ๊ฐ’์„ ์ž…๋ ฅํ•˜์—ฌ ํ•ด๋‹น ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. 

     

     

    ๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜๊ธฐ

        private fun updateRecyclerView() {
            if(db == null) return
    
            todoAdapter.clear()
            Thread {
                val list = mutableListOf<Todo>()
                val projection = arrayOf(COLUMN_TITLE, COLUMN_DESCRIPTION, COLUMN_DATE)
                val sortOrder = "$_ID ASC"
                val cursor = db?.query(
                    TABLE_NAME,
                    projection,
                    null,
                    null,
                    null,
                    null,
                    sortOrder
                )
                
                while (cursor.moveToNext()) {
                    list.add(
                        Todo(
                            cursor.getString(0),
                            cursor.getString(1),
                            cursor.getString(2)
                        )
                    )
                }
                todoAdapter.addAll(list)
                runOnUiThread {
                    todoAdapter.notifyDataSetChanged()
                }
            }.start()
        }

     ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ query ๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ Cursor ๊ตฌํ˜„์ฒด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. projection์€ ์กฐํšŒํ•˜๋ ค๊ณ  ํ•˜๋Š” ์ปฌ๋Ÿผ๋“ค์„ ๋ฐฐ์—ด๋กœ ์ •์˜ํ•˜๊ณ , ๊ทธ ์™ธ selection, selectionArg, groupBy, having, orderBy ๋“ฑ ๋‹ค์–‘ํ•˜๊ฒŒ ์กฐ๊ฑด์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” ์ „์ฒด์˜ ๊ฐ’๋“ค์„ ์กฐํšŒํ•˜๋ ค๊ณ  selection๊ณผ selectionArg ๊ฐ’์— null์„ ๋Œ€์ž…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.  ์—ญ์‹œ๋‚˜ ์ž์„ธํ•œ ๊ฑด ๊ณต์‹๋ฌธ์„œ์— ์ž˜ ๋‚˜์™€์žˆ์Šต๋‹ˆ๋‹ค.

     

     

     

    ๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๊ธฐ

        private fun deleteTodoData(id: Int) {
            Thread {
                val selection = "$_ID = ?"
                val selectionArg = arrayOf("$id")
                db?.delete(TABLE_NAME, selection, selectionArg)
                updateRecyclerView()
            }.start()
        }

      ๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๋Š” SQL๋ฌธ์ฒ˜๋Ÿผ WHERE ์กฐ๊ฑด์ ˆ๋งŒ ์„ค์ • ํ›„ delete ๋ฉ”์†Œ๋“œ๋งŒ ํ˜ธ์ถœํ•˜๋ฉด ์กฐ๊ฑด์— ๋งž๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ๋ฐ˜์‘ํ˜•

     

     

    ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ

    object TodoContract {
        object TodoEntry : BaseColumns {
            const val TABLE_NAME = "TODO"
            const val COLUMN_TITLE = "title"
            const val COLUMN_DESCRIPTION = "description"
            const val COLUMN_DATE = "date"
            const val COLUMN_TIME = "time"
    
            const val CREATE_QUERY = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
                    "$_ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    "$COLUMN_TITLE TEXT, " +
                    "$COLUMN_DESCRIPTION TEXT, " +
                    "$COLUMN_DATE TEXT)"
    
            const val CREATE_QUERY2 = "CREATE TABLE IF NOT EXISTS $TABLE_NAME (" +
                    "$_ID INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    "$COLUMN_TITLE TEXT, " +
                    "$COLUMN_DESCRIPTION TEXT, " +
                    "$COLUMN_DATE TEXT, " +
                    "$COLUMN_TIME TEXT"
    
            const val DROP_QUERY = "DROP TABLE IF EXISTS $TABLE_NAME"
        }
    }

      SQLiteOpenHelper ์ƒ์„ฑ์ž์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์— DB ๋ช…์ด ์žˆ์–ด ์ƒˆ๋กœ์šด DB๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋งˆ๋‹ค DB Helper ํด๋ž˜์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ํ•˜๋‚˜์˜ ํ—ฌํผํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์ง€๋งŒ, ์—ฌ๋Ÿฌ DB๋ฅผ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€๋Š” DB๋ฝ ๋ฌธ์ œ๊ฐ€ ์žˆ์„์ˆ˜๋„ ์žˆ๊ธฐ์— ํ•˜๋‚˜์˜ DB์—๋Š” ํ•˜๋‚˜์˜ Helper ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š”๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

     

    class TodoSQLHelper private constructor(context: Context
    ) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    
        override fun onCreate(db: SQLiteDatabase?) {
            db?.execSQL(CREATE_QUERY2)
        }
    
        override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
            Log.d("msg", "onUpgrade")
            when(oldVersion){
                1 -> db?.execSQL("ALTER TABLE $TABLE_NAME ADD COLUMN " +
                        "$COLUMN_TIME TEXT")
            }
        }
    
        companion object {
            const val DATABASE_VERSION = 2
            const val DATABASE_NAME = "Todo.db"
            var instance : TodoSQLHelper? = null
    
            fun getInstance(context: Context) : TodoSQLHelper{
                return if(instance == null) TodoSQLHelper(context.applicationContext) else instance!!
            }
        }
    }

      onCreate ์ฝœ๋ฐฑ๋ฉ”์†Œ๋“œ๊ฐ€ SQLiteOpenHelper ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ ํ›„ getWritableDatabase, getReableDatabase ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ์‹คํ–‰๋œ๋‹ค๊ณ  ํ•˜์˜€์Šต๋‹ˆ๋‹ค. onUpgrade ์ฝœ๋ฐฑ๋ฉ”์†Œ๋“œ๋Š” ์ด๋ฏธ DB๊ฐ€ ์ƒ์„ฑ๋œ ์‹œ์ ์—์„œ ์Šคํ‚ค๋งˆ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜, ํ…Œ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜๋Š” ๋“ฑ DB์˜ ๋ฒ„์ „์ด ์—…๋ฐ์ดํŠธ๊ฐ€ ๋  ๋•Œ ํ˜ธ์ถœ์ด ๋ฉ๋‹ˆ๋‹ค. 
      oldVersion์— ์ด์ „ ๋ฒ„์ „์ด๊ณ  newVersion์ด ์ƒˆ๋กœ์šด ๋ฒ„์ „์œผ๋กœ downgrade๋Š” ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ ์‹œ์—๋Š” ์ด์ „ ๋ฒ„์ „๋ณด๋‹ค๋Š” ๋†’์€ ์ˆซ์ž๋ฅผ ๋Œ€์ž…ํ•˜์—ฌ์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  onCreate, onUpgrade ๋‘˜ ๋ฉ”์†Œ๋“œ ์ค‘ ๋ฌด์กฐ๊ฑด ํ•˜๋‚˜๋งŒ ์‹คํ–‰์ด ๋ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ DB ๋ฒ„์ „์ด 4๋กœ ์ตœ์‹ ๋ฒ„์ „์ด ๋‚˜์™”์„ ๋•Œ ์ƒˆ๋กญ๊ฒŒ ์•ฑ์„ ์„ค์น˜ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” onCreate ๋ฉ”์†Œ๋“œ๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ์ด์ „์— DB๋ฅผ ์„ค์น˜ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” onUpgrade ๋ฉ”์†Œ๋“œ๋กœ ์Šคํ‚ค๋งˆ ๋˜๋Š” ํ…Œ์ด๋ธ”์„ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

     

    ์ตœ์‹  ๋ฒ„์ „์˜ ์•ฑ์„ ์„ค์น˜ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” onCreate ๋ฉ”์†Œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜์–ด CREATE_QUERY2๋กœ SQL๋ฅผ ์‹คํ–‰ํ•˜๊ณ , ์ด๋ฏธ ์ด์ „ ๋ฒ„์ „์˜ DB๊ฐ€ ์„ค์น˜๋œ ์‚ฌ๋žŒ์—๊ฒŒ๋Š” onUpgrade๋กœ ALTER TABLE ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์œ„์˜ ๊ทธ๋ฆผ์€ onUpgrade๋กœ time ์ปฌ๋Ÿผ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ…Œ์ด๋ธ”์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

     

     

     

     

     

    ์ฐธ๊ณ 

    • ์•ˆ๋“œ๋กœ์ด๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ Next Step p169 ~ p184
    • ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ p537 ~ p551

     

    ๋ฐ˜์‘ํ˜•
Designed by Tistory.