[Android/Flutter 교육] 34일차

MSU·2024년 2월 15일

Android-Flutter

목록 보기
36/85
post-thumbnail

SQLite

별도의 고유값을 가지는 컬럼이 없을경우에는 idx컬럼을 설정하면 되지만 idx컬럼 외에 학생번호와 같은 고유값을 가지는 컬럼이 있을 경우에는 idx컬럼이 없어도 된다. -> db 성능 최적화 이유

데이터 클래스를 먼저 정의하고 그걸 기반으로 테이블을 정의한다

  • 데이터 베이스 : 데이터들을 저장하고 관리하는 데이터 집합
  • SQLite : 데이터 베이스를 관리하는 관계형 데이터 베이스 시스템(RDBMS)의 한 종류
  • 테이블 : 데이터베이스 내에서 데이터를 묶어서 관리하는 요소
  • 컬럼 : 테이블 내의 데이터 항목(컬럼, 열)
  • 로우 : 테이블에 저장되어 있는 개체 하나에 대한 컬럼의 묶음(로우, 행)

SQLiteOpenHelper 생성자의 매개변수

class DBHelper(context: Context, dbName:String, version:Int) : SQLiteOpenHelper(context, dbName, null, version) {
  • 첫 번째 : 컨텍스트
  • 두 번째 : 데이터 베이스 파일의 이름. 이 이름으로 만들어진 데이터 베이스 파일이 없으면 OS가 파일을 만들어주고 OS가 onCreate 메서드를 호출한다.
  • 세 번째 : null에 대한 처리를 어떻게 할 것인가. 그냥 null로 셋팅해준다.
  • 네 번째 : 데이터 베이스 파일의 버전. 애플리케이션 버전과 무관하며 아무 숫자나 지정해줘도 된다.
    애플리케이션을 서비스 하는 중에 테이블의 개수나 구조를 변경해야 하는 경우
    이전의 숫자보다 높은 숫자로 지정해주면 예전에 저장한 애플리케이션들은 onUpgrade를 호출해준다.
class DBHelper(context: Context, dbName:String, version:Int) : SQLiteOpenHelper(context, dbName, null, version) {

    companion object{
        val databaseVersion = 2
    }

    // 사용하고자 하는 데이터 베이스 파일이 없을 경우 파일을 만들어주고 호출되는 메서드
    // 테이블을 생성하는 작업을 수행한다.

    // 테이블을 만드는 쿼리문
    // create table 테이블이름 (컬럼이름, 자료형 제약조건, 컬럼이름 자료형 제약조건, ...)
    // 자료형 : 정수 - integer, 문자열 - text, 실수 - real, 날짜 - date
    // 제약조건 : 저장할 수 있는 값에 대한 조건
    // primary key : null을 허용하지 않고 중복된 값을 허용하지 않는다.
    // 각 행들을 개별적으로 구분하기위한 값을 저장하기 위해 사용한다.
    // autoincrement : 컬럼에 저장할 값을 지정하지 않으면 1부터 1씩 증가되는 값이 자동으로 저장된다.
    // not null : null을 허용하지 않는다. 개발자가 무조건 값을 정해줘야 한다.

    override fun onCreate(db: SQLiteDatabase?) {
        val sql = """create table TestTable
            (idx integer primary key autoincrement,
             data1 integer not null,
             data2 real not null,
             data3 text not null,
             data4 date not null)
        """.trimIndent()

        // 쿼리문을 실행한다.
        db?.execSQL(sql)
    }

    // 데이터 베이스 파일의 버전이 올라갔을 경우 호출되는 메서드
    // 테이블의 구조를 최종 형태로 변경하는 작업을 수행한다.
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        Log.d("test1234", "onUpgrade : $oldVersion -> $newVersion")
    }

}

SQLiteOpenHelper 객체 생성

		val dbHelper = DBHelper(this, "TEST.db", DBHelper.databaseVersion)
        // 데이터 베이스에 접속
        // 이때 데이터 베이스 파일을 찾게 된다. 파일이 있으면 데이터 베이스 파일을 열고
        // 없으면 파일을 생성하고 데이터 베이스 파일을 열고 SQLiteOpenHelper 에 있는 onCreate 메서드를 호출한다.
        // 만약 기존 데이터 베이스 파일 버전보다 더 높은 버전으로 설정하면 onUpgrade 가 호출된다.
        val sqLiteDatabase = dbHelper.writableDatabase

        // 데이터 베이스를 닫아준다.
        sqLiteDatabase.close()

