Skip to content

Commit 49ede83

Browse files
committed
Complete the documents and update Kotlin's version
1 parent e0369ab commit 49ede83

File tree

7 files changed

+543
-16
lines changed

7 files changed

+543
-16
lines changed

CHANGELOG.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,33 @@
22

33
- Date format: YYYY-MM-dd
44

5-
## 2.0.0 / 2025-10-xx
5+
## 2.0.0 / 2025-10-23
66

77
### All
88

9-
* Update `Kotlin`'s version to `2.2.20`
9+
* Update `Kotlin`'s version to `2.2.21`
1010
* Remove the Desuger configuration
11+
* Update minimal supported Android version from API 23 to 24
1112

1213
### sqllin-dsl
1314

1415
* Optimized performance for SQL assembly
15-
* New API for creating Database: `DSLDBConfiguration`
16-
* New experimental API: `DatabaseScope#CREATE`
17-
* New experimental API: `DatabaseScope#DROP`
18-
* New experimental API: `DatabaseSceop#ALERT`
16+
* New annotation for marking primary key: `PrimaryKey`
17+
* New annotation for marking composite primary key: `CompositePrimaryKey`
18+
* New experimental API for creating Database: `DSLDBConfiguration`
19+
* New experimental DSL API: `DatabaseScope#CREATE`
20+
* New experimental DSL API: `DatabaseScope#DROP`
21+
* New experimental DSL API: `DatabaseSceop#ALERT`
1922
* Support using ByteArray in DSL, that represents BLOB in SQLite
2023

2124
### sqllin-driver
2225

2326
* Update the `sqlite-jdbc`'s version to `3.50.3.0`
27+
* **Breaking change**: The data type of `bindParams` in `DatabaseConnection#query` changed from `Array<out String?>?` to `Array<out Any?>?`
2428

2529
### sqllin-processor
2630

27-
* Update `KSP`'s version to `2.2.20-2.0.4`
31+
* Update `KSP`'s version to `2.3.0`
2832

2933
## 1.4.4 / 2025-07-07
3034

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[versions]
22

3-
kotlin = "2.2.20"
3+
kotlin = "2.2.21"
44
agp = "8.12.3"
5-
ksp = "2.2.20-2.0.4"
5+
ksp = "2.3.0"
66
serialization = "1.9.0"
77
coroutines = "1.10.2"
88
androidx-annotation = "1.9.1"
@@ -11,7 +11,7 @@ androidx-test-runner = "1.7.0"
1111
sqlite-jdbc = "3.50.3.0"
1212
jvm-toolchain = "21"
1313
android-sdk-compile = "36"
14-
android-sdk-min = "23"
14+
android-sdk-min = "24"
1515
vanniktech-maven-publish = "0.34.0"
1616

1717
[libraries]

sqllin-architecture.png

-1.65 MB
Loading

sqllin-dsl/doc/getting-start-cn.md

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ plugins {
1414
id("com.google.devtools.ksp")
1515
}
1616

17-
val sqllinVersion = "1.4.4"
17+
val sqllinVersion = "2.0.0"
1818

