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.
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.
- Close to SQL: Storm embraces SQL rather than abstracting it away, keeping you in control of your database operations.
- Type-safe: Storm's DSL mirrors SQL, providing a type-safe, intuitive experience that makes queries easy to write and read while reducing the risk of runtime errors.
- Direct Database Interaction: Storm translates method calls directly into database operations, offering a transparent and straightforward experience. It eliminates inefficiencies like the N+1 query problem for predictable and efficient interactions.
- Stateless: Avoids hidden complexities and "magic" with stateless, record-based entities, ensuring simplicity and eliminating lazy initialization and transaction issues downstream.
- 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:
npm install -g @storm-orm/cli
storm initThis installs Storm's rules, skills, and optional local MCP setup for supported AI coding tools. See AI-Assisted Development for the full workflow.
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.
Storm’s mission: Make database development productive and enjoyable, with full developer control and high performance.
Storm embraces SQL rather than abstracting it away. It simplifies database interactions while remaining transparent, and scales from prototypes to enterprise systems.
| Traditional ORM Pain | Storm Solution |
|---|---|
| N+1 queries from lazy loading | Entity graphs load in a single query |
| Hidden magic (proxies, implicit flush, cascades) | Stateless records—explicit, predictable behavior |
| Entity state confusion (managed/detached/transient) | Immutable records—no state to manage |
| Entities tied to session/context | Stateless records easily cached and shared across layers |
| Dirty checking via bytecode manipulation | Lightning-fast dirty checking thanks to immutability |
| Complex mapping configuration | Convention over configuration |
| Runtime query errors | Compile-time type-safe DSL |
| SQL hidden behind abstraction layers | SQL-first design—stay close to the database |
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 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:
- You prompt the AI.
- The AI uses Storm skills and local schema context to generate code.
- Storm verifies the generated entities and queries in tests.
- 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.
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.
// Define an entity
data class User(
@PK val id: Int = 0,
val email: String,
val name: String,
@FK val city: City
) : Entity<Int>
// Type-safe predicates — query nested properties like city.name in one go
val users = orm.findAll(User_.city.name eq "Sunnyvale")
// Custom repository — inherits all CRUD operations, add your own queries
interface UserRepository : EntityRepository<User, Int> {
fun findByCityName(name: String) = findAll(User_.city.name eq name)
}
val users = userRepository.findByCityName("Sunnyvale")
// Block DSL — build queries with where, orderBy, joins, pagination
val users = userRepository.select {
where(User_.city.name eq "Sunnyvale")
orderBy(User_.name)
}.resultList
// SQL Template for full control; parameterized by default, SQL injection safe
val users = orm.query { """
SELECT ${User::class}
FROM ${User::class}
WHERE ${User_.city.name} = $cityName"""
}.resultList<User>()Full coroutine support with Flow for streaming and programmatic transactions:
// Streaming with Flow
val users: Flow<User> = orm.entity(User::class).select().resultFlow
users.collect { user -> println(user.name) }
// Programmatic transactions
transaction {
val city = orm insert City(name = "Sunnyvale", population = 155_000)
val user = orm insert User(email = "bob@example.com", name = "Bob", city = city)
}// Define an entity
record User(@PK Integer id,
String email,
String name,
@FK City city
) implements Entity<Integer> {}
// Custom repository—inherits all CRUD operations, add your own queries
interface UserRepository extends EntityRepository<User, Integer> {
default List<User> findByCityName(String name) {
return select().where(User_.city.name, EQUALS, name).getResultList();
}
}
List<User> users = userRepository.findByCityName("Sunnyvale");
// Query Builder for more complex operations
List<User> users = orm.entity(User.class)
.select()
.where(User_.city.name, EQUALS, "Sunnyvale")
.orderBy(User_.name)
.getResultList();
// SQL Template for full control; parameterized by default, SQL injection safe
List<User> users = orm.query(RAW."""
SELECT \{User.class}
FROM \{User.class}
WHERE \{User_.city.name} = \{cityName}
""").getResultList(User.class);String Templates: Kotlin uses a compiler plugin that automatically wraps interpolations at compile time. Java uses String Templates, a preview feature. See String Templates for setup and details on both languages.
Storm provides a Bill of Materials (BOM) for centralized version management. Import the BOM once and omit version numbers from individual Storm dependencies.
Maven:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-bom</artifactId>
<version>@@STORM_VERSION@@</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>Gradle (Kotlin DSL):
dependencies {
implementation(platform("st.orm:storm-bom:@@STORM_VERSION@@"))
}With the BOM imported, add Storm modules without specifying versions:
dependencies {
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.
kotlinCompilerPluginClasspath("st.orm:storm-compiler-plugin-2.0")
}<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-java21</artifactId>
</dependency>
<dependency>
<groupId>st.orm</groupId>
<artifactId>storm-core</artifactId>
<scope>runtime</scope>
</dependency>Full documentation is available at storm-repo.github.io/storm-framework.
Everything you need to build applications with Storm. Start with Getting Started and work through the topics as needed.
| Topic | Description |
|---|---|
| Getting Started | Installation and first steps (7 min) |
| Entities | Defining entities, annotations, naming (12 min) |
| Projections | Read-only database views (8 min) |
| Relationships | One-to-one, many-to-one, many-to-many (13 min) |
| Repositories | Repository pattern and custom methods (5 min) |
| Queries | Select, filter, aggregate, order (8 min) |
| Metamodel | Compile-time type safety (10 min) |
| Refs | Lazy loading and optimized references (7 min) |
| Batch & Streaming | Bulk operations and Flow/Stream (5 min) |
| Upserts | Insert-or-update operations (6 min) |
| Polymorphism | Sealed type inheritance strategies (20 min) |
| Entity Lifecycle | Callbacks for auditing, validation, and logging (8 min) |
| JSON Support | JSON columns and aggregation (6 min) |
| Transactions | Transaction management and propagation (22 min) |
| Spring Integration | Spring Boot Starter and auto-configuration (8 min) |
| Database Dialects | Database-specific support (5 min) |
| Testing | JUnit 5 integration and statement capture (5 min) |
| Validation | Record and schema validation (5 min) |
Deep dives into Storm's internals. You don't need these to be productive, but they help you understand what happens under the hood and optimize performance.
| Topic | Description |
|---|---|
| String Templates | Kotlin compiler plugin and Java string templates (5 min) |
| SQL Templates | Template parameters and query generation (10 min) |
| Hydration | Result mapping to records (16 min) |
| Dirty Checking | Update modes and change detection (19 min) |
| Entity Cache | Transaction-scoped caching and identity (10 min) |
| Configuration | System properties reference (7 min) |
| SQL Logging | Declarative query logging with @SqlLog (6 min) |
| Metrics | JMX runtime metrics for monitoring (5 min) |
Guides for evaluating Storm and transitioning from other frameworks.
| Topic | Description |
|---|---|
| Comparison | Storm vs other frameworks |
| FAQ | Frequently asked questions |
| Migration from JPA | Transitioning from JPA/Hibernate |
Storm works with any JDBC-compatible database. Dialect packages provide optimized support for:
Storm targets Kotlin 2.0+ and Java 21+ as minimum supported versions. These baselines will be maintained for the foreseeable future.
We welcome contributions! See CONTRIBUTING.md for guidelines.
Storm is released under the Apache 2.0 License.