Skip to main content
Version: 1.9.0

Validation

Storm validates your entity and projection definitions at two levels: structural validation ensures your records follow the ORM's rules (valid primary key types, correct use of annotations, no circular dependencies), while schema validation compares your definitions against the actual database to catch mismatches before they surface as runtime errors.

Both levels are optional and configurable. Structural validation runs automatically on first use; schema validation must be explicitly enabled.


Record Validation

When Storm first encounters an entity or projection type, it inspects the record structure and validates that the definition is well-formed. This catches common modeling mistakes early, at startup rather than at query time.

What Gets Checked

Primary key rules:

  • The @PK type must be one of: boolean, int, long, short, String, UUID, BigInteger, Enum, or Ref. Floating-point types (float, double, BigDecimal) are rejected because they cannot reliably serve as identity values.
  • Compound keys (inline records annotated with @PK) follow the same type restrictions for each component.

Foreign key rules:

  • Fields annotated with @FK must be a Data type (entity, projection, or data class with a @PK) or a Ref wrapping such a type. Scalars like String or Integer cannot be foreign keys.
  • Auto-generated foreign keys (@FK(generation = ...)) cannot be inlined.

Inline component rules:

  • Fields annotated with @Inline must be record types. Scalars cannot be inlined.
  • Inline records must not declare their own @PK, since they are embedded within a parent entity.

Version fields:

  • At most one field per entity can be annotated with @Version. Multiple version fields are rejected.

Structural integrity:

  • Records must be immutable. Mutable fields (Kotlin var) are rejected.
  • Entities or projections that contain other entities or projections must annotate them as @FK or @Inline. Storm needs to know the relationship type to generate correct SQL.
  • The record graph is checked for cycles. If entity A inlines entity B, which inlines entity A, the circular dependency is reported.

Configuration

Record validation runs by default and causes startup to fail on the first error. The record-mode property controls this behavior:

ValueBehavior
failValidation errors cause startup to fail (default).
warnErrors are logged as warnings; startup continues.
noneRecord validation is skipped entirely.

This can be set as a system property, via StormConfig, or in Spring Boot's application.yml:

storm:
validation:
record-mode: fail # or "warn" or "none" (default: fail)

Schema Validation

Schema validation compares your entity and projection definitions against the actual database schema. It catches mismatches before they surface as runtime errors, similar to Hibernate's ddl-auto=validate. Storm never modifies the schema; it only reports mismatches.

What Gets Checked

CheckError KindSeverity
Table exists in the databaseTABLE_NOT_FOUNDError
Each mapped column exists in the tableCOLUMN_NOT_FOUNDError
Kotlin/Java type is compatible with the SQL column typeTYPE_INCOMPATIBLEError
Entity primary key columns match the database primary keyPRIMARY_KEY_MISMATCHError
Sequences referenced by @PK(generation = SEQUENCE) existSEQUENCE_NOT_FOUNDError
Numeric cross-category conversions (e.g., Integer mapped to DECIMAL)TYPE_NARROWINGWarning
Non-nullable entity field mapped to a nullable database columnNULLABILITY_MISMATCHWarning
@UK field has a matching unique constraint in the databaseUNIQUE_KEY_MISSINGWarning
@FK field has a matching foreign key constraint in the databaseFOREIGN_KEY_MISSINGWarning

Errors indicate definitive mismatches that will cause runtime failures, such as missing tables or columns.

Warnings indicate situations where the mapping works at runtime but may involve subtle differences, such as precision loss when mapping a Kotlin Int to an Oracle NUMBER column. Warnings are logged but do not cause validation to fail (unless strict mode is enabled).

Constraint Validation

The UNIQUE_KEY_MISSING and FOREIGN_KEY_MISSING checks verify that the database has the constraints your entity model declares. These are warnings rather than errors because the ORM functions correctly without database-level enforcement: queries return the same results, inserts and updates succeed, and keyset pagination works as expected.

However, database constraints serve as a safety net that the application layer cannot replace:

  • Unique constraints protect against application bugs and concurrent modifications that could insert duplicate values. Without a database-level unique constraint, a @UK field might contain duplicates that go undetected until a findBy call unexpectedly returns multiple results.
  • Foreign key constraints protect referential integrity. Without a database-level foreign key constraint, orphaned rows can accumulate when referenced rows are deleted.

In strict mode, these warnings are promoted to errors, causing validation to fail if the constraints are missing. Use @DbIgnore to suppress these warnings for fields where the missing constraint is intentional (for example, when using application-level deduplication or soft deletes that make database constraints impractical).

Use @DbIgnore to exclude entity fields from schema validation. See Entities for annotation details.

Programmatic API

Any ORMTemplate created from a DataSource supports schema validation:

val orm = dataSource.orm

// Inspect errors programmatically
val errors: List<String> = orm.validateSchema()

// Or validate and throw on failure
orm.validateSchemaOrThrow()

Both methods have overloads that accept specific types to validate:

orm.validateSchema(listOf(User::class.java, Order::class.java))

The no-argument variants discover all entity and projection types on the classpath automatically.

On success, a confirmation message is logged at INFO level. On failure, each error is logged at ERROR level, and validateSchemaOrThrow() throws a PersistenceException with a summary of all errors. Warnings are always logged at WARN level regardless of the outcome.

Templates created from a raw Connection or JPA EntityManager do not support schema validation, since they lack the DataSource needed to query database metadata.

Strict Mode

By default, warnings (type narrowing and nullability mismatches) do not cause validation to fail. In strict mode, all findings are treated as errors:

val config = StormConfig.of(mapOf("storm.validation.strict" to "true"))
val orm = ORMTemplate.of(dataSource, config)
orm.validateSchemaOrThrow() // Warnings now cause failure

Suppressing Validation with @DbIgnore

Use @DbIgnore to suppress schema validation for specific entities or fields. This is useful for legacy tables, columns handled by custom converters, or known mismatches that are safe to ignore.

Suppress validation for an entire entity:

@DbIgnore
data class LegacyUser(
@PK val id: Int = 0,
val name: String
) : Entity<Int>

Suppress validation for a specific field:

data class User(
@PK val id: Int = 0,
val name: String,
@DbIgnore("DB uses FLOAT, but column only stores whole numbers")
val age: Int
) : Entity<Int>

The optional value parameter documents why the mismatch is acceptable. When @DbIgnore is placed on an inline component field, validation is suppressed for all columns within that component.

Custom Schemas

Schema validation respects @DbTable(schema = "..."). Each entity is validated against the schema specified in its annotation, or the connection's default schema if none is specified.

@DbTable(schema = "reporting")
data class Report(
@PK val id: Int = 0,
val name: String
) : Entity<Int>

Spring Boot Configuration

When using the Spring Boot Starter, both record and schema validation can be configured through application.yml:

storm:
validation:
record-mode: fail # or "warn" or "none" (default: fail)
schema-mode: none # or "warn" or "fail" (default: none)
strict: false # treat schema warnings as errors (default: false)

The schema-mode values:

ValueBehavior
noneSchema validation is skipped (default).
warnMismatches are logged at WARN level; startup continues.
failMismatches cause startup to fail with a PersistenceException.

Configuration Properties

PropertyDefaultDescription
storm.validation.record_modefailRecord validation mode: fail, warn, or none
storm.validation.schema_modenoneSchema validation mode: none, warn, or fail (Spring Boot only)
storm.validation.strictfalseWhen true, schema validation warnings are treated as errors