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
3 changes: 2 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ jobs:
done

- name: Generate llms-full.txt
run: bash website/scripts/generate-llms-full.sh
run: bash scripts/generate-llms-full.sh
working-directory: website

- name: Build Docusaurus
run: npm run build
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ build/
.DS_Store

### Maven Flatten Plugin ###
.flattened-pom.xml
**/.flattened-pom.xml

### Docusaurus ###
website/node_modules/
Expand Down
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

**Storm** is a modern, high-performance ORM for Kotlin 2.0+ and Java 21+, built around a powerful SQL template engine. It focuses on simplicity, type safety, and predictable performance through immutable models and compile-time metadata.

**Key benefits:**
**Core ORM benefits:**

- **Minimal code**: Define entities with simple records/data classes and query with concise, readable syntax; no boilerplate.
- **Parameterized by default**: String interpolations are automatically converted to bind variables, making queries SQL injection safe by design.
Expand All @@ -21,6 +21,17 @@
- **Performance**: Template caching, transaction-scoped entity caching, and zero-overhead dirty checking (thanks to immutability) ensure efficient database interactions. Batch processing, lazy streams, and upserts are built in.
- **Universal Database Compatibility**: Fully compatible with all SQL databases, it offers flexibility and broad applicability across various database systems.

Storm also includes an AI-assisted workflow for database development. It gives AI tools full schema awareness through a local MCP server, guides them with Storm-specific skills, and closes the loop with automated tests. The AI generates code, but the final checks are done by running code against the schema and captured SQL, not by trusting LLM reasoning.

To set up the AI workflow in your project:

```bash
npm install -g @storm-orm/cli
storm init
```

This installs Storm's rules, skills, and optional local MCP setup for supported AI coding tools. See [AI-Assisted Development](docs/ai.md) for the full workflow.

## Why Storm?

Storm draws inspiration from established ORMs such as Hibernate, but is built from scratch around a clear design philosophy: capturing exactly what you want to do using the minimum amount of code, optimized for Kotlin and modern Java.
Expand All @@ -42,6 +53,25 @@ Storm embraces SQL rather than abstracting it away. It simplifies database inter

**Storm is ideal for** developers who understand that the best solutions emerge when object model and database model work in harmony. If you value a database-first approach where records naturally mirror your schema, Storm is built for you. Custom mappings are supported when needed, but the real elegance comes from alignment, not abstraction.

## AI Workflow

AI tools can write a lot of database code quickly, but subtle mistakes are still common: wrong joins, missing constraints, stale schema assumptions, or queries that compile but do the wrong thing.

Storm addresses that in two ways.

First, it improves generation quality. A local MCP server gives the AI full schema awareness without exposing credentials or data, and Storm skills teach the AI how to create entities, queries, repositories, and migrations that follow Storm's conventions.

Second, it verifies the result with automated tests. Storm can validate that generated entities still match the schema and that generated queries behave as intended. These checks run in unit tests, so the final gate is actual code execution rather than model self-evaluation.

The workflow is simple:

1. You prompt the AI.
2. The AI uses Storm skills and local schema context to generate code.
3. Storm verifies the generated entities and queries in tests.
4. You review the result and keep moving with more confidence.

This is the core idea: AI generates database code, and Storm closes the loop with context and verification.

## Choose Your Language

Both Kotlin and Java support SQL Templates for powerful query composition. Kotlin additionally provides a type-safe DSL with infix operators for a more idiomatic experience.
Expand Down Expand Up @@ -143,7 +173,7 @@ Storm provides a Bill of Materials (BOM) for centralized version management. Imp
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-bom</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -155,7 +185,7 @@ Storm provides a Bill of Materials (BOM) for centralized version management. Imp

```kotlin
dependencies {
implementation(platform("st.orm:storm-bom:1.11.0"))
implementation(platform("st.orm:storm-bom:@@STORM_VERSION@@"))
}
```

Expand All @@ -165,7 +195,7 @@ With the BOM imported, add Storm modules without specifying versions:

```kotlin
dependencies {
implementation(platform("st.orm:storm-bom:1.11.0"))
implementation(platform("st.orm:storm-bom:@@STORM_VERSION@@"))
implementation("st.orm:storm-kotlin")
runtimeOnly("st.orm:storm-core")
// Use storm-compiler-plugin-2.0 for Kotlin 2.0.x, -2.1 for 2.1.x, etc.
Expand Down
8 changes: 4 additions & 4 deletions docs/api-java.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The main Java API module. It provides the `ORMTemplate` entry point, repository
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-java21</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
</dependency>
```

