Core Concepts
POJO Mapping and Requirements
Stormify maps Plain Old Java Objects (POJOs) to database tables using property names that match database column names, minimizing the need for configuration or annotations.
Bean Property Matching
Stormify discovers entity properties by scanning for getter/setter method pairs following JavaBean conventions
(e.g., getName()/setName() or isActive()/setActive() for booleans). A matching getter and setter define a
property that is mapped to a database column. Read-only properties (getter without setter) are also supported but
cannot be written to.
Important: Raw public fields without getters/setters are not mapped. Your POJOs must follow the JavaBean
convention, or use Kotlin var properties (which generate getters/setters automatically).
- Automatic Mapping: Properties are automatically mapped to database columns with matching names (after applying the naming policy). No annotations are required.
- Optional Annotations: You can use the
@DbTableand@DbFieldannotations to customize the mapping.
Naming Policy
Stormify provides flexible naming policies to convert class names to table names and field names to column names,
ensuring consistency across your database schema. You can set the naming policy using the setNamingPolicy method in
the Stormify manager. By default, the naming policy is set to lowerCaseWithUnderscores (snake_case). This policy
will only affect tables and fields that are not already registered.
NamingPolicy is an interface with three predefined implementations. You can also create custom implementations.
Predefined Naming Policies
-
NamingPolicy.camelCase- Uses class and field names as they are, preserving camel case.
- Example: A class named
UserAccountremainsUserAccount, and a field nameduserNameremainsuserName.
-
NamingPolicy.lowerCaseWithUnderscores(default)- Converts class and field names to lower case with underscores (snake_case).
- Example: A class named
UserAccountbecomesuser_account, and a field nameduserNamebecomesuser_name.
-
NamingPolicy.upperCaseWithUnderscores- Converts class and field names to upper case with underscores (SCREAMING_SNAKE_CASE).
- Example: A class named
UserAccountbecomesUSER_ACCOUNT, and a field nameduserNamebecomesUSER_NAME.
Custom Naming Policy
You can create a custom naming policy by implementing the NamingPolicy interface:
Custom Primary Key Resolvers
The primary keys in databases commonly follow a naming convention. If this is the case, instead of using annotations, you can register custom primary key resolvers to help Stormify identify primary keys based on their name.
How It Works
To set up a primary key resolver, use the registerPrimaryKeyResolver method. You provide:
- Priority: Determines which resolver is used first if multiple are registered. Higher values mean higher priority.
- Resolver Function: A function that takes the table name and field name, and returns true if the field is a primary key.
Simple Example
If your primary keys follow a specific naming pattern, you can register a resolver that uses your own criteria. For instance:
stormify().registerPrimaryKeyResolver(10, (tableName, fieldName) -> fieldName.equalsIgnoreCase("id"));
Annotations
@DbTable Annotation
The @DbTable annotation is used to specify the database table name associated with a class. This annotation is
optional and is only needed if the table name differs from the class name.
Attributes
name: Specifies the name of the table in the database. If not provided, the class name will be converted using the current naming policy.
Example
import onl.ycode.stormify.DbTable;
@DbTable(name = "custom_table_name")
public class Test {
private int id;
private String name;
// Getters and setters
}
@DbField Annotation
The @DbField annotation is used to provide additional information about a specific field in a class. This annotation
is optional and allows you to customize how fields are mapped to database columns.
Attributes
name: Specifies the name of the field in the database. If not provided, the field name in the class will be converted using the current naming policy.primaryKey: Indicates whether the field is a primary key. Defaults tofalse.primarySequence: Specifies the name of the primary key sequence in the database. If not provided, the primary key value generation relies on the database.creatable: Determines whether the field is included in INSERT statements. Defaults totrue.updatable: Determines whether the field is included in UPDATE statements. Defaults totrue.
Example
import onl.ycode.stormify.DbField;
public class Test {
@DbField(name = "custom_id", primaryKey = true, primarySequence = "id_seq")
private int id;
@DbField(creatable = false, updatable = true)
private String name;
// Getters and setters
}
JPA-Compatible Annotations
Stormify provides support for several standard annotations from the javax.persistence package, making it easy to
integrate with existing Java applications. The following annotations are supported:
-
@Id: Marks a field as the primary key of the entity. -
@Table: Specifies the table in the database that maps to the entity, using thenameattribute. -
@Column: Maps a field to a specific column in the database table. Stormify reads thename,insertable, andupdatableattributes. -
@JoinColumn: Specifies the column used for joining an entity association. Stormify reads thename,insertable, andupdatableattributes. -
@SequenceGenerator: Defines a primary key generator that uses a database sequence, using thenameattribute to specify the sequence name. -
@Transient: Marks a field to be excluded from entity mapping entirely.
Reference Fields (Foreign Keys)
When a property's type is another POJO (not a primitive, String, date, etc.), Stormify treats it as a reference field (foreign key). The database column stores the primary key value of the referenced entity.
public class Order {
@DbField(primaryKey = true)
private Integer id;
private Customer customer; // Reference field — DB column stores customer's primary key
// Getters and setters
}
When reading an Order from the database, Stormify creates a Customer instance with only its primary key set.
If Customer extends AutoTable, its remaining fields are lazy-loaded on first access (see
AutoTable).
Blacklist Management
Stormify includes a feature to manage fields that should be excluded from entity mapping entirely. Blacklisted fields are completely ignored during all database interactions (reads, creates, and updates).
Note: Fields marked as @Transient are also ignored automatically.
- Add to Blacklist: Use
addBlacklistField(String fieldName)to add a field to the blacklist. - Remove from Blacklist: Use
removeBlacklistField(String fieldName)to remove a field from the blacklist.
The following fields are blacklisted by default: serialVersionUID, idFieldValue, transientId.
Example:
Strict Mode
Strict mode controls how Stormify handles database columns that have no matching field in the Java entity.
- Strict mode enabled: A
QueryExceptionis thrown when a database column returned by a query has no corresponding field in the entity class. - Strict mode disabled (default): A warning is logged and the unmatched column is skipped.