1919
kotlin {
2020
// ......
@@ -122,10 +122,48 @@ val database = Database(
122122
注意,由于 Android Framework 的限制,`inMemory``journalMode``lookasideSlotSize``lookasideSlotCount` 这些参数仅在 Android 9 及以上版本生效。 并且,由于
123123
[sqlite-jdbc](https://github.com/xerial/sqlite-jdbc)(SQLlin 在 JVM 上基于它)不支持 `sqlite3_config()``lookasideSlotSize``lookasideSlotCount` 两个属性在 JVM 平台不生效。
124124

125-
当前由于会改变数据库结构的操作暂时还没有 DSL 化支持。因此,你需要在 `create``update` 参数中使用字符串编写 SQL 语句。
125+
### 使用 DSLDBConfiguration 进行类型安全的模式管理
126+
127+
除此之外,你还可以使用新的试验性 API `DSLDBConfiguration`,它允许你在 `create``upgrade` 回调中使用类型安全的 SQL DSL,而不是原始的 SQL 字符串:
128+
129+
```kotlin
130+
import com.ctrip.sqllin.driver.DSLDBConfiguration
131+
import com.ctrip.sqllin.dsl.Database
132+
133+
val database = Database(
134+
DSLDBConfiguration(
135+
name = "Person.db",
136+
path = getGlobalDatabasePath(),
137+
version = 1,
138+
isReadOnly = false,
139+
inMemory = false,
140+
journalMode = JournalMode.WAL,
141+
synchronousMode = SynchronousMode.NORMAL,
142+
busyTimeout = 5000,
143+
lookasideSlotSize = 0,
144+
lookasideSlotCount = 0,
145+
create = {
146+
// Use type-safe DSL instead of raw SQL
147+
CREATE(PersonTable)
148+
},
149+
upgrade = { oldVersion, newVersion ->
150+
when (oldVersion) {
151+
1 -> {
152+
// Example: Add a new column in version 2
153+
PersonTable ALERT_ADD_COLUMN PersonTable.email
154+
}
155+
}
156+
}
157+
)
158+
)
159+
```
160+
161+
通过使用 `DSLDBConfiguration`,你可以直接在回调中使用 CREATE、DROP 和 ALTER 操作,使模式管理更加类型安全和易于维护。这些回调中可用的 DSL 操作与常规 `database { }` 块中可用的操作相同。
126162

127163
通常你只需要在你的组件的生命周期内创建一个 `Database` 对象,所以你需要在组件的生命周期结束时手动关闭数据库:
128164

165+
> 注意: `DSLDBConfiguration` 处于实验性阶段,但当其稳定后会彻底取代 `DatabaseConfiguration`, 也就是说在未来版本中 _sqllin-dsl_ 将不再支持使用 `DatabaseConfiguration` 创建 `Database` 实例。
166+
129167
```kotlin
130168
override fun onDestroy() {
131169
database.close()
@@ -156,6 +194,74 @@ data class Person(
156194
_sqllin-dsl_ 中,对象序列化为 SQL 语句,或者从游标中反序列化依赖 _kotlinx.serialization_,所以你需要在你的 data class
157195
上添加 `@Serializable` 注解。因此,如果你想在序列化或反序列化以及 `Table` 类生成的时候忽略某些属性,你可以给你的属性添加 `kotlinx.serialization.Transient` 注解。
158196

197+
### 定义主键
198+
199+
SQLlin 提供了用于定义数据库表主键的注解。
200+
201+
#### 使用 @PrimaryKey 定义单一主键
202+
203+
使用 `@PrimaryKey` 标记单个属性作为主键:
204+
205+
```kotlin
206+
import com.ctrip.sqllin.dsl.annotation.DBRow
207+
import com.ctrip.sqllin.dsl.annotation.PrimaryKey
208+
import kotlinx.serialization.Serializable
209+
210+
@DBRow
211+
@Serializable
212+
data class Person(
213+
@PrimaryKey(autoIncrement = true)
214+
val id: Long? = null, // Auto-incrementing primary key
215+
val name: String,
216+
val age: Int,
217+
)
218+
```
219+
220+
**重要的类型和可空性规则:**
221+
222+
- **对于自增的 `Long` 主键**:属性**必须**声明为可空类型(`Long?`)。这会映射到 SQLite 的 `INTEGER PRIMARY KEY`,它作为内部 `rowid` 的别名。当插入 `id = null` 的新记录时,SQLite 会自动生成 ID。
223+
224+
- **对于其他类型(String、Int 等)**:属性**必须**是非空的。插入时必须提供唯一值:
225+
226+
```kotlin
227+
@DBRow
228+
@Serializable
229+
data class User(
230+
@PrimaryKey
231+
val username: String, // Non-nullable, user-provided primary key
232+
val email: String,
233+
)
234+
```
235+
236+
`autoIncrement` 参数启用更严格的自增行为(使用 `AUTOINCREMENT` 关键字),确保行 ID 永远不会被重用。这仅对 `Long?` 属性有意义。
237+
238+
#### 使用 @CompositePrimaryKey 定义组合主键
239+
240+
当表的主键由多个列组成时,使用 `@CompositePrimaryKey`
241+
242+
```kotlin
243+
import com.ctrip.sqllin.dsl.annotation.DBRow
244+
import com.ctrip.sqllin.dsl.annotation.CompositePrimaryKey
245+
import kotlinx.serialization.Serializable
246+
247+
@DBRow
248+
@Serializable
249+
data class Enrollment(
250+
@CompositePrimaryKey
251+
val studentId: Long,
252+
@CompositePrimaryKey
253+
val courseId: Long,
254+
val enrollmentDate: String,
255+
)
256+
```
257+
258+
**重要规则:**
259+
260+
- 你可以在同一个类中对**多个属性**应用 `@CompositePrimaryKey`
261+
- 所有带有 `@CompositePrimaryKey` 的属性**必须是非空的**
262+
-**不能**在同一个类中混合使用 `@PrimaryKey``@CompositePrimaryKey` - 只能使用其中一个
263+
- 所有 `@CompositePrimaryKey` 属性的组合形成表的组合主键
264+
159265
## 接下来
160266

161267
你已经学习完了所有的准备工作,现在可以开始学习如何操作数据库了:

sqllin-dsl/doc/getting-start.md

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ plugins {
1616
id("com.google.devtools.ksp")
1717
}
1818

19-
val sqllinVersion = "1.4.4"
19+
val sqllinVersion = "2.0.0"
2020

2121
kotlin {
2222
// ......
@@ -126,15 +126,52 @@ val database = Database(
126126
)
127127
```
128128

129-
Note, because of limitation by Android Framework, the `inMemory`, `busyTimeout`, `lookasideSlotSize`, `lookasideSlotCount`
129+
Note, because of limitation by Android Framework, the `inMemory`, `busyTimeout`, `lookasideSlotSize`, `lookasideSlotCount`
130130
only work on Android 9 and higher. And, because [sqlite-jdbc](https://github.com/xerial/sqlite-jdbc)(SQLlin is based on it on JVM) doesn't support
131131
`sqlite3_config()`, the `lookasideSlotSize` and `lookasideSlotCount` don't work on JVM target.
132132

133-
Now, the operations that change database structure haven't been supported by DSL yet. So, you need to write these SQL statements by string
134-
as in `create` and `upgrade` parameters.
133+
### Using DSLDBConfiguration for Type-Safe Schema Management
134+
135+
Alternatively, you can use `DSLDBConfiguration` which allows you to use the type-safe SQL DSL in the `create` and `upgrade` callbacks instead of raw SQL strings:
136+
137+
```kotlin
138+
import com.ctrip.sqllin.driver.DSLDBConfiguration
139+
import com.ctrip.sqllin.dsl.Database
140+
141+
val database = Database(
142+
DSLDBConfiguration(
143+
name = "Person.db",
144+
path = getGlobalDatabasePath(),
145+
version = 1,
146+
isReadOnly = false,
147+
inMemory = false,
148+
journalMode = JournalMode.WAL,
149+
synchronousMode = SynchronousMode.NORMAL,
150+
busyTimeout = 5000,
151+
lookasideSlotSize = 0,
152+
lookasideSlotCount = 0,
153+
create = {
154+
// Use type-safe DSL instead of raw SQL
155+
CREATE(PersonTable)
156+
},
157+
upgrade = { oldVersion, newVersion ->
158+
when (oldVersion) {
159+
1 -> {
160+
// Example: Add a new column in version 2
161+
PersonTable ALERT_ADD_COLUMN PersonTable.email
162+
}
163+
}
164+
}
165+
)
166+
)
167+
```
168+
169+
With `DSLDBConfiguration`, you can use CREATE, DROP, and ALTER operations directly in the callbacks, making schema management more type-safe and maintainable. The DSL operations available in these callbacks are the same as those available in regular `database { }` blocks.
135170

136171
Usually, you just need to create one `Database` instance in your component lifecycle. So, you need to close database manually when the lifecycle ended:
137172

173+
> Notice: `DSLDBConfiguration` is experimental, but it will completely replace `DatabaseConfiguration` when it is stable. That means _sqllin-dsl_ will not support to use `DatabaseConfiguration` to create `Database` instances in the future versions.
174+
138175
```kotlin
139176
override fun onDestroy() {
140177
database.close()
@@ -167,6 +204,74 @@ name as table name, for example, `Person`'s default table name is "Person".
167204
In _sqllin-dsl_, objects are serialized to SQL and deserialized from cursor depend on _kotlinx.serialization_. So, you also need to add the `@Serializable` onto your data classes. Therefore, if
168205
you want to ignore some properties when serialization or deserialization and `Table` classes generation, you can annotate your properties with `kotlinx.serialization.Transient`.
169206

207+
### Defining Primary Keys
208+
209+
SQLlin provides annotations to define primary keys for your database tables.
210+
211+
#### Single Primary Key with @PrimaryKey
212+
213+
Use `@PrimaryKey` to mark a single property as the primary key:
214+
215+
```kotlin
216+
import com.ctrip.sqllin.dsl.annotation.DBRow
217+
import com.ctrip.sqllin.dsl.annotation.PrimaryKey
218+
import kotlinx.serialization.Serializable
219+
220+
@DBRow
221+
@Serializable
222+
data class Person(
223+
@PrimaryKey(autoIncrement = true)
224+
val id: Long? = null, // Auto-incrementing primary key
225+
val name: String,
226+
val age: Int,
227+
)
228+
```
229+
230+
**Important type and nullability rules:**
231+
232+
- **For `Long` primary keys with auto-increment**: The property **must** be declared as nullable (`Long?`). This maps to SQLite's `INTEGER PRIMARY KEY` which acts as an alias for the internal `rowid`. When inserting a new record with `id = null`, SQLite automatically generates the ID.
233+
234+
- **For other types (String, Int, etc.)**: The property **must** be non-nullable. You must provide a unique value when inserting:
235+
236+
```kotlin
237+
@DBRow
238+
@Serializable
239+
data class User(
240+
@PrimaryKey
241+
val username: String, // Non-nullable, user-provided primary key
242+
val email: String,
243+
)
244+
```
245+
246+
The `autoIncrement` parameter enables stricter auto-incrementing behavior (using `AUTOINCREMENT` keyword), ensuring row IDs are never reused. This is only meaningful for `Long?` properties.
247+
248+
#### Composite Primary Key with @CompositePrimaryKey
249+
250+
Use `@CompositePrimaryKey` when your table's primary key consists of multiple columns:
251+
252+
```kotlin
253+
import com.ctrip.sqllin.dsl.annotation.DBRow
254+
import com.ctrip.sqllin.dsl.annotation.CompositePrimaryKey
255+
import kotlinx.serialization.Serializable
256+
257+
@DBRow
258+
@Serializable
259+
data class Enrollment(
260+
@CompositePrimaryKey
261+
val studentId: Long,
262+
@CompositePrimaryKey
263+
val courseId: Long,
264+
val enrollmentDate: String,
265+
)
266+
```
267+
268+
**Important rules:**
269+
270+
- You can apply `@CompositePrimaryKey` to **multiple properties** in the same class
271+
- All properties with `@CompositePrimaryKey` **must be non-nullable**
272+
- You **cannot** mix `@PrimaryKey` and `@CompositePrimaryKey` in the same class - use one or the other
273+
- The combination of all `@CompositePrimaryKey` properties forms the table's composite primary key
274+
170275
## Next Step
171276

172277
You have learned all the preparations, you can start learn how to operate database now:

0 commit comments

Comments
 (0)