SQLDelight는 DB를 다루는 라이브러리면서 ROOM과는 반대로 작동한다. ROOM은 DB를 다루는 클래스를 만들면, 여기서 안전한 SQL Query를 만들어 준다면, SQLDelight는 SQLQuery를 작성하면, 이로부터 typesafe하고 안전하게 사용가능한 Kotlin API를 생성해준다. 뭐가 더 좋은지는 선택에 달려있는데, SQLDelight가 순수하게 Kotlin베이스라는 점에서 KMP(Kotlin Multi-Platform)개발에서 주로 사용되는 것으로 보인다.
안드로이드에서 사용하기 위해선 우선 플러그인이 필요하다. 버전 카탈로그를 사용하는 기준으로 libs.versions.toml 에 다음과 같이 관련 내용을 추가해준다.
[versions]
...
sqldelight = "2.0.2"
...
[plugins]
...
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
build.gradle.kts 에 다음과 같이 플러그인을 추가해준다.
plugins {
...
alias(libs.plugins.sqldelight)
}
...
sqldelight {
databases {
create("Database") {
packageName.set("net.batmask.sqldelighttest01")
}
}
}
sqldelight { } 블럭이 눈에 띄는데, 대충 읽히듯 DB생성에 대한 것이고 앱의 패키지 명을 써주면 된다.
앞에서 기술했듯, sqldelight는 SQL문을 작성하면 이를 Kotlin API로 만들어준다. 디폴트로 SQLDelight 플러그인은 sqldelight 폴더안에 있는 .sq파일들을 찾게 되어 있다. 작성중인 패키지의 src/main/ 밑에 sqldelight 폴더를 만들고 거기에 .sq파일을 작성한다. 아래 파일을 Player.sq 이름으로 작성한다.
CREATE TABLE hockeyPlayer (
player_number INTEGER PRIMARY KEY NOT NULL,
full_name TEXT NOT NULL
);
CREATE INDEX hockeyPlayer_full_name ON hockeyPlayer(full_name);
INSERT INTO hockeyPlayer (player_number, full_name)
VALUES (15, 'Ryan Getzlaf');
테이블을 생성하고 인덱스를 생성, 또 초기 데이터를 추가하는 작업까지 가능하다.
이렇게 생성된 DB를 사용하려면 플랫폼에 맞는 드라이버가 필요하다. 안드로이드라면, 다음의 드라이버를 toml파일과 build.gradle.kts에 추가해준다.
[libraries]
...
android-driver = { group = "app.cash.sqldelight", name = "android-driver", version.ref = "sqldelight" }
...
implementation(libs.android.driver)
드라이버 추가후, 코드 내에서 사용하려면 다음과 같이 사용한다.
val driver: SqlDriver = AndroidSqliteDriver(Database.Schema, context, "test.db")
Database는 build.gradle.kts에 기술했던 sqldelight블럭에 있는 create()로 명시된 DB명이다. 이것은 sqldelight 플러그인에 의해 동일한 명의 클래스가 생성되며, 여기에 앞서 기술한 SQL Schema들이 코틀린 코드로 포함되어있다. 이를 Database.Schema로 전달하고 있는 것이다. 두번째 인자인 context는 android application의 context를 전달하며, 마지막은 실제로 생성될 DB의 이름이다.
이제 여기에 DB를 사용하기 위한 쿼리들을 추가할 수 있다. 앞서 작성했던 Player.sq파일에 다음과 같이 추가가 가능하다.
selectAll:
SELECT *
FROM hockeyPlayer;
insert:
INSERT INTO hockeyPlayer(player_number, full_name)
VALUES (?, ?);
insertFullPlayerObject:
INSERT INTO hockeyPlayer(player_number, full_name)
VALUES ?;
이렇게 하면, Queries 오브젝트가 각 sq파일마다 생성되는데, 위 파일 이름이 Player.sq였으므로 PlayerQueries 오브젝트가 생성된다. 각 쿼리에 레이블을 쓰고 콜론(:)을 붙이고 있다. 그리고 쿼리가 기술되고 있는데, 이 레이블은 각각 실제 사용가능한 함수명이 되고, 이는 이 오브젝트의 멤버들로 포함된다.
이렇게 생성된 쿼리에 대한 코틀린 코드는 다음과 같이 사용가능하다.
fun doDatabaseThings(driver: SqlDriver) {
val database = Database(driver)
val playerQueries: PlayerQueries = database.playerQueries
println(playerQueries.selectAll().executeAsList())
// [HockeyPlayer(15, "Ryan Getzlaf")]
playerQueries.insert(player_number = 10, full_name = "Corey Perry")
println(playerQueries.selectAll().executeAsList())
// [HockeyPlayer(15, "Ryan Getzlaf"), HockeyPlayer(10, "Corey Perry")]
val player = HockeyPlayer(10, "Ronald McDonald")
playerQueries.insertFullPlayerObject(player)
}
코드를 보면, Database에 앞에서 생성한 드라이버를 넘겨 DB에 대한 객체를 생성하고 있다. 또한, 앞에서 말한대로 생성된 PlayerQueries 객체를 이 database로부터 참조한다. 이로부터 각 쿼리함수들을 자유롭게 사용하고 있다.
기본적인 사용방법은 이와 같고, 다른 플랫폼이나 추가적인 API들은 공식 사이트를 참조하기 바란다.