Migrating from V1 to V2
Stormify V2 is a major rewrite that brings Kotlin Multiplatform support, replacing the JVM-only V1. This guide covers all breaking changes and how to update your code.
What Changed
| Area | V1 | V2 |
|---|---|---|
| Platforms | JVM only | JVM, Android, Linux, iOS, macOS |
| Database access | JDBC directly | KDBC (wraps JDBC on JVM, native drivers elsewhere) |
| Initialization | Singleton (StormifyManager.stormify()) |
Constructor (Stormify(dataSource)) |
| Entity discovery | Reflection only | Reflection (JVM) + KSP code generation (native) |
| Exceptions | QueryException, SPParamException |
Unified SQLException |
| Stored procedures | SPParam with Mode enum |
Sp.In, Sp.Out<T>, Sp.InOut<T> |
| Coroutines | Not supported | SuspendStormify with connection pooling |
| Language | Java + optional Kotlin extensions | Kotlin (with Java interop on JVM) |
Dependencies
Maven
Gradle
The separate db and kotlin modules are merged into a single stormify artifact.
The group ID changed from onl.ycode.stormify to onl.ycode.
Initialization
The global singleton is gone. Create Stormify instances via constructor.
Key change: instead of a global singleton, you hold a Stormify instance and pass it
to your services. This makes testing and multi-database setups straightforward.
Transactions
The transaction API moves from ThreadLocal-based context to a lambda-with-receiver pattern.
Nested transactions use savepoints automatically:
stormify.transaction {
create(user)
transaction {
// Creates savepoint — rolls back only this block on failure
create(profile)
}
}
CRUD Operations
Method Signatures
// Reified generics — no Class parameter
val users = stormify.read<User>("SELECT * FROM users WHERE age > ?", 18)
val user = stormify.readOne<User>("SELECT * FROM users WHERE id = ?", 1)
val created = stormify.create(user) // Returns the created item
val updated = stormify.update(user) // Returns the updated item
stormify.delete(user)
Changes:
- No Class<T> parameter — V2 uses Kotlin reified generics
- create() and update() now return the item (useful for generated IDs)
- For Java callers, use StormifyJ which accepts Class<T> parameters
findById
Cursor-based Reading
V2 adds readCursor for processing large result sets without loading everything into memory:
stormify.readCursor<User>("SELECT * FROM users") { user ->
processUser(user) // Called for each row
}
Stored Procedures
The stored procedure API is redesigned for type safety.
Parameter types:
| V1 | V2 | Purpose |
|---|---|---|
SPParam(Mode.IN, value) |
Sp.In(value) or spIn(value) |
Input parameter |
SPParam(Mode.OUT, type) |
Sp.Out<T>() or spOut<T>() |
Output parameter |
SPParam(Mode.IN_OUT, value) |
Sp.InOut<T>(value) or spInOut(value) |
Bidirectional |
Exception Handling
V2 uses a single unified exception type across all platforms.
QueryException and SPParamException are removed. All database errors throw
onl.ycode.kdbc.SQLException.
Configuration
The naming policies and annotations (@DbTable, @DbField, @Id, @Table, @Column,
@Transient, etc.) are unchanged.
Entity Registration
On JVM, reflection-based entity discovery works the same as V1 (no changes needed) —
kotlin-reflect is now included as a transitive dependency.
If you plan to target native platforms or want faster JVM startup, V2 offers the
annproc annotation processor (via KSP) to generate entity metadata at compile time.
See Annotation Processor for setup details.
DataSource Wrapping (JVM)
On JVM, V2 accepts javax.sql.DataSource directly in the Stormify constructor (via a
convenience overload). If you need the KDBC DataSource interface explicitly:
The JdbcDataSource wrapper has zero overhead — it delegates directly to the underlying
JDBC types.
Removed APIs
The following V1 APIs no longer exist in V2:
onInit()callbacks — not needed; use constructor-based initialization instead.closeDataSource()/close()— Stormify no longer owns the DataSource. Close it directly.CRUDTable.populate()andCRUDTable.tableName()— removed. Usestormify.populate(entity)orAutoTable.populate()instead.User::class.db(Kotlin table name extension) — removed. Usestormify.getTableInfo(User::class).tableNameinstead.storedProcedure()— renamed toprocedure().
Quick Migration Checklist
- Update dependencies:
onl.ycode.stormify:db→onl.ycode:stormify-jvm - Replace singleton:
StormifyManager.stormify()→Stormify(dataSource)constructor - Hold the instance: pass
Stormifyto services instead of calling static methods - Update transactions:
TransactionContext.begin()→stormify.transaction { } - Remove Class parameters:
read(User.class, sql)→read<User>(sql) - Update exception handling:
QueryException→SQLException - Update stored procedures:
storedProcedure()→procedure(),SPParam→SpAPI - Add KSP if targeting native platforms
- Optional: adopt coroutine API for async workloads