[Android] SQLite Database
android app 내부에서 프라이빗하게 사용할 수 있는 간단한 SQLite에 대해 알아보자.
파일 형태로 입출력할 경우 검색을 위해서는 먼저 모든 file을 스캔해야 하고
검색, 수정, 삭제 등 모든 기능을 직접 구현해야 해서 매우 귀찮기 때문에,
용량이 비교적 큰 데이터를 다룰 때는 raw file과 직접 IO하는 것 보다 DB를 사용하는 편이 더 좋다.
DBHelper
가장 먼저 Database와 통신하기 위한 클래스를 작성한다.
class MyDBHelper(
val context: Context,
) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
companion object {
val DB_NAME = "mydb.db"
val DB_VERSION = 1
val TABLE_NAME = "products"
val PID = "pid"
val PNAME = "pname"
val PQUANTITY = "pquantity"
}
override fun onCreate(db: SQLiteDatabase?) {
val create_table = "CREATE TABLE IF NOT EXISTS $TABLE_NAME(" +
"$PID INTEGER PRIMARY KEY AUTOINCREMENT," +
"$PNAME text," +
"$PQUANTITY INTEGER);"
db!!.execSQL(create_table)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
val drop_table = "DROP TABLE IF EXISTS $TABLE_NAME"
db!!.execSQL(drop_table)
onCreate(db)
}
}
DB_NAME은 데이터베이스의 이름
DB_VERSION은 데이터베이스의 버전 (앱 버전 업데이트 시 데이터베이스의 구조가 변경되면 이 값을 바꾸어 테이블을 다시 생성할 수 있다)
TABLE_NAME 데이터베이스의 테이블 이름
PID 데이터베이스의 TABLE_NAME에 해당하는 테이블의 attribute 이름
PNAME 데이터베이스의 TABLE_NAME에 해당하는 테이블의 attribute 이름
PQUANTITY 데이터베이스의 TABLE_NAME에 해당하는 테이블의 attribute 이름
onCreate는 데이터베이스가 처음 생성될 때 호출된다.
onUpgrade는 DB_VERSION값이 현재 DB 버전과 다를 때 호출되며,
함수 내부에서 DB를 삭제하고 onCreate를 호출함으로써 다시 데이터베이스를 생성한다.
DBHelper에서 다룰 데이터 정보는 다음과 같다.
data class Product(var pId:Int, var pName: String, var pQuantity:Int)
위 3개 멤버 변수를 db에 저장하고, CRUD해 볼 것이다.
Read
class MyDBHelper(
val context: Context,
) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
companion object {
val DB_NAME = "mydb.db"
val DB_VERSION = 1
val TABLE_NAME = "products"
val PID = "pid"
val PNAME = "pname"
val PQUANTITY = "pquantity"
}
//...
fun getAllProducts(): ArrayList<Product> {
val list: ArrayList<Product> = ArrayList<Product>()
val strSql = "SELECT * FROM $TABLE_NAME"
val db = readableDatabase
//cursor hold result of sql execution
val cursor = db.rawQuery(strSql, null)
//column count
val attrCnt = cursor.columnCount
//데이터 갯수(row count)가 0일 경우 종료
if (cursor.count == 0)
return list
else{
cursor.moveToNext()
do {
var pid = 0
var pName = ""
var pQuantity = 0
for (i in 0 until attrCnt) {
when (i) {
0 -> pid = cursor.getString(i).toInt()
1 -> pName = cursor.getString(i)
2 -> pQuantity = cursor.getString(i).toInt()
}
}
//build Product and add it to list
list.add(Product(pid, pName, pQuantity))
} while (cursor.moveToNext())
}
//free resources
cursor.close()
db.close()
return list
}
//...
}
데이터베이스에 저장된 모든 Product 정보를 뽑아오는 코드이다.
보다시피 간단한 SQL을 사용하고 있는데, 아예 문외한이라면 응용하기는 힘들테니
간단하게 SQL을 공부하고 오는 것을 추천한다.
https://satisfactoryplace.tistory.com/215?category=907961
Create
fun insertProduct(product: Product): Boolean {
val values = ContentValues()
values.put(PNAME, product.pName)
values.put(PQUANTITY, product.pQuantity)
val db = writableDatabase
return if (db.insert(TABLE_NAME, null, values) > 0) {
db.close()
true
} else {
db.close()
false
}
}
ContentValues에 값을 넣고, db.insert할 때 그 인스턴스를 입력하는 식으로 구현한다.
Update
fun updateProduct(product: Product): Boolean {
val pid = product.pId
val sql = "SELECT * FROM $TABLE_NAME WHERE $PID='$pid'"
val db = readableDatabase
val cursor = db.rawQuery(sql, null)
val flag = cursor.count != 0
if (flag) {
cursor.moveToFirst()
val values = ContentValues()
values.put(PNAME, product.pName)
values.put(PQUANTITY, product.pQuantity)
db.update(TABLE_NAME, values, "$PID=?", arrayOf(pid.toString()))
}
cursor.close()
db.close()
return flag
}
Update는 SQL로 UPDATE TABLE SET [values...] WHERE [condition...] 하지 않고
SELECT로 먼저 cursor를 얻은 뒤 db.update해서 수정한다.
Delete
fun deleteProduct(pid: String): Boolean {
val sql = "SELECT * FROM $TABLE_NAME WHERE $PID='$pid'"
val db = readableDatabase
val cursor = db.rawQuery(sql, null)
val flag = cursor.count != 0
if (flag) {
cursor.moveToFirst()
db.delete(TABLE_NAME, "$PID=?", arrayOf(pid))
}
cursor.close()
db.close()
return flag
}
Delete도 Update와 유사한 방식으로 사용한다.
Use in activity
class ProductActivity : AppCompatActivity() {
lateinit var binding:ActivityProductBinding
lateinit var myDBHelper: MyDBHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding= ActivityProductBinding.inflate(layoutInflater)
setContentView(binding.root)
myDBHelper= MyDBHelper(this)
}
//...
}
단순히 MyDBHelper의 생성자에서 Context를 전달하면 된다.
onCreate 이후에는 myDBHelper.getAllProducts() 처럼 사용해 자유자재로 사용할 수 있게 되었다.