쿼리문을 이용한 코드

        activityMainBinding.apply {
            // 저장(쿼리문)
            buttonInsert1.setOnClickListener {
                // 쿼리문
                // autoincrement를 설정한 컬럼은 제외한다.
                // insert into 테이블명 (컬럼명, 컬럼명, 컬럼명, ...) values(값, 값, 값, ...)
                // 값이 들어가는 부분은 ?로 설정해준다.
                val sql = """insert into TestTable
                    (data1, data2, data3, data4)
                    values(?, ?, ?, ?)
                """.trimIndent()

                // 현재 시간을 구해 년-월-일 양식의 문자열로 만들어준다.
                val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
                val now = sdf.format(Date())

                // ?에 맵핑될 값
                // ?에 맵핑될 값을 순서대로 작성해줘야 한다.
                val arg1 = arrayOf(100, 11.11, "문자열1",now)
                val arg2 = arrayOf(200, 22.22, "문자열2",now)

                // 데이터 베이스를 사용한다.
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)

                // 쿼리문을 실행한다.
                // 첫 번째 - 쿼리문
                // 두 번째 - ?에 바인딩될 값이 담긴 배열
                dbHelper.writableDatabase.execSQL(sql, arg1)
                dbHelper.writableDatabase.execSQL(sql, arg2)

                // 데이터 베이스를 닫아준다.
                dbHelper.close()

                textViewResult.text = "데이터가 저장되었습니다 (쿼리문)"
            }

            // 데이터 가져오기(쿼리문)
            buttonSelect1.setOnClickListener {
                // 쿼리문
                // select (컬럼명, 컬럼명, ...) 또는 * from 테이블명 where 조건식
                val sql = """select idx, data1, data2, data3, data4
                    from TestTable
                """.trimIndent()

                // 쿼리 실행
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)
                // rawQuery() 쿼리문을 통해 가져온 객체를 받아옴
                // 첫 번째 : 쿼리문
                // 두 번째 : ? 부분이 있을 경우 ? 부분에 맵핑될 값 배열, 없으면 null
                val cursor = dbHelper.writableDatabase.rawQuery(sql,null)

                showResult(cursor)

                dbHelper.close()

            }

            // 수정(쿼리)
            buttonUpdate1.setOnClickListener {
                // 쿼리문
                // Update 테이블명
                // set 컬럼=값, 컬럼=값, 컬럼=값 ...
                // where 조건절
                val sql = """update TestTable 
                    set data1=?, data2=?
                    where idx=?
                """.trimIndent()

                // ?에 들어갈 값
                val args1 = arrayOf(1000, 111.111, 1)
                val args2 = arrayOf(2000, 222.222, 2)

                // 쿼리 실행
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)
                dbHelper.writableDatabase.execSQL(sql,args1)
                dbHelper.writableDatabase.execSQL(sql,args2)
                dbHelper.close()

                textViewResult.text = "수정되었습니다 (쿼리)"
            }
            
            // 삭제 (쿼리)
            buttonDelete1.setOnClickListener { 
                // 쿼리문
                // delete from 테이블명
                // where 조건절
                val sql = """delete from TestTable
                    where idx = ?
                """.trimIndent()
                // ?에 들어갈 값
                val args = arrayOf(1)

                // 쿼리 실행
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)
                dbHelper.writableDatabase.execSQL(sql, args)
                dbHelper.close()

                textViewResult.text = "삭제되었습니다 (쿼리)"
            }

        }

