@zio.dev/zio-blocks 0.0.33 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: repo
|
|
3
|
+
title: "Repo"
|
|
4
|
+
description: "Reference for Repo[E, ID]: a type-safe CRUD repository in the sql module that pre-generates all SQL at construction time from a Schema-derived Table."
|
|
5
|
+
keywords:
|
|
6
|
+
- "Repo CRUD repository"
|
|
7
|
+
- "schema-driven SQL derivation"
|
|
8
|
+
- "insertBatch batch insert"
|
|
9
|
+
- "insertAll multi-row insert"
|
|
10
|
+
- "find primary key lookup"
|
|
11
|
+
- "DbCon implicit context"
|
|
12
|
+
- "Table entity mapping"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
`Repo[E, ID]` is a type-safe CRUD repository that provides `all`, `find`, `insert`, `update`, `delete`, and related operations for entities of type `E` identified by a primary key of type `ID`. In the `sql` module's layered architecture, `Repo` sits above `Table[E]` (which it wraps) and below the `Transactor` (which supplies the `DbCon` or `DbTx` context each operation requires). All SQL — `SELECT`, `INSERT`, `UPDATE`, and `DELETE` — is assembled from the `Table`'s column metadata at construction time; individual calls do only parameter binding.
|
|
16
|
+
|
|
17
|
+
Its primary constructor and public fields have this structure:
|
|
18
|
+
|
|
19
|
+
```scala
|
|
20
|
+
class Repo[E, ID](
|
|
21
|
+
val table: Table[E],
|
|
22
|
+
val idColumn: String,
|
|
23
|
+
val idCodec: DbCodec[ID],
|
|
24
|
+
val getId: E => ID
|
|
25
|
+
) {
|
|
26
|
+
// ... all the CRUD methods are defined here ...
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Motivation
|
|
31
|
+
|
|
32
|
+
Writing CRUD SQL by hand requires keeping column names, result-set positions, and prepared-statement parameter counts in sync with your Scala types — a maintenance burden that grows with every added field or renamed column. `Repo` eliminates that burden by deriving all standard CRUD statements from the same `Schema[E]` that already describes your domain type: `Repo.derived` inspects the schema's field names, types, and `@Modifier` annotations once at construction time and stores the resulting parameterized fragments for reuse.
|
|
33
|
+
|
|
34
|
+
When the standard operations — select-all, select-by-id, insert, update, delete — cover your needs, `Repo` means zero SQL maintenance. When you need custom queries (filtered selects, joins, aggregations), the `sql"..."` interpolator and `Frag` type compose freely alongside `Repo` inside the same `Transactor#connect` or `Transactor#transact` block, so you never have to choose between a repository pattern and hand-written SQL.
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
The example below shows the full lifecycle: deriving a repository from a `Schema`, setting up the table, writing and reading entities, and performing both bulk and single-entity operations — all inside one `transact` block:
|
|
39
|
+
|
|
40
|
+
```scala
|
|
41
|
+
import zio.blocks.sql._
|
|
42
|
+
import zio.blocks.schema.Schema
|
|
43
|
+
import zio.blocks.maybe.Maybe
|
|
44
|
+
|
|
45
|
+
case class User(id: Int, name: String, email: String)
|
|
46
|
+
object User {
|
|
47
|
+
implicit val schema: Schema[User] = Schema.derived
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// All SQL is generated here, once, from User's Schema.
|
|
51
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
52
|
+
// repo: Repo[User, Int] = zio.blocks.sql.Repo$DerivedRepo@16e9cebd
|
|
53
|
+
val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
54
|
+
// tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@aae0228
|
|
55
|
+
|
|
56
|
+
tx.transact {
|
|
57
|
+
repo.table.createTable(summon[DbTx].dialect).update // CREATE TABLE IF NOT EXISTS users …
|
|
58
|
+
|
|
59
|
+
repo.insert(User(1, "Alice", "alice@example.com"))
|
|
60
|
+
repo.insert(User(2, "Bob", "bob@example.com"))
|
|
61
|
+
|
|
62
|
+
val allUsers: List[User] = repo.all // SELECT id, name, email FROM users
|
|
63
|
+
val one: Maybe[User] = repo.find(1) // SELECT … WHERE id = ?
|
|
64
|
+
val exists: Boolean = repo.exists(99) // SELECT … WHERE id = ?
|
|
65
|
+
val total: Long = repo.count // SELECT COUNT(*) FROM users
|
|
66
|
+
|
|
67
|
+
repo.update(User(1, "Alice Smith", "alice.smith@example.com"))
|
|
68
|
+
repo.delete(2)
|
|
69
|
+
|
|
70
|
+
// Single multi-row VALUES (…),(…) statement; returns the supplied IDs
|
|
71
|
+
val ids: Seq[Int] = repo.insertAll(Seq(User(3, "Carol", "carol@example.com")))
|
|
72
|
+
|
|
73
|
+
// JDBC addBatch/executeBatch; returns total row count
|
|
74
|
+
val n: Int = repo.insertBatch(List(User(4, "Dave", "dave@example.com")))
|
|
75
|
+
|
|
76
|
+
(allUsers, one, exists, total, ids, n)
|
|
77
|
+
}
|
|
78
|
+
// res1: Tuple6[List[User], Maybe[User], Boolean, Long, Seq[Int], Int] = (
|
|
79
|
+
// List(
|
|
80
|
+
// User(id = 1, name = "Alice", email = "alice@example.com"),
|
|
81
|
+
// User(id = 2, name = "Bob", email = "bob@example.com")
|
|
82
|
+
// ),
|
|
83
|
+
// User(id = 1, name = "Alice", email = "alice@example.com"),
|
|
84
|
+
// false,
|
|
85
|
+
// 2L,
|
|
86
|
+
// List(3),
|
|
87
|
+
// 1
|
|
88
|
+
// )
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Operations inside `transact` run under auto-commit disabled; on success the block commits and closes the connection, on any exception it rolls back.
|
|
92
|
+
|
|
93
|
+
## Construction / Creating Instances
|
|
94
|
+
|
|
95
|
+
`Repo` exposes four factory methods, three of which auto-derive the underlying `Table[E]` from an implicit `Schema[E]`. The `Repo.derived` overloads differ only in how much naming information is supplied; `Repo.apply` accepts a fully pre-built `Table[E]` for cases where you have already constructed or derived a `Table` manually.
|
|
96
|
+
|
|
97
|
+
### `Repo.derived` — Derive with ID column and getter
|
|
98
|
+
|
|
99
|
+
This overload derives the `Table[E]` from the implicit `Schema[E]` and uses the caller-supplied `idColumn` name and `getId` getter. The table name is computed by applying the default singular-snake-case policy to the type name — for example, `User` becomes `"user"` and `BlogPost` becomes `"blog_post"`.
|
|
100
|
+
|
|
101
|
+
```scala
|
|
102
|
+
object Repo {
|
|
103
|
+
def derived[E, ID](idColumn: String, getId: E => ID)(using schema: Schema[E], idCodec: DbCodec[ID]): Repo[E, ID]
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
A `DbCodec[ID]` instance is resolved automatically for all primitive ID types (`Int`, `Long`, `String`, `UUID`, and others) without any additional imports. The typical usage looks like this:
|
|
108
|
+
|
|
109
|
+
```scala
|
|
110
|
+
import zio.blocks.sql._
|
|
111
|
+
import zio.blocks.schema.Schema
|
|
112
|
+
|
|
113
|
+
case class Article(id: Int, title: String, body: String)
|
|
114
|
+
object Article {
|
|
115
|
+
implicit val schema: Schema[Article] = Schema.derived
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
val repo = Repo.derived[Article, Int]("id", _.id)
|
|
119
|
+
// repo: Repo[Article, Int] = zio.blocks.sql.Repo$DerivedRepo@2f907618
|
|
120
|
+
repo.table.name
|
|
121
|
+
// res3: String = "article"
|
|
122
|
+
repo.table.codec.columns
|
|
123
|
+
// res4: IndexedSeq[String] = Vector("id", "title", "body")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
:::caution
|
|
127
|
+
The `idColumn` string must exactly match a column name in the derived table (after `SqlNameMapper` applies, so `camelCase` fields become `snake_case`). A mismatch throws `IllegalArgumentException` (via `require`) at construction time — not at query time.
|
|
128
|
+
:::
|
|
129
|
+
|
|
130
|
+
### `Repo.derived` — Derive with table name, ID column, and getter
|
|
131
|
+
|
|
132
|
+
This overload is identical to the previous one except that it accepts an explicit `tableName`, bypassing the naming policy entirely. Use it when the default snake-case convention does not match the actual database table name — for example, when migrating from a legacy schema.
|
|
133
|
+
|
|
134
|
+
```scala
|
|
135
|
+
object Repo {
|
|
136
|
+
def derived[E, ID](tableName: String, idColumn: String, getId: E => ID)(using schema: Schema[E], idCodec: DbCodec[ID]): Repo[E, ID]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The supplied `tableName` is used verbatim (after SQL identifier validation) in all generated statements:
|
|
141
|
+
|
|
142
|
+
```scala
|
|
143
|
+
import zio.blocks.sql._
|
|
144
|
+
import zio.blocks.schema.Schema
|
|
145
|
+
|
|
146
|
+
case class OrderLine(lineId: Int, qty: Int, unitPrice: BigDecimal)
|
|
147
|
+
object OrderLine {
|
|
148
|
+
implicit val schema: Schema[OrderLine] = Schema.derived
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Overrides the default "order_line" table name with the legacy one
|
|
152
|
+
val repo = Repo.derived[OrderLine, Int]("tbl_order_lines", "line_id", _.lineId)
|
|
153
|
+
// repo: Repo[OrderLine, Int] = zio.blocks.sql.Repo$DerivedRepo@2bc241ca
|
|
154
|
+
repo.table.name
|
|
155
|
+
// res6: String = "tbl_order_lines"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `Repo.derived` — Fully automatic derivation
|
|
159
|
+
|
|
160
|
+
When called without any term arguments, `Repo.derived` requires an additional implicit `Schema[ID]` and locates the ID field in `E` using a four-priority rule, trying each in order until one matches:
|
|
161
|
+
|
|
162
|
+
1. A field annotated `@Modifier.id` whose type matches `ID`.
|
|
163
|
+
2. The unique field (among all fields) whose type matches `ID`, if there is exactly one.
|
|
164
|
+
3. A field literally named `"id"` whose type matches `ID`.
|
|
165
|
+
4. A field named `<entity>Id` (for example, `userId` for entity `User`) whose type matches `ID`.
|
|
166
|
+
|
|
167
|
+
The ID column name respects that field's `@Modifier.rename` annotation if present, or applies `SqlNameMapper.SnakeCase` to the field name otherwise. The table name uses the default naming policy.
|
|
168
|
+
|
|
169
|
+
```scala
|
|
170
|
+
object Repo {
|
|
171
|
+
def derived[E, ID](using schema: Schema[E], idSchema: Schema[ID], idCodec: DbCodec[ID]): Repo[E, ID]
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
This is the most concise form when the entity type has exactly one field of the `ID` type:
|
|
176
|
+
|
|
177
|
+
```scala
|
|
178
|
+
import zio.blocks.sql._
|
|
179
|
+
import zio.blocks.schema.Schema
|
|
180
|
+
|
|
181
|
+
case class Tag(id: Long, label: String)
|
|
182
|
+
object Tag {
|
|
183
|
+
implicit val schema: Schema[Tag] = Schema.derived
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Inspects Tag's Schema, finds the unique Long field "id", maps it to column "id"
|
|
187
|
+
val repo = Repo.derived[Tag, Long]
|
|
188
|
+
// repo: Repo[Tag, Long] = zio.blocks.sql.Repo$DerivedRepo@589393f1
|
|
189
|
+
repo.idColumn
|
|
190
|
+
// res8: String = "id"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
:::caution
|
|
194
|
+
`Repo.derived` (fully auto) throws `IllegalArgumentException` at runtime if `E` is not a case class, if no field matches any of the four priorities, or if a priority level itself is ambiguous — multiple `@Modifier.id`-annotated fields of type `ID`, or multiple type-matching fields when none of them is named `"id"` or `<entity>Id`. When any ambiguity is possible, annotate the intended field with `@Modifier.id`, or prefer the explicit overload that names the ID column and getter directly.
|
|
195
|
+
:::
|
|
196
|
+
|
|
197
|
+
### `Repo.apply` — Construct from an explicit Table
|
|
198
|
+
|
|
199
|
+
`Repo.apply` is the base constructor. It accepts a fully built `Table[E]` together with the ID column name, codec, and getter. Use it when you already have a `Table` — for example, one obtained from `Table.derived` — and want precise control over all four components.
|
|
200
|
+
|
|
201
|
+
```scala
|
|
202
|
+
object Repo {
|
|
203
|
+
def apply[E, ID](table: Table[E], idColumn: String, idCodec: DbCodec[ID], getId: E => ID): Repo[E, ID]
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Combining `Table.derived` with `Repo.apply` gives access to DDL generation via `createTable` and `dropTable` while also enabling full CRUD:
|
|
208
|
+
|
|
209
|
+
```scala
|
|
210
|
+
import zio.blocks.sql._
|
|
211
|
+
import zio.blocks.schema.Schema
|
|
212
|
+
|
|
213
|
+
case class Category(categoryId: Int, name: String)
|
|
214
|
+
object Category {
|
|
215
|
+
implicit val schema: Schema[Category] = Schema.derived
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
val table = Table.derived[Category]
|
|
219
|
+
val repo = Repo(table, "category_id", DbCodec[Int], _.categoryId)
|
|
220
|
+
|
|
221
|
+
given DbCon = ???
|
|
222
|
+
|
|
223
|
+
repo.table.createTable(summon[DbCon].dialect).update // DDL from Table
|
|
224
|
+
repo.all // pre-built SELECT from Repo
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Core Operations
|
|
228
|
+
|
|
229
|
+
Every `Repo` method requires a `DbCon` (or `DbTx`) given in scope, which is supplied by the enclosing `Transactor#connect` or `Transactor#transact` block. Using `DbTx` (from `Transactor#transact`) means all writes in a block are committed atomically or rolled back together on exception.
|
|
230
|
+
|
|
231
|
+
### Read Operations
|
|
232
|
+
|
|
233
|
+
The read operations query the database without modifying it. `Repo#all`, `Repo#findAll`, and `Repo#find` decode and return entity values; `Repo#exists` and `Repo#count` return summary information without decoding full rows.
|
|
234
|
+
|
|
235
|
+
#### `all` — Retrieve all rows
|
|
236
|
+
|
|
237
|
+
`Repo#all` executes `SELECT <columns> FROM <table>` and decodes every result-set row into an `E` using the entity's `DbCodec`, returning all rows as a `List[E]` in database-native order.
|
|
238
|
+
|
|
239
|
+
```scala
|
|
240
|
+
class Repo[E, ID] {
|
|
241
|
+
def all(using con: DbCon): List[E]
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Inside a `connect` or `transact` block, the call requires no arguments:
|
|
246
|
+
|
|
247
|
+
```scala
|
|
248
|
+
import zio.blocks.sql._
|
|
249
|
+
import zio.blocks.schema.Schema
|
|
250
|
+
|
|
251
|
+
case class User(id: Int, name: String, email: String)
|
|
252
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
253
|
+
|
|
254
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
255
|
+
given DbCon = ???
|
|
256
|
+
|
|
257
|
+
// Inside a Transactor#connect or #transact block:
|
|
258
|
+
val users: List[User] = repo.all
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
:::caution
|
|
262
|
+
`all` loads the entire table into memory. For large tables, complement `Repo` with a custom `Frag` query that includes `LIMIT` and `OFFSET` clauses.
|
|
263
|
+
:::
|
|
264
|
+
|
|
265
|
+
#### `findAll` — Retrieve rows by a set of primary keys
|
|
266
|
+
|
|
267
|
+
`Repo#findAll` executes `SELECT <columns> FROM <table> WHERE <idColumn> IN (...)` for the given IDs and decodes every matching row into an `E`. It returns an empty `List` immediately, without executing any SQL, when `ids` is empty.
|
|
268
|
+
|
|
269
|
+
```scala
|
|
270
|
+
class Repo[E, ID] {
|
|
271
|
+
def findAll(ids: Iterable[ID])(using con: DbCon): List[E]
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Use it to batch-fetch a known set of rows in a single round-trip instead of calling `find` in a loop:
|
|
276
|
+
|
|
277
|
+
```scala
|
|
278
|
+
import zio.blocks.sql._
|
|
279
|
+
import zio.blocks.schema.Schema
|
|
280
|
+
|
|
281
|
+
case class User(id: Int, name: String, email: String)
|
|
282
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
283
|
+
|
|
284
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
285
|
+
given DbCon = ???
|
|
286
|
+
|
|
287
|
+
val users: List[User] = repo.findAll(List(1, 2, 3))
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
#### `find` — Find a row by primary key
|
|
291
|
+
|
|
292
|
+
`Repo#find` executes `SELECT <columns> FROM <table> WHERE <idColumn> = ?`, binding the ID through `idCodec`. It returns `Maybe.absent` if no row with the given key exists, or `Maybe(entity)` if a row is found.
|
|
293
|
+
|
|
294
|
+
```scala
|
|
295
|
+
class Repo[E, ID] {
|
|
296
|
+
def find(id: ID)(using con: DbCon): Maybe[E]
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
The ID value is bound as a parameterized `?` — no string formatting or concatenation:
|
|
301
|
+
|
|
302
|
+
```scala
|
|
303
|
+
import zio.blocks.sql._
|
|
304
|
+
import zio.blocks.schema.Schema
|
|
305
|
+
import zio.blocks.maybe.Maybe
|
|
306
|
+
|
|
307
|
+
case class User(id: Int, name: String, email: String)
|
|
308
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
309
|
+
|
|
310
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
311
|
+
given DbCon = ???
|
|
312
|
+
|
|
313
|
+
val alice: Maybe[User] = repo.find(1)
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
#### `exists` — Check whether a row exists
|
|
317
|
+
|
|
318
|
+
`Repo#exists` returns `true` when a row with the given primary key exists in the table. It delegates to `find` and tests whether the result is defined, running a single parameterized `SELECT`.
|
|
319
|
+
|
|
320
|
+
```scala
|
|
321
|
+
class Repo[E, ID] {
|
|
322
|
+
def exists(id: ID)(using con: DbCon): Boolean
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Use `exists` when you only need to confirm presence without loading the full entity:
|
|
327
|
+
|
|
328
|
+
```scala
|
|
329
|
+
import zio.blocks.sql._
|
|
330
|
+
import zio.blocks.schema.Schema
|
|
331
|
+
|
|
332
|
+
case class User(id: Int, name: String, email: String)
|
|
333
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
334
|
+
|
|
335
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
336
|
+
given DbCon = ???
|
|
337
|
+
|
|
338
|
+
val exists: Boolean = repo.exists(99)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### `count` — Count all rows
|
|
342
|
+
|
|
343
|
+
`Repo#count` executes `SELECT COUNT(*) FROM <table>` and returns the row count as a `Long`. The result is `0L` when the table is empty.
|
|
344
|
+
|
|
345
|
+
```scala
|
|
346
|
+
class Repo[E, ID] {
|
|
347
|
+
def count(using con: DbCon): Long
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The `Long` result avoids boxing and handles tables with more than `Int.MaxValue` rows correctly:
|
|
352
|
+
|
|
353
|
+
```scala
|
|
354
|
+
import zio.blocks.sql._
|
|
355
|
+
import zio.blocks.schema.Schema
|
|
356
|
+
|
|
357
|
+
case class User(id: Int, name: String, email: String)
|
|
358
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
359
|
+
|
|
360
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
361
|
+
given DbCon = ???
|
|
362
|
+
|
|
363
|
+
val total: Long = repo.count
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Write Operations
|
|
367
|
+
|
|
368
|
+
The write operations insert, update, or delete rows in the database. `Repo#insert`, `Repo#insertBatch`, `Repo#insertAll`, `Repo#update`, `Repo#delete`, `Repo#deleteAll`, and `Repo#clear` each return an `Int` row count (`insertAll` returns the inserted IDs instead); `Repo#insertReturning` is the exception and returns the full inserted entity.
|
|
369
|
+
|
|
370
|
+
#### `insert` — Insert a single entity
|
|
371
|
+
|
|
372
|
+
`Repo#insert` encodes the entity with `DbCodec[E]` and executes `INSERT INTO <table> (<columns>) VALUES (?, …, ?)`, returning the number of affected rows — normally 1 on success.
|
|
373
|
+
|
|
374
|
+
```scala
|
|
375
|
+
class Repo[E, ID] {
|
|
376
|
+
def insert(entity: E)(using con: DbCon): Int
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Each call uses the pre-built SQL string and binds fresh parameter values from the entity:
|
|
381
|
+
|
|
382
|
+
```scala
|
|
383
|
+
import zio.blocks.sql._
|
|
384
|
+
import zio.blocks.schema.Schema
|
|
385
|
+
|
|
386
|
+
case class User(id: Int, name: String, email: String)
|
|
387
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
388
|
+
|
|
389
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
390
|
+
given DbCon = ???
|
|
391
|
+
|
|
392
|
+
val rowsAffected: Int = repo.insert(User(1, "Alice", "alice@example.com"))
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### `insertReturning` — Insert and return the inserted entity
|
|
396
|
+
|
|
397
|
+
`Repo#insertReturning` inserts the entity, retrieves the generated primary key via JDBC's `getGeneratedKeys`, and re-fetches the full row by calling `find` with that key. If the driver returns no generated key, it falls back to `find(getId(entity))`.
|
|
398
|
+
|
|
399
|
+
```scala
|
|
400
|
+
class Repo[E, ID] {
|
|
401
|
+
def insertReturning(entity: E)(using con: DbCon): E
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
This method is most useful when the database assigns the primary key — for example, via an auto-increment or sequence column — and the caller needs to read the assigned value back:
|
|
406
|
+
|
|
407
|
+
```scala
|
|
408
|
+
import zio.blocks.sql._
|
|
409
|
+
import zio.blocks.schema.Schema
|
|
410
|
+
|
|
411
|
+
case class User(id: Int, name: String, email: String)
|
|
412
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
413
|
+
|
|
414
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
415
|
+
given DbCon = ???
|
|
416
|
+
|
|
417
|
+
// Pass a placeholder ID; the returned entity carries the database-assigned key.
|
|
418
|
+
val inserted: User = repo.insertReturning(User(0, "Bob", "bob@example.com"))
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
:::caution
|
|
422
|
+
`insertReturning` throws `NoSuchElementException` if the inserted row cannot be found after the insert. This can occur when the JDBC driver returns a key whose type does not match what `idCodec` expects. Verify that `idCodec` matches the database column type before relying on auto-generated keys.
|
|
423
|
+
:::
|
|
424
|
+
|
|
425
|
+
#### `insertBatch` — Batch-insert multiple entities
|
|
426
|
+
|
|
427
|
+
`Repo#insertBatch` inserts a collection of entities using JDBC batch execution (`addBatch` / `executeBatch`). Sending all parameter sets to the driver in a single round-trip is significantly faster than calling `insert` in a loop for large collections. It returns the total number of affected rows across all batched statements.
|
|
428
|
+
|
|
429
|
+
```scala
|
|
430
|
+
class Repo[E, ID] {
|
|
431
|
+
def insertBatch(entities: Iterable[E])(using con: DbCon): Int
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
The method returns 0 immediately when the input is empty, without opening a prepared statement:
|
|
436
|
+
|
|
437
|
+
```scala
|
|
438
|
+
import zio.blocks.sql._
|
|
439
|
+
import zio.blocks.schema.Schema
|
|
440
|
+
|
|
441
|
+
case class User(id: Int, name: String, email: String)
|
|
442
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
443
|
+
|
|
444
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
445
|
+
given DbCon = ???
|
|
446
|
+
|
|
447
|
+
val users = List(
|
|
448
|
+
User(1, "Alice", "alice@example.com"),
|
|
449
|
+
User(2, "Bob", "bob@example.com"),
|
|
450
|
+
User(3, "Carol", "carol@example.com")
|
|
451
|
+
)
|
|
452
|
+
val rowsAffected: Int = repo.insertBatch(users)
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
`insertBatch` and `insertAll` solve different problems: `insertBatch` uses JDBC's multi-statement batch protocol (one prepared statement, multiple `executeBatch` rounds) and returns a row count; `insertAll` constructs a single multi-row `VALUES (…), (…)` SQL statement (one round-trip) and returns the primary keys in input order. For large collections or when relying on database-generated keys, prefer `insertBatch`. For moderate-sized collections where the caller controls the keys and wants a single SQL statement, prefer `insertAll`.
|
|
456
|
+
|
|
457
|
+
#### `insertAll` — Multi-row insert returning primary keys
|
|
458
|
+
|
|
459
|
+
`Repo#insertAll` assembles a single `INSERT INTO <table> (<columns>) VALUES (?, …, ?), …, (?, …, ?)` statement covering all rows and executes it in one database round-trip. It then extracts the primary keys from the input entities via `getId` and returns them in input order.
|
|
460
|
+
|
|
461
|
+
```scala
|
|
462
|
+
class Repo[E, ID] {
|
|
463
|
+
def insertAll(rows: Seq[E])(using con: DbCon): Seq[ID]
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
Because the generated SQL grows with the number of rows, `insertAll` is best suited for moderate-sized batches where the caller controls the primary keys:
|
|
468
|
+
|
|
469
|
+
```scala
|
|
470
|
+
import zio.blocks.sql._
|
|
471
|
+
import zio.blocks.schema.Schema
|
|
472
|
+
|
|
473
|
+
case class User(id: Int, name: String, email: String)
|
|
474
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
475
|
+
|
|
476
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
477
|
+
given DbCon = ???
|
|
478
|
+
|
|
479
|
+
val newUsers = Seq(
|
|
480
|
+
User(10, "Dave", "dave@example.com"),
|
|
481
|
+
User(11, "Eve", "eve@example.com")
|
|
482
|
+
)
|
|
483
|
+
// Returns Seq(10, 11) — the IDs extracted from the input entities via getId
|
|
484
|
+
val ids: Seq[Int] = repo.insertAll(newUsers)
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
:::caution
|
|
488
|
+
`insertAll` throws `IllegalArgumentException` for an empty `Seq`. Always verify the input is non-empty before calling it, or use `insertBatch`, which accepts empty collections and returns 0 without error.
|
|
489
|
+
:::
|
|
490
|
+
|
|
491
|
+
#### `update` — Update an entity's non-ID columns
|
|
492
|
+
|
|
493
|
+
`Repo#update` executes `UPDATE <table> SET <col1> = ?, …, <colN> = ? WHERE <idColumn> = ?` for all non-ID columns of the entity, identifying the target row by its primary key. It returns the number of affected rows — 0 when no row with that ID exists.
|
|
494
|
+
|
|
495
|
+
```scala
|
|
496
|
+
class Repo[E, ID] {
|
|
497
|
+
def update(entity: E)(using con: DbCon): Int
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
Only non-ID columns appear in the `SET` clause; the ID column appears only in the `WHERE` clause, so it is never overwritten:
|
|
502
|
+
|
|
503
|
+
```scala
|
|
504
|
+
import zio.blocks.sql._
|
|
505
|
+
import zio.blocks.schema.Schema
|
|
506
|
+
|
|
507
|
+
case class User(id: Int, name: String, email: String)
|
|
508
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
509
|
+
|
|
510
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
511
|
+
given DbCon = ???
|
|
512
|
+
|
|
513
|
+
val rowsAffected: Int = repo.update(User(1, "Alice Smith", "alice.smith@example.com"))
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
:::note
|
|
517
|
+
`update` returns 0 when the entity type has only an ID column and no other updatable fields. In that case the generated `SET` clause would be empty, and `Repo` skips the statement entirely.
|
|
518
|
+
:::
|
|
519
|
+
|
|
520
|
+
#### `delete` — Delete by primary key
|
|
521
|
+
|
|
522
|
+
`Repo#delete` executes `DELETE FROM <table> WHERE <idColumn> = ?`, binding the ID through `idCodec`. It returns the number of deleted rows — 0 if no row with the given ID exists.
|
|
523
|
+
|
|
524
|
+
```scala
|
|
525
|
+
class Repo[E, ID] {
|
|
526
|
+
def delete(id: ID)(using con: DbCon): Int
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
The ID value is bound as a parameterized `?`, not interpolated into the SQL string:
|
|
531
|
+
|
|
532
|
+
```scala
|
|
533
|
+
import zio.blocks.sql._
|
|
534
|
+
import zio.blocks.schema.Schema
|
|
535
|
+
|
|
536
|
+
case class User(id: Int, name: String, email: String)
|
|
537
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
538
|
+
|
|
539
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
540
|
+
given DbCon = ???
|
|
541
|
+
|
|
542
|
+
val rowsAffected: Int = repo.delete(42)
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
To delete by an entity value rather than a bare ID, extract the key with `getId` first: `repo.delete(repo.getId(user))`.
|
|
546
|
+
|
|
547
|
+
#### `deleteAll` — Delete rows by a set of primary keys
|
|
548
|
+
|
|
549
|
+
`Repo#deleteAll` executes `DELETE FROM <table> WHERE <idColumn> IN (...)` for the given IDs in a single round-trip and returns the total number of deleted rows. It returns `0` immediately, without executing any SQL, when `ids` is empty.
|
|
550
|
+
|
|
551
|
+
```scala
|
|
552
|
+
class Repo[E, ID] {
|
|
553
|
+
def deleteAll(ids: Iterable[ID])(using con: DbCon): Int
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
Use it to batch-delete a known set of rows instead of calling `delete` in a loop:
|
|
558
|
+
|
|
559
|
+
```scala
|
|
560
|
+
import zio.blocks.sql._
|
|
561
|
+
import zio.blocks.schema.Schema
|
|
562
|
+
|
|
563
|
+
case class User(id: Int, name: String, email: String)
|
|
564
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
565
|
+
|
|
566
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
567
|
+
given DbCon = ???
|
|
568
|
+
|
|
569
|
+
val rowsAffected: Int = repo.deleteAll(List(1, 2, 3))
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### `clear` — Remove all rows
|
|
573
|
+
|
|
574
|
+
`Repo#clear` executes `DELETE FROM <table>` without a `WHERE` clause and returns the number of deleted rows.
|
|
575
|
+
|
|
576
|
+
```scala
|
|
577
|
+
class Repo[E, ID] {
|
|
578
|
+
def clear()(using con: DbCon): Int
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
Despite the different name, this method issues a `DELETE FROM` statement rather than a SQL `TRUNCATE`, so the operation participates in transactions and the returned row count is exact:
|
|
583
|
+
|
|
584
|
+
```scala
|
|
585
|
+
import zio.blocks.sql._
|
|
586
|
+
import zio.blocks.schema.Schema
|
|
587
|
+
|
|
588
|
+
case class User(id: Int, name: String, email: String)
|
|
589
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
590
|
+
|
|
591
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
592
|
+
given DbCon = ???
|
|
593
|
+
|
|
594
|
+
val rowsDeleted: Int = repo.clear()
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
:::note
|
|
598
|
+
`DELETE FROM` is transactional and precise but may be slower than `TRUNCATE` for very large tables on databases that support the `TRUNCATE` statement. When performance for bulk deletes is critical, issue a `TRUNCATE` via a custom `Frag` query instead.
|
|
599
|
+
:::
|
|
600
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: sql-dialect
|
|
3
|
+
title: "SqlDialect"
|
|
4
|
+
description: "Reference for SqlDialect, the interface for database-specific SQL type names and parameter placeholders."
|
|
5
|
+
keywords:
|
|
6
|
+
- "SQL Dialect"
|
|
7
|
+
- "PostgreSQL Dialect"
|
|
8
|
+
- "SQLite Dialect"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
`SqlDialect` tells the framework how to render SQL for a specific database. Two dialects are built in: `PostgreSQL` and `SQLite`.
|
|
12
|
+
|
|
13
|
+
## Core API
|
|
14
|
+
|
|
15
|
+
```scala
|
|
16
|
+
sealed trait SqlDialect {
|
|
17
|
+
def name: String
|
|
18
|
+
def typeName(dbValue: DbValue): String
|
|
19
|
+
def paramPlaceholder(index: Int): String
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
object SqlDialect {
|
|
23
|
+
case object PostgreSQL extends SqlDialect
|
|
24
|
+
case object SQLite extends SqlDialect
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
You can specify the dialect when creating your `Transactor`:
|
|
31
|
+
|
|
32
|
+
```scala
|
|
33
|
+
import zio.blocks.sql._
|
|
34
|
+
|
|
35
|
+
val txPostgres = JdbcTransactor.fromUrl("jdbc:postgresql://...", SqlDialect.PostgreSQL)
|
|
36
|
+
val txSqlite = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The dialect is available through `DbCon#dialect` inside a transaction:
|
|
40
|
+
|
|
41
|
+
```scala
|
|
42
|
+
import zio.blocks.sql._
|
|
43
|
+
|
|
44
|
+
val tx = JdbcTransactor.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
45
|
+
// tx: JdbcTransactor = zio.blocks.sql.JdbcTransactor@f150688
|
|
46
|
+
|
|
47
|
+
tx.connect {
|
|
48
|
+
val dialect = summon[DbCon].dialect
|
|
49
|
+
|
|
50
|
+
(
|
|
51
|
+
dialect.typeName(DbValue.DbInt(0)),
|
|
52
|
+
dialect.typeName(DbValue.DbUUID(new java.util.UUID(0L, 0L))),
|
|
53
|
+
dialect.paramPlaceholder(1)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
// res2: Tuple3[String, String, String] = ("INTEGER", "TEXT", "?")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Type Mapping
|
|
60
|
+
|
|
61
|
+
PostgreSQL and SQLite map Scala types to SQL differently:
|
|
62
|
+
|
|
63
|
+
| Type | PostgreSQL | SQLite |
|
|
64
|
+
|------------|---------------|-----------|
|
|
65
|
+
| Long | `BIGINT` | `INTEGER` |
|
|
66
|
+
| Boolean | `BOOLEAN` | `INTEGER` |
|
|
67
|
+
| Instant | `TIMESTAMPTZ` | `TEXT` |
|
|
68
|
+
| LocalDate | `DATE` | `TEXT` |
|
|
69
|
+
| UUID | `UUID` | `TEXT` |
|
|
70
|
+
| BigDecimal | `NUMERIC` | `TEXT` |
|
|
71
|
+
| Bytes | `BYTEA` | `BLOB` |
|
|
72
|
+
|
|
73
|
+
PostgreSQL uses native types; SQLite uses `INTEGER` for all integers and `TEXT` for temporal/UUID types.
|