안드로이드에서 사용하는 내장 데이터 베이스로 표준 SQL문을 사용하는 관계형 데이터 베이스이다.
MySQL 과 유사한 문법을 사용하고 있으며 일반적인 관계형 데이터 베이스가 가지고 있는 기능을 가지고 있다.
MPC -> MVC -> ... -> MVVM
MVC :
Model
View
MVVM : Model, View+ViewModel, Controll
SQLite 데이터베이스는 임베디드형 데이터베이스로써 데이터베이스를 사용하는 애플리케이션에 셋팅되는 데이터 베이스이다.
안드로이드는 안드로이드 OS에 내장되어 있으며 개발자가 만드는 애플리케이션은 안드로이드 OS에게 쿼리문을 전달하고 안드로이드 OS가 직접 데이터 베이스에 대한 처리를 하게된다.
안드로이드에서의 SQLite 데이터베이스 사용은 쿼리문을 이용하는 방법과 제공되는 클래스를 이용하는 방법 두 가지가 있다.
쿼리문을 이용하는 방식은 일반적인 SQL문을 사용하며 MySQL과 유사한 문법을 사용한다.
클래스를 이용하는 방법은 개발자가 정해줘야 하는 몇 가지 정보를 제공하면 쿼리문이 생성되고 실행되는 구조이다.
안드로이드에서 SQLite 데이터 베이스를 사용하려면 SQLiteOpenHelper를 상속받은 클래스를 만들어야 한다.
이 클래스는 사용할 데이터베이스의 이름을 설정하는 것 뿐만 아니라 다음 기능들을 제공하고 있다.
지정된 데이터베이스 파일을 사용하려고 할 때 파일이 없으면 파일을 만들고 onCreate 메서드를 호출한다. 이 메서드에서는 테이블을 만드는 쿼리를 실행해주면 된다.
애플리케이션을 서비스하다가 데이터 베이스 구조를 변경하려면 데이터베이스의 버전을 변경하면 된다. 버전을 변경하면 onUpgrade 메서드가 호출되고 여기에서 테이블을 새로운 구조로 변경해주는 작업을 해주면 된다.
// Test.db : 사용할 데이터 베이스 파일의 이름(파일명, 확장자 상관없음)
// null : NullFactory, Null에 대한 처리르 어떻게 할 것인가
// 1 : 버전
class DBHelper(context:Context) : SQLiteOpenHelper(context, "Test.db", null, 1) {
// 데이터 베이스 파일이 없으면 만들고 이 메서드를 호출해준다.
// 테이블을 생성하는 작업을 수행하면 된다.
override fun onCreate(db: SQLiteDatabase?) {
// 테이블의 구조를 정의한다.
// create table 테이블이름
// (컬럼이름 자료형 제약조건 ... )
// 자료형 : 정수 - integer, 문자열 - text, 실수 - real, 날짜 - date
// 제약조건 : 저장할 수 있는 값에 대한 조건
// primary key : null을 허용하지 않고 중복된 값을 허용하지 않는다.
// 각 행들을 개별적으로 구분할 수 있는 값을 저장하기 위해 사용한다.
// autoincrement : 컬럼에 저장할 값을 지정하지 않으면 1부터 1씩 증가되는 값이 자동으로 저장한다.
// not null : null 값을 허용하지 않는다. 즉, 개발자가 무조건 값을 정해줘야 한다.
val sql = """create table TestTable
(idx integer primary key autoincrement,
textData text not null,
intData integer not null,
doubleData real not null,
dateData date not null)
""".trimIndent()
// 쿼리문을 수행한다.
db?.execSQL(sql)
}
// 사용하는 데이터 베이스 파일의 버전이 변경되어 있을 때 호출되는 메서드
// 부모의 생성자의 마지막에 넣어준 버전 번호가 데이터 베이스 파일에 기록 버전 보다 높을 때 호출된다.
// 과거에 만들어진 테이블을 현재의 구조가 될 수 있도록 테이블을 수정하는 작업을 하면 된다.
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
// TODO("Not yet implemented")
}
}
// Create : 저장
fun insertData(context:Context, data:TestClass){
// autoincrement가 있는 컬럼은 제외하고 나머지만 지정한다.
val sql = """insert into TestTable
| (textData, intData, doubleData, dateData)
| values(?,?,?,?)
""".trimMargin()
// ? 에 설정할 값을 배열에 담아준다.
val arg1 = arrayOf(
data.textData,
data.intData,
data.doubleData,
data.dateData
)
// 데이터베이스 오픈
val sqliteDatabase = DBHelper(context)
// 쿼리 실행( 쿼리문, ?에 세팅할 값 배열)
sqliteDatabase.writableDatabase.execSQL(sql, arg1)
// 데이터 베이스를 받아준다.
sqliteDatabase.close()
}
// Read Condition : 조건에 맞는 행 하나를 가져온다.
fun selectData(context: Context, idx: Int): TestClass {
// 쿼리문을 작성한다.
val sql = "select * from TestTable where idx=?"
// ?에 들어갈 값, 값이 하나라도 배열로 만들어야 한다. (문자열 배열)
val arg1 = arrayOf("$idx")
// 데이터 베이스 오픈
val dbHelper = DBHelper(context)
// 쿼리 실행
val cursor = dbHelper.writableDatabase.rawQuery(sql, arg1)
cursor.moveToNext()
// 컬럼의 이름을 지정하여 컬럼의 순서값을 가져온다.(원하는 행을 지칭할 수 없기 때문)
val idx1 = cursor.getColumnIndex("idx")
val idx2 = cursor.getColumnIndex("textData")
val idx3 = cursor.getColumnIndex("intData")
val idx4 = cursor.getColumnIndex("doubleData")
val idx5 = cursor.getColumnIndex("dateData")
// 데이터를 가져온다.
val idx = cursor.getInt(idx1)
val textData = cursor.getString(idx2)
val intData = cursor.getInt(idx3)
val doubleData = cursor.getDouble(idx4)
val dateData = cursor.getString(idx5)
dbHelper.close()
return TestClass(idx, textData, intData, doubleData, dateData)
}
// Read All : 모든 행을 가져온다.
fun selectAllData(context: Context):MutableList<TestClass>{
// 모든 행을 가져오는 쿼리문을 작성한다.
val sql = "select * from TestTable"
// 데이터 베이스 오픈
val dbHelper = DBHelper(context)
// 쿼리 실행
val cursor = dbHelper.writableDatabase.rawQuery(sql, null)
// cursor 객체는 쿼리문에 맞는 행에 접근할 수 있는 객체가 된다.
// 처음에는 아무 행도 가르치고 있지 않는다.
// moveToNext 메서드를 호출하면 다음 행에 접근할 수 있다.
// 이때 접근할 행이 있으면 true를 반환하고 없으면 false을 반환한다.
val dataList = mutableListOf<TestClass>()
while(cursor.moveToNext()){
// 컬럼의 이름을 지정하여 컬럼의 순서값을 가져온다.(원하는 행을 지칭할 수 없기 때문)
val idx1 = cursor.getColumnIndex("idx")
val idx2 = cursor.getColumnIndex("textData")
val idx3 = cursor.getColumnIndex("intData")
val idx4 = cursor.getColumnIndex("doubleData")
val idx5 = cursor.getColumnIndex("dateData")
// 데이터를 가져온다.
val idx = cursor.getInt(idx1)
val textData = cursor.getString(idx2)
val intData = cursor.getInt(idx3)
val doubleData = cursor.getDouble(idx4)
val dateData = cursor.getString(idx5)
val testClass = TestClass(idx, textData, intData, doubleData, dateData)
dataList.add(testClass)
}
dbHelper.close()
return dataList
}
// Update : 조건에 맞는 행의 컬럼의 값을 수정한다.
fun updateData(context: Context, obj:TestClass){
// 쿼리문
// idx가 ? 인 행의 textData, intData, doubleData, dateData 컬럼의 값을 변경한다.
val sql = """update TestTable
| set textData=?, intData=?, doubleData=?, dateData=?
| where idx=?
""".trimMargin()
// ? 에 들어갈 값
val args = arrayOf(
obj.textData,
obj.intData,
obj.doubleData,
obj.dateData,
obj.idx)
// 쿼리 실행
val dbHelper = DBHelper(context)
dbHelper.writableDatabase.execSQL(sql, args)
dbHelper.close()
}
// Delete : 조건에 맞는 행을 삭제한다.
fun deleteData(context: Context, idx:Int){
// 쿼리문
// TestTable에서 idx가 ? 인 행을 삭제한다.
val sql = "delete from TestTable where idx = ?"
// ? 에 들어갈 값
val args = arrayOf(idx)
// 쿼리 실행
val dbHelper = DBHelper(context)
dbHelper.writableDatabase.execSQL(sql, args)
dbHelper.close()
}
안드로이드는 SQLite 데이터 베이스 사용시 직접 쿼리문을 작성하는 것 뿐만 아니라 제공되는 클래스를 이용하는 방법을 제공하고 있다.
코드 구현은 대부분 비슷하며 쿼리문 작성 대신에 클래스를 사용하면 된다.
클래스를 이용하는 방법을 사용할 때 가장 중요한 클래스이다.
ContentValue 클래스는 값을 저장할 때 이름을 부여하는 클래스로써 값을 저장할 때 사용하는 이름은 테이블의 컬럼이름과 매칭된다.
ContentValue에 저장한 데이터는 테이블의 컬럼과 매칭되어 insert, update 등에 사용된다.