라이브러리를 이용한 코드

        activityMainBinding.apply {

            // 저장(라이브러리)
            buttonInsert2.setOnClickListener {
                // 현재 시간을 구해 년-월-일 양식의 문자열로 만들어준다.
                val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
                val now = sdf.format(Date())

                // 데이터를 담을 객체
                val cv1 = ContentValues()
                // ContentValues는 이름을 통해 데이터를 보관하는 객체
                // 데이터를 저장할 때 사용하는 이름을 컬럼의 이름으로 지정해주면
                // 저장처리가 이루어진다.
                cv1.put("data1", 300)
                cv1.put("data2", 33.33)
                cv1.put("data3", "문자열3")
                cv1.put("data4", now)
                // autoincrement가 있는 컬럼은 지정해주지 않아도 된다.

                val cv2 = ContentValues()
                cv2.put("data1", 400)
                cv2.put("data2", 44.44)
                cv2.put("data3", "문자열4")
                cv2.put("data4", now)

                // 저장한다.
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)

                // 첫 번째 : 데이터를 저장할 테이블의 이름
                // 두 번째 : null 값을 어떻게 처리할 것인가.. 그냥 null 지정해주세요....
                // 세 번째 : 저장할 데이터를 가지고 있는 ContentValues
                dbHelper.writableDatabase.insert("TestTable", null, cv1)
                dbHelper.writableDatabase.insert("TestTable", null, cv2)

                dbHelper.close()

                textViewResult.text = "데이터가 저장되었습니다 (라이브러리)"
            }

            // 데이터 가져오기(라이브러리)
            buttonSelect2.setOnClickListener {
                // 가져올 컬럼 목록
                // null을 담으면 select * 과 같이 모든 컬럼을 불러올 수 있음
                val cols = arrayOf("idx","data1","data2","data3","data4")
                // 테이블 명
                val tableName = "TestTable"

                // 데이터를 가져온다.
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)

                // 첫 번째 : 테이블명
                // 두 번째 : 가지고 오고자 하는 컬럼 목록. null을 넣어주면 모두 가져온다.
                // 세 번째 : 특정 행을 선택하기 위한 조건절
                // 네 번째 : 조건절의 ? 에 들어갈 값 배열
                // 다섯 번째 : Group by 의 기준 컬럼
                // 여섯 번째 : Having 절에 들어갈 조건절
                // 일곱 번째 : Having 절의 ?에 들어갈 값 배열
                val cursor = dbHelper.writableDatabase.query(tableName, cols, null, null, null, null, null)

                showResult(cursor)

                dbHelper.close()
            }

            // 수정 (라이브러리)
            buttonUpdate2.setOnClickListener {
                // ContentValues에 컬럼명과 값을 담아준다
                val cv1 = ContentValues()
                cv1.put("data1", 3000)
                cv1.put("data2", 333.333)

                val cv2 = ContentValues()
                cv2.put("data1", 4000)
                cv2.put("data2", 444.444)

                // 조건절
                val condition = "idx = ?"
                // 조건절 ? 에 바인딩 될 값 배열
                // 모든 값들은 반드시 문자열로 되어 있어야 한다.
                val arg1 = arrayOf("3")
                val arg2 = arrayOf("4")

                // 쿼리 실행
                val dbHelper = DBHelper(this@MainActivity, "Test.db", DBHelper.databaseVersion)
                dbHelper.writableDatabase.update("TestTable", cv1, condition, arg1)
                dbHelper.writableDatabase.update("TestTable", cv2, condition, arg2)
                dbHelper.close()

                textViewResult.text = "수정되었습니다 (라이브러리)"
            }

            // 삭제 (라이브러리)
            buttonDelete2.setOnClickListener {
                // 테이블명
                val tableName = "TestTable"
                // 조건절
                val condition = "idx = ?"
                // 조건절 ?에 들어갈 값. 문자열로 해야 한다.
                val args = arrayOf("2")

                val dbHelper = DBHelper(this@MainActivity,"Test.db",DBHelper.databaseVersion)
                dbHelper.writableDatabase.delete(tableName,condition,args)
                dbHelper.close()

                textViewResult.text = "삭제되었습니다 (라이브러리)"
            }

        }

EX12

Model 클래스 : 데이터를 담아두는 클래스들
학생 데이터 클래스
학생번호
이름
나이
국어점수
영어점수
수학점수

data class StudentModel(
	var idx:Int, 
    var name:String, 
    var grade:Int, 
    var kor:Int, 
    var eng:Int, 
    var math:Int
)

Dao 클래스 : 데이터베이스에 관련된 작업을 담아두는 클래스들
Database access object

class StudentDao {