Expand Down Expand Up @@ -44,7 +44,7 @@ Spring Framework integration for Java. Provides `RepositoryBeanFactoryPostProces
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-spring</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
</dependency>
```

Expand All @@ -58,7 +58,7 @@ Spring Boot auto-configuration for Java. Automatically creates an `ORMTemplate`
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-spring-boot-starter</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
</dependency>
```

Expand All @@ -83,7 +83,7 @@ The `storm-metamodel-processor` annotation processor generates type-safe metamod
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-metamodel-processor</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
<scope>provided</scope>
</dependency>
```
Expand Down
12 changes: 6 additions & 6 deletions docs/api-kotlin.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ The main Kotlin API module. It provides the `ORMTemplate` interface, extension f

```kotlin
// Gradle (Kotlin DSL)
implementation("st.orm:storm-kotlin:1.11.0")
implementation("st.orm:storm-kotlin:@@STORM_VERSION@@")
```

```xml
<!-- Maven -->
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-kotlin</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
</dependency>
```

Expand All @@ -33,7 +33,7 @@ The Kotlin API does not depend on any preview features. All APIs are stable and
Spring Framework integration for Kotlin. Provides `RepositoryBeanFactoryPostProcessor` for repository auto-discovery and injection, `@EnableTransactionIntegration` for bridging Storm's programmatic transactions with Spring's `@Transactional`, and transaction-aware coroutine support. Add this module when you use Spring Framework without Spring Boot.

```kotlin
implementation("st.orm:storm-kotlin-spring:1.11.0")
implementation("st.orm:storm-kotlin-spring:@@STORM_VERSION@@")
```

See [Spring Integration](spring-integration.md) for configuration details.
Expand All @@ -43,7 +43,7 @@ See [Spring Integration](spring-integration.md) for configuration details.
Spring Boot auto-configuration for Kotlin. Automatically creates an `ORMTemplate` bean from the `DataSource`, discovers repositories, enables transaction integration, and binds `storm.*` properties from `application.yml`. This is the recommended dependency for Spring Boot applications.

```kotlin
implementation("st.orm:storm-kotlin-spring-boot-starter:1.11.0")
implementation("st.orm:storm-kotlin-spring-boot-starter:@@STORM_VERSION@@")
```

