Add runnable DB serialization example
This commit is contained in:
parent
92e9325f40
commit
66b8806b11
@ -1,4 +1,4 @@
|
||||
### lyng.io.db — SQL database access for Lyng scripts
|
||||
# lyng.io.db — SQL database access for Lyng scripts
|
||||
|
||||
This module provides the portable SQL database contract for Lyng. The current shipped providers are SQLite via `lyng.io.db.sqlite` and a JVM-only JDBC bridge via `lyng.io.db.jdbc`.
|
||||
|
||||
@ -6,7 +6,7 @@ This module provides the portable SQL database contract for Lyng. The current sh
|
||||
|
||||
---
|
||||
|
||||
#### Install the module into a Lyng session
|
||||
## Install the module into a Lyng session
|
||||
|
||||
For SQLite-backed database access, install both the generic DB module and the SQLite provider:
|
||||
|
||||
@ -54,7 +54,7 @@ suspend fun bootstrapJdbc() {
|
||||
|
||||
---
|
||||
|
||||
#### Using from Lyng scripts
|
||||
## Using from Lyng scripts
|
||||
|
||||
Typed SQLite open helper:
|
||||
|
||||
@ -188,13 +188,41 @@ assertThrows(RollbackException) {
|
||||
|
||||
---
|
||||
|
||||
#### Portable API
|
||||
## Runnable serialization sample
|
||||
|
||||
##### `Database`
|
||||
A complete runnable example is in [examples/sqlite_serialization.lyng](/home/sergeych/dev/lyng/examples/sqlite_serialization.lyng).
|
||||
|
||||
It uses:
|
||||
|
||||
- `@DbJson`
|
||||
- `@DbLynon`
|
||||
- `@DbExcept`
|
||||
- `@cols(...)`, `@vals(...)`, `@set(...)`
|
||||
- `decodeAs<T>()`
|
||||
|
||||
The current direct read form that works under `jlyng` is:
|
||||
|
||||
```lyng
|
||||
tx.select("select * from item where id = ?", 1).decodeAs<Item>().first
|
||||
```
|
||||
|
||||
If we want a shorter form such as:
|
||||
|
||||
```lyng
|
||||
tx.selectAllAs<Item>("item where id = ?", 1).first
|
||||
```
|
||||
|
||||
it should be added as a built-in `SqlTransaction` API. A pure Lyng generic wrapper around `decodeAs<T>()` does not currently preserve `T` reliably enough under `jlyng`.
|
||||
|
||||
---
|
||||
|
||||
## Portable API
|
||||
|
||||
### `Database`
|
||||
|
||||
- `transaction(block)` — opens a transaction, commits on normal exit, rolls back on uncaught failure.
|
||||
|
||||
##### `SqlTransaction`
|
||||
### `SqlTransaction`
|
||||
|
||||
- `select(clause, params...)` — execute a statement whose primary result is a row set.
|
||||
- `execute(clause, params...)` — execute a side-effect statement and return `ExecutionResult`.
|
||||
@ -221,7 +249,7 @@ tx.execute("update item set @set(?1) where id = ?2", item, item.id)
|
||||
|
||||
When a clause uses any of these macros, non-expanded scalar parameters in the same SQL string must use explicit indexed placeholders such as `?2`, `?3`, and so on.
|
||||
|
||||
##### `ResultSet`
|
||||
### `ResultSet`
|
||||
|
||||
- `columns` — positional `SqlColumn` metadata, available before iteration.
|
||||
- `size()` — result row count.
|
||||
@ -230,7 +258,7 @@ When a clause uses any of these macros, non-expanded scalar parameters in the sa
|
||||
- `toList()` — materialize detached `SqlRow` snapshots that may be used after the transaction ends.
|
||||
- `decodeAs<T>()` — transaction-scoped iterable view that decodes each row into `T`.
|
||||
|
||||
##### `SqlRow`
|
||||
### `SqlRow`
|
||||
|
||||
- `row[index]` — zero-based positional access.
|
||||
- `row["columnName"]` — case-insensitive lookup by output column label.
|
||||
@ -238,7 +266,7 @@ When a clause uses any of these macros, non-expanded scalar parameters in the sa
|
||||
|
||||
Name-based access fails with `SqlUsageException` if the name is missing or ambiguous.
|
||||
|
||||
##### `DbFieldAdapter`
|
||||
### `DbFieldAdapter`
|
||||
|
||||
Custom DB field projection hook used by `@DbDecodeWith(...)` and `@DbSerializeWith(...)`.
|
||||
|
||||
@ -251,7 +279,7 @@ Use `@DbSerializeWith(adapter)` on constructor parameters and class-body fields/
|
||||
|
||||
Annotation arguments are evaluated once when the declaration is created, and the resulting adapter instance is retained in declaration metadata.
|
||||
|
||||
##### `ExecutionResult`
|
||||
### `ExecutionResult`
|
||||
|
||||
- `affectedRowsCount`
|
||||
- `getGeneratedKeys()`
|
||||
@ -260,7 +288,7 @@ Statements that return rows directly, such as `... returning ...`, should use `s
|
||||
|
||||
---
|
||||
|
||||
#### Value mapping
|
||||
## Value mapping
|
||||
|
||||
Portable bind values:
|
||||
|
||||
@ -387,7 +415,7 @@ For temporal types, see [time functions](time.md).
|
||||
|
||||
---
|
||||
|
||||
#### SQLite provider
|
||||
## SQLite provider
|
||||
|
||||
`lyng.io.db.sqlite` currently provides the first concrete backend.
|
||||
|
||||
@ -431,7 +459,7 @@ Open-time validation failures:
|
||||
- malformed URL or bad option shape -> `IllegalArgumentException`
|
||||
- runtime open failure -> `DatabaseException`
|
||||
|
||||
#### JDBC provider
|
||||
## JDBC provider
|
||||
|
||||
`lyng.io.db.jdbc` is currently implemented on the JVM target only. The `lyngio-jvm` artifact bundles and explicitly loads these JDBC drivers:
|
||||
|
||||
@ -503,7 +531,7 @@ PostgreSQL-specific notes:
|
||||
|
||||
---
|
||||
|
||||
#### Lifetime rules
|
||||
## Lifetime rules
|
||||
|
||||
`ResultSet` is valid only while its owning transaction is active.
|
||||
|
||||
@ -528,7 +556,7 @@ The same rule applies to generated keys from `ExecutionResult.getGeneratedKeys()
|
||||
|
||||
---
|
||||
|
||||
#### Platform support
|
||||
## Platform support
|
||||
|
||||
- `lyng.io.db` — generic contract, available when host code installs it
|
||||
- `lyng.io.db.sqlite` — implemented on JVM and Linux Native in the current release tree
|
||||
|
||||
56
examples/sqlite_serialization.lyng
Normal file
56
examples/sqlite_serialization.lyng
Normal file
@ -0,0 +1,56 @@
|
||||
import lyng.io.db.sqlite
|
||||
|
||||
println("SQLite serialization demo: write-side projection and decodeAs<T>()")
|
||||
|
||||
class Payload(name: String, count: Int)
|
||||
|
||||
class Item(
|
||||
id: Int,
|
||||
title: String,
|
||||
@DbJson meta: Payload,
|
||||
@DbLynon state: Payload
|
||||
) {
|
||||
var note: String = ""
|
||||
@DbExcept var cache: String = ""
|
||||
}
|
||||
|
||||
val restored = openSqlite(":memory:").transaction { tx ->
|
||||
tx.execute(
|
||||
"create table item(id integer not null, title text not null, meta text not null, state blob not null, note text not null)"
|
||||
)
|
||||
|
||||
val item = Item(1, "first", Payload("json", 10), Payload("bin", 20))
|
||||
item.note = "created"
|
||||
item.cache = "not stored"
|
||||
|
||||
tx.execute("insert into item(@cols(?1)) values(@vals(?1))", item)
|
||||
|
||||
item.title = "second"
|
||||
item.meta = Payload("json2", 11)
|
||||
item.state = Payload("bin2", 21)
|
||||
item.note = "updated"
|
||||
|
||||
tx.execute(
|
||||
"update item set @set(?1 except: \"id\") where id = ?2",
|
||||
item,
|
||||
item.id
|
||||
)
|
||||
|
||||
val restored = tx.select("select * from item where id = ?", 1).decodeAs<Item>().first
|
||||
|
||||
assertEquals("second", restored.title)
|
||||
assertEquals("json2", restored.meta.name)
|
||||
assertEquals(11, restored.meta.count)
|
||||
assertEquals("bin2", restored.state.name)
|
||||
assertEquals(21, restored.state.count)
|
||||
assertEquals("updated", restored.note)
|
||||
restored
|
||||
} as Item
|
||||
|
||||
println("Restored item:")
|
||||
println(" id=" + restored.id)
|
||||
println(" title=" + restored.title)
|
||||
println(" meta=" + restored.meta.name + "/" + restored.meta.count)
|
||||
println(" state=" + restored.state.name + "/" + restored.state.count)
|
||||
println(" note=" + restored.note)
|
||||
println("OK")
|
||||
Loading…
x
Reference in New Issue
Block a user