    companion object{

        // select one
        fun selectOneStudent(context: Context, idx:Int) : StudentModel{
            // 쿼리
            val sql = """select idx, name, grade, kor, eng, math
                | from StudentTable
                | where idx = ?
            """.trimMargin()

            // ?에 들어갈 값
            val args = arrayOf("$idx")

            // 쿼리 실행
            val dbHelper = DBHelper(context)
            val cursor = dbHelper.writableDatabase.rawQuery(sql, args)

            // 데이터를 가져온다.
            cursor.moveToNext()

            val idx1 = cursor.getColumnIndex("idx")
            val idx2 = cursor.getColumnIndex("name")
            val idx3 = cursor.getColumnIndex("grade")
            val idx4 = cursor.getColumnIndex("kor")
            val idx5 = cursor.getColumnIndex("eng")
            val idx6 = cursor.getColumnIndex("math")

            val idx = cursor.getInt(idx1)
            val name = cursor.getString(idx2)
            val grade = cursor.getInt(idx3)
            val kor = cursor.getInt(idx4)
            val eng = cursor.getInt(idx5)
            val math = cursor.getInt(idx6)

            // 객체에 데이터를 담는다.
            val studentModel = StudentModel(idx,name,grade,kor,eng,math)

            // 데이터 베이스를 닫아준다.
            dbHelper.close()

            return studentModel
        }

        // select all
        fun selectAllStudent(context:Context) : MutableList<StudentModel>{
            // 쿼리문
            // order by : 정렬
            // order by 기준컬럼 정렬방식
            // 정렬방식 : asc나 생략 - 오름차순, desc - 내림차순
            val sql = """select idx, name, grade, kor, eng, math
                from StudentTable
                order by idx desc
            """.trimIndent()

            // 쿼리 실행
            val dbHelper = DBHelper(context)
            val cursor = dbHelper.writableDatabase.rawQuery(sql,null)

            // 데이터를 담을 리스트
            val studentList = mutableListOf<StudentModel>()

            // 데이터를 가져온다.
            while (cursor.moveToNext()){
                val idx1 = cursor.getColumnIndex("idx")
                val idx2 = cursor.getColumnIndex("name")
                val idx3 = cursor.getColumnIndex("grade")
                val idx4 = cursor.getColumnIndex("kor")
                val idx5 = cursor.getColumnIndex("eng")
                val idx6 = cursor.getColumnIndex("math")

                val idx = cursor.getInt(idx1)
                val name = cursor.getString(idx2)
                val grade = cursor.getInt(idx3)
                val kor = cursor.getInt(idx4)
                val eng = cursor.getInt(idx5)
                val math = cursor.getInt(idx6)

                // 객체에 데이터를 담는다.
                val studentModel = StudentModel(idx,name,grade,kor,eng,math)

                // 객체를 리스트에 담는다.
                studentList.add(studentModel)
            }

            // 데이터 베이스를 닫아준다.
            dbHelper.close()

            return studentList
        }

        // insert
        fun insertStudent(context: Context, studentModel: StudentModel){
            // 쿼리문
            val sql = """insert into StudentTable
                (name, grade, kor, eng, math)
                values(?, ?, ?, ?, ?)
            """.trimIndent()

            // ?에 바인딩될 값
            val args = arrayOf(
                studentModel.name,
                studentModel.grade,
                studentModel.kor,
                studentModel.eng,
                studentModel.math
            )

            // 쿼리 실행
            val dbHelper = DBHelper(context)
            dbHelper.writableDatabase.execSQL(sql, args)
            dbHelper.close()
        }

        // update
        fun updateStudent(context: Context, studentModel: StudentModel){
            // 쿼리문
            val sql = """update StudentTable
                set name=?, grade=?, kor=?, eng=?, math=?
                where idx=?
            """.trimIndent()

            // ?에 바인딩될 값
            val args = arrayOf(
                studentModel.name,
                studentModel.grade,
                studentModel.kor,
                studentModel.eng,
                studentModel.math,
                studentModel.idx,
            )

            // 쿼리 실행
            val dbHelper = DBHelper(context)
            dbHelper.writableDatabase.execSQL(sql, args)
            dbHelper.close()
        }

        // delete
        fun deleteStudent(context: Context, idx: Int){
            // 쿼리문
            val sql = """delete from StudentTable
                where idx = ?
            """.trimIndent()

            // ?에 바인딩될 값
            val args = arrayOf(idx)

            // 쿼리 실행
            val dbHelper = DBHelper(context)
            dbHelper.writableDatabase.execSQL(sql, args)
            dbHelper.close()
        }
    }

}

EX13

학생 정보를 담을 테이블을 DBHelper의 onCreate에 create문 작성
StudentModel을 정의하고 그걸 토대로 StudentDAO 작성

MainActivity에는 DB에서 불러온 학생 개체를 담을 리스트 studentList 를 만들어줌

MainFragment에서는 매번 보여질때마다 전체 학생정보를 불러올 수 있도록 initView메서드에서
전체 select 메서드를 호출하고 studentList에 담아주기

학생 정보 입력 Fragment를 보여줄 때 리사이클러뷰의 어댑터 포지션이 아니라 선택한 항목 학생의 idx를 전달해서 해당 idx에 해당하는 학생 데이터를 DB에서 불러오기

DAO클래스에는 총 5개의 메서드를 작성
1. 하나의 학생 데이터를 db 테이블에서 select하는 메서드
2. 전체 학생 데이터를 db 테이블에서 select하는 메서드
3. 학생 데이터를 db 테이블에 insert하는 메서드
4. 학생 데이터를 db 테이블에서 update하는 메서드
5. 학생 데이터를 db 테이블에서 delete하는 메서드

update할때는 update할 특정 값만 처리하는것보다 모든 값을 한번에 update처리해줄 수 있음




※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스 
profile
안드로이드공부

0개의 댓글