See [Spring Integration: Spring Boot Starter](spring-integration.md#spring-boot-starter) for what the starter provides and how to override its defaults.
Expand Down Expand Up @@ -96,7 +96,7 @@ plugins {
}

dependencies {
ksp("st.orm:storm-metamodel-ksp:1.11.0")
ksp("st.orm:storm-metamodel-ksp:@@STORM_VERSION@@")
}
```

Expand All @@ -115,7 +115,7 @@ dependencies {
<path>
<groupId>st.orm</groupId>
<artifactId>storm-metamodel-processor</artifactId>
<version>1.11.0</version>
<version>@@STORM_VERSION@@</version>
</path>
</annotationProcessorPaths>
</configuration>
Expand Down
14 changes: 7 additions & 7 deletions docs/batch-streaming.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Database performance often degrades when applications issue many individual SQL

## Batch Processing

When you pass a list of entities to Storm's insert, update, delete, or upsert methods, Storm automatically uses JDBC batch statements. The framework groups rows together and sends them to the database in a single round-trip, rather than issuing one statement per entity.
When you pass a list of entities to Storm's insert, update, remove, or upsert methods, Storm automatically uses JDBC batch statements. The framework groups rows together and sends them to the database in a single round-trip, rather than issuing one statement per entity.

### Batch Insert

Expand Down Expand Up @@ -73,25 +73,25 @@ orm.entity(User.class).update(updatedUsers);
</TabItem>
</Tabs>

### Batch Delete
### Batch Remove

Batch deletes remove multiple entities in a single round-trip. Storm generates a batched DELETE using each entity's primary key.
Batch removes delete multiple entities in a single round-trip. Storm generates a batched DELETE using each entity's primary key.

<Tabs groupId="language">
<TabItem value="kotlin" label="Kotlin" default>

```kotlin
orm delete users
orm remove users

// Or delete all entities of a type
orm.deleteAll<User>()
// Or remove all entities of a type
orm.removeAll<User>()
```

</TabItem>
<TabItem value="java" label="Java">

```java
orm.entity(User.class).delete(users);
orm.entity(User.class).remove(users);
```

</TabItem>
Expand Down
18 changes: 9 additions & 9 deletions docs/common-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,13 +454,13 @@ Use the `scroll()` method on any entity repository with a `Scrollable` that capt
// First page of 20 users ordered by ID
val window: Window<User> = userRepository.scroll(Scrollable.of(User_.id, 20))

// Navigate forward: nextScrollable() is non-null whenever the window has content.
// Navigate forward: next() is non-null whenever the window has content.
// hasNext() is an informational flag indicating whether more rows existed at
// query time, but the developer decides whether to follow the cursor.
val next: Window<User> = userRepository.scroll(window.nextScrollable())
val next: Window<User> = userRepository.scroll(window.next())

// Navigate backward
val previous: Window<User> = userRepository.scroll(window.previousScrollable())
val previous: Window<User> = userRepository.scroll(window.previous())
```

</TabItem>
Expand All @@ -470,19 +470,19 @@ val previous: Window<User> = userRepository.scroll(window.previousScrollable())
// First page of 20 users ordered by ID
Window<User> window = userRepository.scroll(Scrollable.of(User_.id, 20));

// Navigate forward: nextScrollable() is non-null whenever the window has content.
// Navigate forward: next() is non-null whenever the window has content.
// hasNext() is an informational flag indicating whether more rows existed at
// query time, but the developer decides whether to follow the cursor.
Window<User> next = userRepository.scroll(window.nextScrollable());
Window<User> next = userRepository.scroll(window.next());

// Navigate backward
Window<User> previous = userRepository.scroll(window.previousScrollable());
Window<User> previous = userRepository.scroll(window.previous());
```

</TabItem>
</Tabs>

Each method returns a `Window` containing the page content and navigation cursors for sequential traversal. The `hasNext()` and `hasPrevious()` flags reflect whether additional rows existed at query time, but they are not prerequisites for calling `nextScrollable()` or `previousScrollable()`. Both methods return a non-null `Scrollable` whenever the window contains at least one element, and return `null` only when the window is empty. This means you can always follow the cursor if you choose to; for example, new rows may have been inserted after the original query. For REST APIs, `Window` also provides `nextCursor()` and `previousCursor()` to serialize the scroll position as an opaque string, and `Scrollable.fromCursor(key, cursor)` to reconstruct a `Scrollable` from a cursor string. See [Repositories: Scrolling](repositories.md#scrolling) for the full API, including sort overloads, filtering, and Ref variants.
Each method returns a `Window` containing the page content and navigation cursors for sequential traversal. The `hasNext()` and `hasPrevious()` flags reflect whether additional rows existed at query time, but they are not prerequisites for calling `next()` or `previous()`. Both methods return a non-null `Scrollable` whenever the window contains at least one element, and return `null` only when the window is empty. This means you can always follow the cursor if you choose to; for example, new rows may have been inserted after the original query. For REST APIs, `Window` also provides `nextCursor()` and `previousCursor()` to serialize the scroll position as an opaque string, and `Scrollable.fromCursor(key, cursor)` to reconstruct a `Scrollable` from a cursor string. See [Repositories: Scrolling](repositories.md#scrolling) for the full API, including sort overloads, filtering, and Ref variants.

### Choosing Between the Two

Expand All @@ -496,8 +496,8 @@ Each method returns a `Window` containing the page content and navigation cursor
| Performance at page 1 | Good | Good |
| Performance at page 1,000 | Degrades (database must skip rows) | Consistent (index seek) |
| Handles concurrent inserts | Rows may shift between pages | Stable cursor |
| Navigate forward | `page.nextPageable()` | `window.nextScrollable()` |
| Navigate backward | `page.previousPageable()` | `window.previousScrollable()` |
| Navigate forward | `page.nextPageable()` | `window.next()` |
| Navigate backward | `page.previousPageable()` | `window.previous()` |

Use pagination when you need random page access or a total count (for example, displaying "Page 3 of 12" in a UI). Use scrolling when you need consistent performance over deep result sets or when the data changes frequently between requests.

Expand Down
2 changes: 1 addition & 1 deletion docs/cursors.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ The following Java types can be used as cursor values (key or sort fields) out o

If your key or sort field uses a type not in this list, serialization via `toCursor()` will throw an `IllegalStateException`. You can either use one of the supported types for your key/sort columns, or register a custom codec.

Note that in-memory navigation (using `nextScrollable()` and `previousScrollable()` directly, without serializing to a cursor string) works with any type, including inline records and other composite types. The type restriction only applies to `toCursor()` serialization.
Note that in-memory navigation (using `next()` and `previous()` directly, without serializing to a cursor string) works with any type, including inline records and other composite types. The type restriction only applies to `toCursor()` serialization.

## Custom cursor codecs

Expand Down
Loading
Loading