@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.
Files changed (150) hide show
  1. package/guides/compile-time-resource-safety-with-scope.md +16 -17
  2. package/guides/getting-started-with-mux.md +1507 -0
  3. package/guides/query-dsl-extending.md +161 -102
  4. package/guides/query-dsl-fluent-builder.md +217 -157
  5. package/guides/query-dsl-reified-optics.md +12 -10
  6. package/guides/query-dsl-sql.md +246 -165
  7. package/guides/telemetry-guide.md +1069 -0
  8. package/guides/zio-schema-migration.md +29 -22
  9. package/index.md +292 -50
  10. package/package.json +1 -1
  11. package/plans/config-follow-up-prs.md +188 -0
  12. package/plans/config-pr-assessment-roadmap.md +310 -0
  13. package/reference/MuxDataFlow.jsx +250 -0
  14. package/reference/async.md +651 -0
  15. package/reference/chunk.md +3533 -308
  16. package/reference/codegen/case-class.md +436 -0
  17. package/reference/codegen/emitter-config.md +383 -0
  18. package/reference/codegen/examples.md +664 -0
  19. package/reference/codegen/field.md +316 -0
  20. package/reference/codegen/index.md +317 -0
  21. package/reference/codegen/scala-emitter.md +392 -0
  22. package/reference/codegen/scala-file.md +276 -0
  23. package/reference/codegen/sealed-trait.md +408 -0
  24. package/reference/codegen/type-definition.md +340 -0
  25. package/reference/codegen/type-ref.md +201 -0
  26. package/reference/combinators.md +347 -117
  27. package/reference/config.md +158 -0
  28. package/reference/context.md +4 -4
  29. package/reference/datastar.md +346 -0
  30. package/reference/docs.md +1461 -345
  31. package/reference/endpoint/auth-type.md +146 -0
  32. package/reference/endpoint/endpoint.md +297 -0
  33. package/reference/endpoint/http-codec.md +249 -0
  34. package/reference/endpoint/index.md +825 -0
  35. package/reference/endpoint/path-codec.md +237 -0
  36. package/reference/endpoint/route-pattern.md +196 -0
  37. package/reference/endpoint/route-tree.md +111 -0
  38. package/reference/endpoint/segment-codec.md +212 -0
  39. package/reference/html.md +1120 -0
  40. package/reference/htmx/attribute-values.md +359 -0
  41. package/reference/htmx/hx-encoding.md +111 -0
  42. package/reference/htmx/hx-params.md +204 -0
  43. package/reference/htmx/hx-swap.md +276 -0
  44. package/reference/htmx/hx-sync.md +251 -0
  45. package/reference/htmx/hx-target.md +314 -0
  46. package/reference/htmx/hx-trigger.md +457 -0
  47. package/reference/htmx/hx-url-update.md +239 -0
  48. package/reference/htmx/index.md +855 -0
  49. package/reference/http-model/index.md +47 -0
  50. package/reference/http-model/model.md +1481 -0
  51. package/reference/http-model/schema.md +747 -0
  52. package/reference/maybe.md +826 -0
  53. package/reference/media-type.md +2 -2
  54. package/reference/mux.mdx +823 -0
  55. package/reference/openapi.md +1351 -0
  56. package/reference/resource-management/defer-handle.md +1 -1
  57. package/reference/resource-management/resource.md +31 -2
  58. package/reference/resource-management/scope.md +28 -12
  59. package/reference/resource-management/wire.md +3 -7
  60. package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
  61. package/reference/ringbuffer/MpscDiagram.jsx +618 -0
  62. package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
  63. package/reference/ringbuffer/SpscDiagram.jsx +677 -0
  64. package/reference/ringbuffer/advanced.mdx +109 -0
  65. package/reference/ringbuffer/index.mdx +145 -0
  66. package/reference/ringbuffer/mpmc.mdx +151 -0
  67. package/reference/ringbuffer/mpsc.mdx +132 -0
  68. package/reference/ringbuffer/spmc.mdx +108 -0
  69. package/reference/ringbuffer/spsc.mdx +344 -0
  70. package/reference/{allows.md → schema/allows.md} +4 -4
  71. package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
  72. package/reference/{binding.md → schema/binding.md} +2 -3
  73. package/reference/schema/built-in-codecs/avro.md +451 -0
  74. package/reference/schema/built-in-codecs/bson.md +480 -0
  75. package/reference/schema/built-in-codecs/csv.md +564 -0
  76. package/reference/schema/built-in-codecs/index.md +77 -0
  77. package/reference/schema/built-in-codecs/json/index.md +295 -0
  78. package/reference/schema/built-in-codecs/json/json-config.md +217 -0
  79. package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
  80. package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
  81. package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
  82. package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
  83. package/reference/schema/built-in-codecs/messagepack.md +508 -0
  84. package/reference/schema/built-in-codecs/thrift.md +433 -0
  85. package/reference/schema/built-in-codecs/toon.md +1078 -0
  86. package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
  87. package/reference/schema/built-in-codecs/yaml.md +552 -0
  88. package/reference/{codec.md → schema/codec.md} +10 -10
  89. package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
  90. package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
  91. package/reference/schema/format.md +92 -0
  92. package/reference/schema/index.md +50 -0
  93. package/reference/schema/migration.md +297 -0
  94. package/reference/{modifier.md → schema/modifier.md} +58 -7
  95. package/reference/{optics.md → schema/optics.md} +2 -2
  96. package/reference/{patch.md → schema/patch.md} +1 -1
  97. package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
  98. package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
  99. package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
  100. package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
  101. package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
  102. package/reference/{schema.md → schema/schema.md} +12 -0
  103. package/reference/{structural-types.md → schema/structural-types.md} +1 -1
  104. package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
  105. package/reference/smithy.md +533 -0
  106. package/reference/sql/db-codec-deriver.md +71 -0
  107. package/reference/sql/db-codec.md +687 -0
  108. package/reference/sql/db-con.md +271 -0
  109. package/reference/sql/db-connection.md +153 -0
  110. package/reference/sql/db-param-writer.md +77 -0
  111. package/reference/sql/db-param.md +66 -0
  112. package/reference/sql/db-result-reader.md +146 -0
  113. package/reference/sql/db-tx.md +82 -0
  114. package/reference/sql/db-value.md +41 -0
  115. package/reference/sql/ddl.md +85 -0
  116. package/reference/sql/frag.md +254 -0
  117. package/reference/sql/index.md +341 -0
  118. package/reference/sql/repo.md +600 -0
  119. package/reference/sql/sql-dialect.md +73 -0
  120. package/reference/sql/sql-logger.md +62 -0
  121. package/reference/sql/sql-name-mapper.md +70 -0
  122. package/reference/sql/table-metadata.md +134 -0
  123. package/reference/sql/table.md +448 -0
  124. package/reference/sql/transactor-zio.md +399 -0
  125. package/reference/sql/transactor.md +353 -0
  126. package/reference/sql-zio.md +112 -0
  127. package/reference/streams/concurrent-operators.md +106 -0
  128. package/reference/streams/index.md +653 -0
  129. package/reference/streams/pipeline.md +718 -0
  130. package/reference/streams/reader.md +1284 -0
  131. package/reference/streams/scala-2-compatibility.md +55 -0
  132. package/reference/streams/sink.md +1426 -0
  133. package/reference/streams/stream.md +2526 -0
  134. package/reference/streams/writer.md +1045 -0
  135. package/reference/streams/zero-boxing.md +275 -0
  136. package/reference/telemetry.md +693 -0
  137. package/reference/typeid.md +5 -19
  138. package/sidebars.js +238 -43
  139. package/reference/formats.md +0 -694
  140. package/reference/http-model.md +0 -1716
  141. package/reference/streams.md +0 -989
  142. package/ringbuffer.md +0 -249
  143. /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
  144. /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
  145. /package/reference/{lazy.md → schema/lazy.md} +0 -0
  146. /package/reference/{reflect.md → schema/reflect.md} +0 -0
  147. /package/reference/{registers.md → schema/registers.md} +0 -0
  148. /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
  149. /package/reference/{syntax.md → schema/syntax.md} +0 -0
  150. /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.