Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ Framework integration points are now instance-scoped (#198), and the Ktor integr
- `OtelDatabaseObservationConvention` (storm-micrometer): opt-in OpenTelemetry database client semantic conventions on query observations — `db.system.name` (derived from the JDBC URL), `db.operation.name`, and `db.query.text` (placeholders only) alongside the `storm.*` key values, so Storm queries surface in the database tooling of OTLP-capable observability backends. Activated with `storm.observations.semantic-conventions=otel` in the Spring Boot starters (database product detected from the DataSource) or by registering the convention in the Ktor dependency container.
- Trace context in SQL statements: the `SqlCommenter` hook (template builder, all language stacks) appends per-execution comment content to statements after all processing and caching; `TraceContextSqlCommenter` (storm-micrometer) renders the current span as a sqlcommenter-style W3C `traceparent` comment, correlating database-side diagnostics such as slow query logs with traces. Opt-in everywhere — `storm.tracing.sql-comments=true` in the Spring Boot starters (with a `Tracer` present), the `sqlCommenter` slot in the Ktor plugin — since a per-execution comment defeats prepared statement caching. Hostile content is rejected (comment terminator, semicolons), and the emitted comment is padded so MySQL/MariaDB executable-comment and optimizer-hint markers are never interpreted.
- Transactions are observed alongside queries: every physical transaction (outermost block or `REQUIRES_NEW`, blocking, suspend, and Spring-bridged alike) reports as a `storm.transaction` Micrometer Observation with duration, outcome (`committed`/`rolled_back`), propagation, and read-only key values; joined blocks are not double-counted. The `QueryObserver` SPI gains a default no-op transaction hook, so existing observers and wiring are unaffected.
- `@DataStormTest` provides an embedded test database on Spring Boot 4 as well: a fallback in the slice activates when Boot's own test-database replacement (relocated to spring-boot-jdbc-test-autoconfigure in Boot 4) is absent, so the slice behaves identically on both generations; `spring.test.database.replace=none` opts out for Testcontainers setups.
- Trace-context SQL comments gain a sampled-only mode: `TraceContextSqlCommenter(tracer, onlySampled)` and `storm.tracing.sql-comments=sampled` comment only statements of sampled traces, aligning the prepared-statement-caching cost with the correlation benefit; unknown property values fail fast.
- `@EnableStormRepositories(basePackages = ...)` (storm-spring, usable from both language stacks): switches on repository scanning in plain Spring applications, mirroring `@EnableJpaRepositories`; without packages, the annotated class's package is scanned. In Spring Boot the annotation doubles as the explicit override: the auto-configured scanning backs off. The `RepositoryBeanFactoryPostProcessor` adapters also gain configuring constructors (base packages, template bean name, prefix), so multi-template applications define one bean per repository set instead of one subclass.
- `@DataStormTest` test slice in the new storm-spring-boot-test-autoconfigure module, the counterpart of `@DataJpaTest`: starts only the `DataSource`, Storm's auto-configuration, and SQL initialization (plus Flyway/Liquibase when present), keeps regular components out, replaces the data source with an embedded database on Spring Boot 3 (opt out via `spring.test.database.replace=none` for Testcontainers `@ServiceConnection` setups), and rolls back a per-test transaction. One module serves both starters and both Spring Boot 3 and 4: the slice imports the starters' identically named auto-configuration classes, lists relocated platform classes as optional imports, and registers its type exclusion through a context customizer instead of the relocated `@TypeExcludeFilters`.

Expand Down Expand Up @@ -67,6 +69,11 @@ Framework integration points are now instance-scoped (#198), and the Ktor integr
- `st.orm.spring.impl.ResolverRegistration` (both language stacks): the autowire-candidate resolver is installed by the scanning engine itself.
- `storm-ktor-koin`: Ktor's built-in dependency injection is the supported DI path. Koin users keep full capability with a few lines of application code; the Ktor integration docs include the recipe.

### Security

- Schema validation reads database metadata through bound parameters instead of building the metadata queries with escaped string literals (storm-core `DatabaseSchema`). The catalog and schema names are database-supplied and were already correctly escaped, so this is defense in depth: no metadata query is assembled from concatenated values.
- The kotlinx-serialization module validates that a `Ref` target resolves to a Storm `Data` type before loading it, and loads it without running static initializers until that check passes. A serializer that reported an unrelated class name can no longer trigger that class's initialization.

## [1.12.0] - 2026-07-06

Feature release centered on the reified Kotlin query API. Breaking changes are accepted with no deprecation shims (1.12 policy).
Expand Down
2 changes: 1 addition & 1 deletion docs/ktor-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ dependencies {
}
```

With tracing in place, the `sqlCommenter` slot appends the current trace context to every statement as a sqlcommenter-style comment, correlating database-side diagnostics such as slow query logs back to the trace. Opt-in: a per-execution comment defeats prepared statement caching.
With tracing in place, the `sqlCommenter` slot appends the current trace context to statements as a sqlcommenter-style comment, correlating database-side diagnostics such as slow query logs back to the trace. Opt-in: a per-execution comment defeats prepared statement caching; `TraceContextSqlCommenter(tracer, onlySampled = true)` limits the comments to sampled traces.

```kotlin
install(Storm) {
Expand Down
4 changes: 4 additions & 0 deletions docs/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ An unloaded ref serializes as a bare value because there is nothing more to conv

A loaded entity ref wraps the full entity data in an `@entity` object. This tells the deserializer that the enclosed data is a complete entity, from which it can reconstruct a loaded ref with `getOrNull()` returning the entity instance.

:::warning Data exposure
Serialization never triggers a fetch, but a ref that is already loaded (for example because a query joined it) serializes its full entity, including every field of the referenced type. When the response shape matters, such as a public REST API, return a projection that exposes only the intended fields rather than an entity whose refs may carry more than the endpoint should reveal. Serialization reflects exactly what is loaded; it does not filter.
:::

A loaded projection ref uses a different wrapper (`@projection`) and includes a separate `@id` field. The explicit ID is necessary because projections are partial views of an entity and may not expose an `id()` accessor. Without the separate `@id` field, the deserializer would have no reliable way to recover the primary key.

Both Jackson and kotlinx.serialization produce identical JSON for the same ref state, so output from one library can be consumed by the other.
Expand Down
8 changes: 5 additions & 3 deletions docs/spring-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -636,13 +636,15 @@ storm:

Customization follows the usual back-off rules: contribute an `ObservationConvention<StormQueryObservationContext>` bean to override the naming and key values (it wins over the property), define your own `QueryObserver` bean to replace the binding entirely, or disable the observation at the registry level with `management.observations.enable.storm.query=false`.

With tracing in place, `storm.tracing.sql-comments: true` additionally appends the current trace context to every statement as a sqlcommenter-style comment (`/*traceparent='00-…'*/`), so database-side diagnostics — MariaDB's slow query log, `pg_stat_activity` — correlate directly back to the trace that issued the statement. This is deliberately opt-in: a per-execution comment changes the statement text on every call, which defeats driver-side and server-side prepared statement caching. Enable it where the correlation is worth that trade-off.
With tracing in place, `storm.tracing.sql-comments` additionally appends the current trace context to statements as a sqlcommenter-style comment (`/*traceparent='00-…'*/`), so database-side diagnostics — MariaDB's slow query log, `pg_stat_activity` — correlate directly back to the trace that issued the statement. `true` comments every statement inside a span; `sampled` comments only statements of sampled traces, which is the recommended mode when the sampling probability is below 1.0. This is deliberately opt-in: a per-execution comment changes the statement text on every call, which defeats driver-side and server-side prepared statement caching.

One composition warning for applications that wire the integration beans themselves: define beans like the `ExceptionMapper`, `QueryObserver`, or `SqlCommenter` unconditionally in your `@Configuration` classes. A `@ConditionalOnBean` condition in a user configuration evaluates before auto-configurations contribute their beans (such as the `Tracer`), so it fails silently and the integration stays dormant while looking enabled.

## Testing with @DataStormTest

`@DataStormTest` (from `storm-spring-boot-test-autoconfigure`, test scope) is the Storm test slice, the counterpart of `@DataJpaTest`: it starts only the `DataSource`, Storm's auto-configuration (template, repository scanning, transaction integration, schema validation, exception translation), and SQL initialization, plus Flyway or Liquibase when present. It complements [`@StormTest`](testing.md), which tests data logic without a Spring context: use `@StormTest` for fast query-level tests, and the slice when the test should see what production Spring code sees — injected repository beans, translated exceptions, Spring-managed transactions, and your `storm.*` configuration. Regular `@Component`, `@Service`, and `@Controller` beans stay out of the context. Each test method runs in a transaction that is rolled back afterwards, so tests cannot see each other's writes.

On Spring Boot 3 the application's `DataSource` is replaced with an embedded in-memory database by default; put a `schema.sql` (and optionally `data.sql`) on the test classpath to initialize it, or let Flyway or Liquibase (included in the slice) create the schema as in production. On Spring Boot 4 the replacement activates when the `spring-boot-jdbc-test-autoconfigure` artifact is present; without it, point the slice at a database with the `properties` attribute:
The application's `DataSource` is replaced with an embedded in-memory database by default, on Spring Boot 3 and 4 alike (the slice ships a fallback for Boot 4's relocated test-database support); put a `schema.sql` (and optionally `data.sql`) on the test classpath to initialize it, or let Flyway or Liquibase (included in the slice) create the schema as in production:

```kotlin
@DataStormTest
Expand All @@ -662,7 +664,7 @@ class VisitRepositoryTest(
}
```

To run against a real database instead, disable the replacement (`spring.test.database.replace=none` on Spring Boot 3; the default without the test-database artifact on Spring Boot 4) and hand the slice a Testcontainers-managed database through `@ServiceConnection`:
To run against a real database instead, disable the replacement with `spring.test.database.replace=none` and hand the slice a Testcontainers-managed database through `@ServiceConnection`:

```kotlin
@DataStormTest(properties = ["spring.test.database.replace=none"])
Expand Down
Loading
Loading