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`.
|
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:
|
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:
|
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.
|
- `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.
|
- `select(clause, params...)` — execute a statement whose primary result is a row set.
|
||||||
- `execute(clause, params...)` — execute a side-effect statement and return `ExecutionResult`.
|
- `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.
|
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.
|
- `columns` — positional `SqlColumn` metadata, available before iteration.
|
||||||
- `size()` — result row count.
|
- `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.
|
- `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`.
|
- `decodeAs<T>()` — transaction-scoped iterable view that decodes each row into `T`.
|
||||||
|
|
||||||
##### `SqlRow`
|
### `SqlRow`
|
||||||
|
|
||||||
- `row[index]` — zero-based positional access.
|
- `row[index]` — zero-based positional access.
|
||||||
- `row["columnName"]` — case-insensitive lookup by output column label.
|
- `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.
|
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(...)`.
|
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.
|
Annotation arguments are evaluated once when the declaration is created, and the resulting adapter instance is retained in declaration metadata.
|
||||||
|
|
||||||
##### `ExecutionResult`
|
### `ExecutionResult`
|
||||||
|
|
||||||
- `affectedRowsCount`
|
- `affectedRowsCount`
|
||||||
- `getGeneratedKeys()`
|
- `getGeneratedKeys()`
|
||||||
@ -260,7 +288,7 @@ Statements that return rows directly, such as `... returning ...`, should use `s
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#### Value mapping
|
## Value mapping
|
||||||
|
|
||||||
Portable bind values:
|
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.
|
`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`
|
- malformed URL or bad option shape -> `IllegalArgumentException`
|
||||||
- runtime open failure -> `DatabaseException`
|
- 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:
|
`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.
|
`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` — generic contract, available when host code installs it
|
||||||
- `lyng.io.db.sqlite` — implemented on JVM and Linux Native in the current release tree
|
- `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