/* 본 게시물은 '참고자료' 의 내용을 토대로 작성되었습니다. */
참고 자료
[boostcourse - 앱 프로그래밍 - 데이터 베이스] : https://www.boostcourse.org/mo316/joinLectures/13174?isDesc=false
#SQL
사용자가 앱의 화면에서 데이터를 입력하면 서버에 보내어 저장할 수도 있고 단말에 저장할 수 있다. 저장소로 많이 사용되는 것이 데이터베이스이며 실무에서는 관계형 데이터베이스를 많이 볼 수 있다. 웹서버쪽에서 데이터베이스를 사용하는 것처럼 단말에서도 데이터베이스를 사용할 수 있다면 좀 더 쉽게 데이터를 저장하고 조회할 수 있는데 안드로이드에서는 SQLite라는 관계형 데이터베이스를 제공한다.
1. 데이터 베이스
데이터베이스는 저장소이며 관계형 데이터베이스는 여러 가지 유형의 데이터베이스 중 하나이다. 보통 실무에서는 MySQL, Oracle과 같은 관계형 데이터베이스를 많이 볼 수 있다.
관계형 데이터베이스는 저장소 안에 여러 개의 테이블을 만들 수 있으며 테이블은 엑셀의 Sheet 처럼 생각하면 쉽습니다. 가로, 세로 격자 모양으로 데이터를 저장해둘 수 있는 공간이라고 할 수 있다. 다만 각각의 칼럼이 갖는 자료형을 명시적으로 표시해야 하기 때문에 테이블을 먼저 정의한 후에 데이터를 추가할 수 있다.
테이블은 엑셀의 Sheet처럼 생각할 수 있지만 사용하는 용어는 약간 다를 수 있다. 왜냐하면 엑셀과는 달리 데이터베이스는 저장된 데이터를 빠르게 조회하는 데 목적이 있기 때문에 각각의 셀에 들어가는 데이터를 어떻게 찾아낼지에 대한 연구가 오랫동안 진행되어 왔기 때문이다. 그래서 생소한 용어들도 있는데, 대표적으로 테이블을 릴레이션(Relation)이라고 부를 때가 있다. 한 줄로 추가되는 데이터는 레코드(Record) 또는 투플(Tuple)이라고 부른다. 각각의 세로 줄은 칼럼(Column) 또는 애트리뷰트(Attribute)라고 부른다. 각각의 셀을 필드(Field)라고 부르지만 세로 줄을 필드라고 부르는 경우도 있어서 약간 혼동될 수 있다. 따라서 칼럼이라고 기억해두는 것이 좋다. 아래 그림을 보면 이해가 쉽게 될 것이다.
2. SQL 문으로 SQLite 사용해보기
SQLite을 사용하기 앞서 기본적으로 SQL문을 사용하는 방법을 알아야 한다.(Helper에서 제공하는 함수를 이용하는 방법도 있지만 이번에는 SQL문을 이용하는 방법에 대해서만 소개할 것이다.) SQL 문에서 가장 자주 쓰이는 CRUD는 옆에 링크를 통해 들어가서 보면 된다.
이미 플레이스토어에 앱을 배포하여 많은 유저가 사용하고 있는 경우 데이터베이스의 테이블 구조가 변경되어야 한다면 어떻게 해야 할까?
새로 앱을 설치하는 단말의 경우에는 새로 테이블을 만들면 되지만 이미 사용하고 있는 상태에서는 테이블을 이미 만들어두었기 때문에 테이블을 삭제하고 다시 만들도록 하기는 어렵다. 이 때 사용할 수 있는 것이 헬퍼 클래스이다.
헬퍼 클래스는 데이터베이스 버전을 지정하여 데이터베이스를 업그레이드할 때 알 수 있도록 한다. 그리고 현재 단말에 설치된 앱의 테이블이 새로 만들어지는지 아니면 업그레이드되어야 하는지를 알려준다.
헬퍼 클래스는 SQLiteOpenHelper를 상속받아 만든다. SQLiteOpenHelper의 생성자로는 context, name, factory, version을 주입해야한다.
1. DBHelper
class DatabaseHelper(
context: Context,
name: String,
factory: SQLiteDatabase.CursorFactory,
version: Int) : SQLiteOpenHelper(context, name, factory, version){
//기존에 사용자가 데이터베이스를 사용안했을 때
override fun onCreate(db: SQLiteDatabase?) {
printLog("onCreate() 호출")
db?.let{
val tableName = "customer"
val sql = "CREATE TABLE IF NOT EXISTS $tableName (_id integer PRIMARY KEY autoincrement, name text, age int, mobile text)"
it.execSQL(sql)
printLog("$tableName 테이블 생성 완료")
} ?: printLog("데이터 베이스를 찾을 수 없음")
}
// 이미 같은 이름의 db가 있을 경우
override fun onOpen(db: SQLiteDatabase?) {
printLog("Helper onOpen 함수 실행")
}
//기존에 사용자가 데이터베이스를 사용할 때 사용자의 테이블에서 수정 추가 삭제를 할 수 있다.
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
printLog("onUpgrade 호출 + $oldVersion + $newVersion")
if (newVersion > 1){
db?.let {
val tableName = "customer"
it.execSQL("DROP TABLE IF EXISTS $tableName")
}
}
}
}
데이터베이스를 생성하는 방법으로 dbHelper을 사용하는 방법이 있다.
class SqliteActivity : AppCompatActivity() {
private var db: SQLiteDatabase? = null
override fun onCreate(savedInstanceState: Bundle?) {
DatabaseHelper(this,databaseName,null,1).let{
db = it.writableDatabase
}
}
}
databaseName은 형식이 *.db로 해야한다. db 변수로 database에 접근이 가능하다.
3. 테이블 생성
테이블을 생성하기 전에 데이터베이스를 생성/오픈이 되있어야 한다. 테이블 이름을 받아 이름이 중복되지 않는다면 새로운 테이블을 생성한다.
fun createTable(tableName: String){
db?.let{
val sql = "CREATE TABLE IF NOT EXISTS $tableName (_id integer PRIMARY KEY autoincrement, name text, age int, mobile text)"
it.execSQL(sql)
printLog("$tableName 테이블 생성 완료")
} ?: printLog("데이터 베이스를 찾을 수 없음")
}
4. 데이터 추가
fun insertData(customerName: String, customerAge: Int, customerMobile: String){
db?.let{
val sql = "INSERT INTO customer(name, age, mobile) VALUES('${customerName}','${customerAge}','${customerMobile}')"
it.execSQL(sql)
printLog("데이터 삽입")
} ?: printLog("데이터 베이스를 찾을 수 없음")
}
5. 데이터 조회
fun findData(name: String, age: Int, mobile: String){
val tableName = "customer"
val name = if (name != "") "name = '$name'" else ""
val age = when (name) {
"" -> if (age != -1) "age = $age" else ""
else -> if (age != -1) " AND age = $age" else ""
}
val mobile = when {
//이름, 나이 중 하나라도 입력했을 때
name != "" || age != "" -> if (mobile != "") " AND mobile = $mobile" else ""
//이름, 나이 둘 다 입력했을 안했을 때
else -> if (mobile != "") "mobile = $mobile" else ""
}
val sql = "SELECT name, age, mobile FROM $tableName WHERE ${name}${age}${mobile}"
db?.let{
val cursor = it.rawQuery(sql, null)
printLog("조회된 데이터 개수 : ${cursor.count}")
while(cursor.moveToNext()){
// columnIndex 로 데이터를 찾을 수 있다.
val name = cursor.getString(0)
val age = cursor.getInt(1)
val mobile = cursor.getString(2)
printLog("이름 : $name 나이 : $age 전화번호 : $mobile")
}
} ?: printLog("데이터 베이스를 찾을 수 없음")
}
fun findAllData(){
val tableName = "customer"
val sql = "SELECT name, age, mobile FROM $tableName" // column 에 * 로 바꿔주면 _id, name, age, mobile
db?.let{
val cursor = it.rawQuery(sql, null)
printLog("조회된 모든 데이터 개수 : ${cursor.count}")
while(cursor.moveToNext()){
// columnIndex 로 데이터를 찾을 수 있다.
val name = cursor.getString(0)
val age = cursor.getInt(1)
val mobile = cursor.getString(2)
printLog("이름 : $name 나이 : $age 전화번호 : $mobile")
}
} ?: printLog("데이터 베이스를 찾을 수 없음")
}
fun deleteAllData(){
val tableName = "customer"
val sql = "DELETE FROM $tableName"
db?.let{
it.execSQL(sql)
printLog("모든 데이터 삭제")
} ?: printLog("데이터베이스를 찾을 수 없음")
}
실행 결과
![](https://blog.kakaocdn.net/dn/DFou5/btrbLfrnEVV/TPCQu2Bjwd9P2H4KKKOqA0/img.gif)