@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,399 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: transactor-zio
|
|
3
|
+
title: "TransactorZIO"
|
|
4
|
+
description: "Reference for TransactorZIO: the ZIO-integrated JDBC transactor with blocking wrappers and interrupt-safe effect-aware connection management."
|
|
5
|
+
keywords:
|
|
6
|
+
- "ZIO SQL Integration"
|
|
7
|
+
- "ZIO Database Transactor"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
`TransactorZIO` is the ZIO-integrated database transactor from the `zio-blocks-sql-zio` module. It wraps a [`JdbcTransactor`](./transactor.md) — the synchronous JDBC transactor from the core `sql` module — and lifts its connection and transaction lifecycle management into the ZIO effect system. The module targets Scala 3 and runs on JVM only.
|
|
11
|
+
|
|
12
|
+
:::info
|
|
13
|
+
This page is a detailed reference for the `TransactorZIO` class specifically. If you only need `ZLayer`-based dependency injection for the plain synchronous `Transactor` (via `JdbcTransactor.postgresLayer` / `sqliteLayer`), see the [SQL — ZIO Integration](../sql-zio.md) guide instead — it covers both integration styles and when to reach for each.
|
|
14
|
+
:::
|
|
15
|
+
|
|
16
|
+
`TransactorZIO` exposes two pairs of methods. The **blocking wrappers** (`connect`, `transact`) run a synchronous body on ZIO's blocking thread pool via `ZIO.attemptBlocking` and return `Task[A]`. The **effect-aware methods** (`connectZIO`, `transactZIO`) accept a body that itself returns a `ZIO[R, E, A]`, bracket the JDBC connection with `ZIO.acquireRelease`, and return `ZIO[R, E | Throwable, A]` — guaranteeing connection cleanup even under fiber interruption.
|
|
17
|
+
|
|
18
|
+
- **Immutable** — `TransactorZIO` holds no mutable state; each `connect` or `transact` invocation opens a fresh connection via the provided `connectionFactory`.
|
|
19
|
+
- **Interrupt-safe** — `connectZIO` and `transactZIO` close the underlying JDBC connection even when the enclosing fiber is interrupted, because they use `ZIO.acquireRelease` internally.
|
|
20
|
+
- **Thread-safe** — the transactor is safe to share across fibers; each invocation manages its own connection.
|
|
21
|
+
- **JDBC-based** — available on JVM only; Scala 3 is required.
|
|
22
|
+
|
|
23
|
+
The structural shape of the class and its companion is:
|
|
24
|
+
|
|
25
|
+
```scala
|
|
26
|
+
class TransactorZIO(
|
|
27
|
+
connectionFactory: () => Connection,
|
|
28
|
+
val dialect: SqlDialect,
|
|
29
|
+
val logger: SqlLogger = SqlLogger.noop
|
|
30
|
+
) {
|
|
31
|
+
def connect[A](f: DbCon ?=> A): Task[A]
|
|
32
|
+
def transact[A](f: DbTx ?=> A): Task[A]
|
|
33
|
+
def connectZIO[R, E, A](f: DbCon ?=> ZIO[R, E, A]): ZIO[R, E | Throwable, A]
|
|
34
|
+
def transactZIO[R, E, A](f: DbTx ?=> ZIO[R, E, A]): ZIO[R, E | Throwable, A]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
object TransactorZIO {
|
|
38
|
+
def fromDataSource(dataSource: javax.sql.DataSource, dialect: SqlDialect): TransactorZIO
|
|
39
|
+
def fromUrl(url: String, dialect: SqlDialect): TransactorZIO
|
|
40
|
+
def fromUrl(url: String, user: String, password: String, dialect: SqlDialect): TransactorZIO
|
|
41
|
+
def layer(url: String, dialect: SqlDialect): ZLayer[Any, Nothing, TransactorZIO]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
The following example demonstrates all four execution modes — a read with `connect`, an atomic write with `transact`, an effect-aware connection with `connectZIO`, and a full transactional ZIO pipeline with `transactZIO`:
|
|
48
|
+
|
|
49
|
+
```scala
|
|
50
|
+
import zio._
|
|
51
|
+
import zio.blocks.sql._
|
|
52
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
53
|
+
import zio.blocks.schema.Schema
|
|
54
|
+
import zio.blocks.maybe.Maybe
|
|
55
|
+
|
|
56
|
+
case class User(id: Int, name: String, email: String)
|
|
57
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
58
|
+
implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
|
|
59
|
+
|
|
60
|
+
val repo = Repo.derived[User, Int]("users", "id", _.id)
|
|
61
|
+
val tx = TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
62
|
+
|
|
63
|
+
// Synchronous read — runs on blocking thread pool, no transaction overhead
|
|
64
|
+
val readAll: Task[List[User]] = tx.connect {
|
|
65
|
+
repo.all
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Atomic write — commits on success, rolls back on any failure
|
|
69
|
+
val insertUser: Task[Int] = tx.transact {
|
|
70
|
+
repo.insert(User(1, "Alice", "alice@example.com"))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Effect-aware connection — body returns ZIO; connection closes even on interruption
|
|
74
|
+
// connectZIO/transactZIO only wrap connection open/close in attemptBlocking, so the
|
|
75
|
+
// body itself must wrap each blocking JDBC call explicitly.
|
|
76
|
+
val effectRead: ZIO[Any, Throwable, List[User]] = tx.connectZIO {
|
|
77
|
+
ZIO.attemptBlocking(repo.all)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Effect-aware transaction — commits on success, rolls back on failure, interrupt-safe
|
|
81
|
+
val effectWrite: ZIO[Any, Throwable, Maybe[User]] = tx.transactZIO {
|
|
82
|
+
for {
|
|
83
|
+
_ <- ZIO.attemptBlocking(repo.insert(User(2, "Bob", "bob@example.com")))
|
|
84
|
+
user <- ZIO.attemptBlocking(repo.find(2))
|
|
85
|
+
} yield user
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ZLayer for wiring TransactorZIO through dependency injection
|
|
89
|
+
val transactorLayer: ZLayer[Any, Nothing, TransactorZIO] =
|
|
90
|
+
TransactorZIO.layer("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
Add the `zio-blocks-sql-zio` artifact to your build. It depends on `zio-blocks-sql`, so you do not need to declare both:
|
|
96
|
+
|
|
97
|
+
```scala
|
|
98
|
+
libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.51"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The artifact is JVM-only and requires Scala 3.
|
|
102
|
+
|
|
103
|
+
## Construction / Creating Instances
|
|
104
|
+
|
|
105
|
+
The `TransactorZIO` companion provides four factory methods. We use `fromDataSource` for production deployments where a connection pool is already in place, `fromUrl` for prototyping or testing, and `layer` when wiring through ZIO's dependency-injection system.
|
|
106
|
+
|
|
107
|
+
### `TransactorZIO.fromDataSource` — Create from a DataSource
|
|
108
|
+
|
|
109
|
+
`TransactorZIO.fromDataSource` is the recommended factory for production use. It calls `dataSource.getConnection()` once per `connect` or `transact` invocation, delegating all pooling concerns to the `DataSource` implementation — for example, HikariCP or c3p0:
|
|
110
|
+
|
|
111
|
+
```scala
|
|
112
|
+
object TransactorZIO {
|
|
113
|
+
def fromDataSource(dataSource: javax.sql.DataSource, dialect: SqlDialect): TransactorZIO
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The following shows how to wrap a pre-existing `DataSource` with a PostgreSQL dialect:
|
|
118
|
+
|
|
119
|
+
```scala
|
|
120
|
+
import zio.blocks.sql._
|
|
121
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
122
|
+
|
|
123
|
+
val dataSource: javax.sql.DataSource = ???
|
|
124
|
+
val tx: TransactorZIO =
|
|
125
|
+
TransactorZIO.fromDataSource(dataSource, SqlDialect.PostgreSQL)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `TransactorZIO.fromUrl` — Create from a JDBC URL
|
|
129
|
+
|
|
130
|
+
`TransactorZIO.fromUrl` creates a transactor that opens each connection via `java.sql.DriverManager.getConnection`. The overload without credentials is convenient for databases that embed authentication in the URL or for local testing:
|
|
131
|
+
|
|
132
|
+
```scala
|
|
133
|
+
object TransactorZIO {
|
|
134
|
+
def fromUrl(url: String, dialect: SqlDialect): TransactorZIO
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The following creates an in-memory SQLite transactor, a common pattern in unit tests:
|
|
139
|
+
|
|
140
|
+
```scala
|
|
141
|
+
import zio.blocks.sql._
|
|
142
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
143
|
+
|
|
144
|
+
val tx: TransactorZIO =
|
|
145
|
+
TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
:::caution
|
|
149
|
+
`fromUrl` opens a new physical connection for every `connect` or `transact` call and does no pooling. For applications with concurrent request workloads, prefer `fromDataSource` backed by a pooling `DataSource`.
|
|
150
|
+
:::
|
|
151
|
+
|
|
152
|
+
### `TransactorZIO.fromUrl` (with credentials) — Create from a JDBC URL with username and password
|
|
153
|
+
|
|
154
|
+
Use the credential-bearing overload when the database requires a separate username and password rather than URL-embedded authentication:
|
|
155
|
+
|
|
156
|
+
```scala
|
|
157
|
+
object TransactorZIO {
|
|
158
|
+
def fromUrl(url: String, user: String, password: String, dialect: SqlDialect): TransactorZIO
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The following creates a PostgreSQL transactor with explicit credentials:
|
|
163
|
+
|
|
164
|
+
```scala
|
|
165
|
+
import zio.blocks.sql._
|
|
166
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
167
|
+
|
|
168
|
+
val tx: TransactorZIO = TransactorZIO.fromUrl(
|
|
169
|
+
"jdbc:postgresql://localhost/mydb",
|
|
170
|
+
"alice",
|
|
171
|
+
"secret",
|
|
172
|
+
SqlDialect.PostgreSQL
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `TransactorZIO.layer` — Provide as a ZLayer
|
|
177
|
+
|
|
178
|
+
`TransactorZIO.layer` wraps `fromUrl` in a `ZLayer` so that `TransactorZIO` can be provided through ZIO's dependency-injection system. The layer type `ZLayer[Any, Nothing, TransactorZIO]` requires no environment and cannot fail at construction time:
|
|
179
|
+
|
|
180
|
+
```scala
|
|
181
|
+
object TransactorZIO {
|
|
182
|
+
def layer(url: String, dialect: SqlDialect): ZLayer[Any, Nothing, TransactorZIO]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The following shows how to compose the layer with a ZIO program that reads `TransactorZIO` from its environment:
|
|
187
|
+
|
|
188
|
+
```scala
|
|
189
|
+
import zio._
|
|
190
|
+
import zio.blocks.sql._
|
|
191
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
192
|
+
import zio.blocks.maybe.Maybe
|
|
193
|
+
|
|
194
|
+
val transactorLayer: ZLayer[Any, Nothing, TransactorZIO] =
|
|
195
|
+
TransactorZIO.layer("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
196
|
+
|
|
197
|
+
val program: ZIO[TransactorZIO, Throwable, Maybe[Int]] =
|
|
198
|
+
ZIO.serviceWithZIO[TransactorZIO](_.connect {
|
|
199
|
+
sql"SELECT 1".queryOne[Int]
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
val runnable: ZIO[Any, Throwable, Maybe[Int]] =
|
|
203
|
+
program.provideLayer(transactorLayer)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
When credentials are required or a `DataSource` is already available, construct the transactor with `fromUrl(url, user, password, dialect)` or `fromDataSource(ds, dialect)` and wrap the result in `ZLayer.succeed`.
|
|
207
|
+
|
|
208
|
+
## Core Operations
|
|
209
|
+
|
|
210
|
+
`TransactorZIO` exposes two pairs of methods that differ in how they handle the body and the connection lifecycle. The first pair accepts a synchronous body and delegates to the underlying `JdbcTransactor` via `ZIO.attemptBlocking`; the second pair accepts an effect-returning body and manages the connection via `ZIO.acquireRelease` for interrupt-safe cleanup.
|
|
211
|
+
|
|
212
|
+
### Connection Management
|
|
213
|
+
|
|
214
|
+
The `connect` method acquires a JDBC connection, runs a synchronous body on ZIO's blocking thread pool, and closes the connection when the body returns. Use it for read-only queries or any non-transactional access that does not require ZIO effects inside the body.
|
|
215
|
+
|
|
216
|
+
#### `connect` — Execute a synchronous body within a connection
|
|
217
|
+
|
|
218
|
+
`TransactorZIO#connect` wraps the underlying `JdbcTransactor#connect` call in `ZIO.attemptBlocking`, moving the blocking JDBC work off the ZIO main thread pool. The body receives a [`DbCon`](./db-con.md) Scala 3 context parameter carrying the active `DbConnection`, `SqlDialect`, and `SqlLogger`:
|
|
219
|
+
|
|
220
|
+
```scala
|
|
221
|
+
class TransactorZIO {
|
|
222
|
+
def connect[A](f: DbCon ?=> A): Task[A]
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Inside the block, `DbCon` is available as a given, so every [`Frag`](./frag.md) execution method and every [`Repo`](./repo.md) CRUD operation works without any explicit argument passing:
|
|
227
|
+
|
|
228
|
+
```scala
|
|
229
|
+
import zio._
|
|
230
|
+
import zio.blocks.sql._
|
|
231
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
232
|
+
import zio.blocks.schema.Schema
|
|
233
|
+
|
|
234
|
+
case class User(id: Int, name: String, email: String)
|
|
235
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
236
|
+
implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
|
|
237
|
+
|
|
238
|
+
val repo: Repo[User, Int] = Repo.derived[User, Int]("users", "id", _.id)
|
|
239
|
+
val tx: TransactorZIO = TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
240
|
+
|
|
241
|
+
// DbCon is synthesized by connect and threaded into all automatically
|
|
242
|
+
val users: Task[List[User]] = tx.connect {
|
|
243
|
+
repo.all
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
:::caution
|
|
248
|
+
Inside `connect`, auto-commit is left at its driver default (typically `true`). Any DML executed here is committed immediately. Use `transact` when you need atomic multi-statement writes.
|
|
249
|
+
:::
|
|
250
|
+
|
|
251
|
+
### Transaction Management
|
|
252
|
+
|
|
253
|
+
The `transact` method acquires a JDBC connection, disables auto-commit, runs a synchronous body on ZIO's blocking thread pool, and commits or rolls back before closing the connection. Use it whenever writes must succeed or fail as a single atomic unit.
|
|
254
|
+
|
|
255
|
+
#### `transact` — Execute a synchronous body within a transaction
|
|
256
|
+
|
|
257
|
+
`TransactorZIO#transact` wraps the underlying `JdbcTransactor#transact` call in `ZIO.attemptBlocking`. Auto-commit is disabled before the body runs; on a normal return the connection is committed, and on any exception it is rolled back. The connection is always closed in a `finally` block regardless of outcome. The body receives a [`DbTx`](./db-tx.md) Scala 3 context parameter:
|
|
258
|
+
|
|
259
|
+
```scala
|
|
260
|
+
class TransactorZIO {
|
|
261
|
+
def transact[A](f: DbTx ?=> A): Task[A]
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Because `DbTx` extends `DbCon`, any method that works inside `connect` also works inside `transact` without modification — we can promote code to transactional scope at the call site without changing helper signatures:
|
|
266
|
+
|
|
267
|
+
```scala
|
|
268
|
+
import zio._
|
|
269
|
+
import zio.blocks.sql._
|
|
270
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
271
|
+
import zio.blocks.schema.Schema
|
|
272
|
+
import zio.blocks.maybe.Maybe
|
|
273
|
+
|
|
274
|
+
case class User(id: Int, name: String, email: String)
|
|
275
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
276
|
+
implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
|
|
277
|
+
|
|
278
|
+
val repo: Repo[User, Int] = Repo.derived[User, Int]("users", "id", _.id)
|
|
279
|
+
val tx: TransactorZIO = TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
280
|
+
|
|
281
|
+
// Both statements run in one transaction; failure in either rolls back the whole block
|
|
282
|
+
val result: Task[Maybe[User]] = tx.transact {
|
|
283
|
+
repo.insert(User(1, "Alice", "alice@example.com"))
|
|
284
|
+
repo.find(1)
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Effect-Aware Connection Management
|
|
289
|
+
|
|
290
|
+
The `connectZIO` method acquires a JDBC connection via `ZIO.acquireRelease`, runs an effect-returning body with `DbCon` in scope, and guarantees the connection is closed when the scope exits — including on fiber interruption. Use it when the body needs to interleave SQL queries with other ZIO operations such as logging, concurrency, or calls to external services.
|
|
291
|
+
|
|
292
|
+
#### `connectZIO` — Execute an effect-returning body within a connection
|
|
293
|
+
|
|
294
|
+
`TransactorZIO#connectZIO` accepts a body of type `DbCon ?=> ZIO[R, E, A]` and returns `ZIO[R, E | Throwable, A]`. The error channel widens to `E | Throwable` because the connection-acquisition step — a blocking JDBC call — can throw `Throwable` independently of the body's declared error type:
|
|
295
|
+
|
|
296
|
+
```scala
|
|
297
|
+
class TransactorZIO {
|
|
298
|
+
def connectZIO[R, E, A](f: DbCon ?=> ZIO[R, E, A]): ZIO[R, E | Throwable, A]
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Inside the body, `DbCon` is available as a given. `connectZIO` only wraps the connection open/close steps in `ZIO.attemptBlocking` — the body itself must wrap each blocking JDBC call the same way to avoid blocking a ZIO compute thread:
|
|
303
|
+
|
|
304
|
+
```scala
|
|
305
|
+
import zio._
|
|
306
|
+
import zio.blocks.sql._
|
|
307
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
308
|
+
import zio.blocks.schema.Schema
|
|
309
|
+
|
|
310
|
+
case class User(id: Int, name: String, email: String)
|
|
311
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
312
|
+
implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
|
|
313
|
+
|
|
314
|
+
val repo: Repo[User, Int] = Repo.derived[User, Int]("users", "id", _.id)
|
|
315
|
+
val tx: TransactorZIO = TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
316
|
+
|
|
317
|
+
// The body returns a ZIO; the connection remains open for the full duration of the effect
|
|
318
|
+
val result: ZIO[Any, Throwable, List[User]] = tx.connectZIO {
|
|
319
|
+
for {
|
|
320
|
+
users <- ZIO.attemptBlocking(repo.all)
|
|
321
|
+
_ <- ZIO.logInfo(s"Fetched ${users.size} users from the database")
|
|
322
|
+
} yield users
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Unlike `connect`, the connection lifecycle here spans the entire ZIO scope. If the fiber is interrupted after the connection is opened but before the body completes, the connection is still released because `ZIO.acquireRelease` guarantees the release action runs regardless of how the scope exits.
|
|
327
|
+
|
|
328
|
+
### Effect-Aware Transaction Management
|
|
329
|
+
|
|
330
|
+
The `transactZIO` method brackets a full JDBC transaction with `ZIO.acquireRelease`: it disables auto-commit, runs an effect-returning body with `DbTx` in scope, commits on success, rolls back on failure, and closes the connection. This is the recommended method for transactional ZIO workflows.
|
|
331
|
+
|
|
332
|
+
#### `transactZIO` — Execute an effect-returning body within a transaction
|
|
333
|
+
|
|
334
|
+
`TransactorZIO#transactZIO` accepts a body of type `DbTx ?=> ZIO[R, E, A]` and returns `ZIO[R, E | Throwable, A]`. Auto-commit is disabled in the acquire step and restored to its original value in the release step. The body's exit value drives the commit/rollback decision:
|
|
335
|
+
|
|
336
|
+
```scala
|
|
337
|
+
class TransactorZIO {
|
|
338
|
+
def transactZIO[R, E, A](f: DbTx ?=> ZIO[R, E, A]): ZIO[R, E | Throwable, A]
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
On success the connection is committed; on failure — whether from an error, a defect, or fiber interruption — the transaction is rolled back before the connection is closed. As with `connectZIO`, wrap each blocking JDBC call in the body with `ZIO.attemptBlocking`:
|
|
343
|
+
|
|
344
|
+
```scala
|
|
345
|
+
import zio._
|
|
346
|
+
import zio.blocks.sql._
|
|
347
|
+
import zio.blocks.sql.zio.TransactorZIO
|
|
348
|
+
import zio.blocks.schema.Schema
|
|
349
|
+
import zio.blocks.maybe.Maybe
|
|
350
|
+
|
|
351
|
+
case class User(id: Int, name: String, email: String)
|
|
352
|
+
object User { implicit val schema: Schema[User] = Schema.derived }
|
|
353
|
+
implicit val userCodec: DbCodec[User] = User.schema.deriving(DbCodecDeriver).derive
|
|
354
|
+
|
|
355
|
+
val repo: Repo[User, Int] = Repo.derived[User, Int]("users", "id", _.id)
|
|
356
|
+
val tx: TransactorZIO = TransactorZIO.fromUrl("jdbc:sqlite::memory:", SqlDialect.SQLite)
|
|
357
|
+
|
|
358
|
+
// The ZIO for-comprehension runs within a single JDBC transaction
|
|
359
|
+
val result: ZIO[Any, Throwable, Maybe[User]] = tx.transactZIO {
|
|
360
|
+
for {
|
|
361
|
+
_ <- ZIO.attemptBlocking(repo.insert(User(1, "Alice", "alice@example.com")))
|
|
362
|
+
user <- ZIO.attemptBlocking(repo.find(1))
|
|
363
|
+
_ <- ZIO.logInfo("Insert and fetch completed atomically")
|
|
364
|
+
} yield user
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
:::caution
|
|
369
|
+
If `commit` itself fails after a successful body, a rollback is attempted. If both `commit` and `rollback` fail, the rollback error is suppressed onto the commit error (via `addSuppressed`), and the commit error is the one that propagates. The body's return value is discarded in either failure case.
|
|
370
|
+
:::
|
|
371
|
+
|
|
372
|
+
## Comparison: `TransactorZIO` vs `JdbcTransactor`
|
|
373
|
+
|
|
374
|
+
[`JdbcTransactor`](./transactor.md) is the synchronous, non-ZIO transactor from the `sql` module. `TransactorZIO` wraps it and adds a ZIO integration layer on top. The table below summarizes when to use each:
|
|
375
|
+
|
|
376
|
+
| Aspect | `JdbcTransactor` (`sql` module) | `TransactorZIO` (`sql-zio` module) |
|
|
377
|
+
|---------------------------|------------------------------------------------------|-------------------------------------------------------------------------------------|
|
|
378
|
+
| **Return type** | `A` (synchronous, blocking) | `Task[A]` or `ZIO[R, E \| Throwable, A]` |
|
|
379
|
+
| **Thread model** | Blocks the calling thread directly | `connect`/`transact` run on ZIO's blocking thread pool via `ZIO.attemptBlocking` |
|
|
380
|
+
| **Interruption safety** | None — body runs to completion | `connectZIO`/`transactZIO` close the connection on fiber interruption |
|
|
381
|
+
| **ZIO dependency** | None — zero ZIO runtime dependency | Requires ZIO runtime |
|
|
382
|
+
| **Dependency injection** | Manual construction | `TransactorZIO.layer` provides a `ZLayer[Any, Nothing, TransactorZIO]` |
|
|
383
|
+
| **Scala versions** | Scala 3 only (the `sql` module is cross-built JVM + JS, but `JdbcTransactor` itself is JVM-only) | Scala 3 only (JVM only) |
|
|
384
|
+
| **When to use** | Synchronous code, scripts, or non-ZIO applications | ZIO-based applications where effects compose across the entire call stack |
|
|
385
|
+
|
|
386
|
+
The following diagram shows how the two types relate within the `sql` and `sql-zio` modules:
|
|
387
|
+
|
|
388
|
+
```
|
|
389
|
+
sql module sql-zio module
|
|
390
|
+
────────────────────────────── ───────────────────────────────────────────────────
|
|
391
|
+
JdbcTransactor TransactorZIO
|
|
392
|
+
connect { DbCon ?=> A }: A ◄── connect { DbCon ?=> A }: Task[A]
|
|
393
|
+
transact { DbTx ?=> A }: A ◄── transact { DbTx ?=> A }: Task[A]
|
|
394
|
+
(no ZIO dep) connectZIO { DbCon ?=> ZIO[R,E,A] }
|
|
395
|
+
transactZIO { DbTx ?=> ZIO[R,E,A] }
|
|
396
|
+
layer(url, dialect): ZLayer[Any, Nothing, TransactorZIO]
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`TransactorZIO` holds a private `JdbcTransactor` field and delegates the synchronous `connect` and `transact` calls to it, wrapping each in `ZIO.attemptBlocking`. The effect-aware `connectZIO` and `transactZIO` bypass `JdbcTransactor` entirely and manage the connection lifecycle with `ZIO.acquireRelease` directly.
|