Package st.orm

Annotation Interface FK


Marks a field as a foreign key. You can specify the foreign key's database column name using either: Both attributes are aliases. If both are specified, they must have the same value.

Usage examples:

  • @FK("user_id")
  • @FK(name="user_id")
  • @FK @DbColumn("user_id")
Each sets the foreign key's database column name to "user_id".

If no value is specified (i.e., both value() and name() are set to ""), an automatic column name resolution strategy will be applied.

Composite Foreign Keys

When referencing an entity with a composite primary key, Storm automatically generates multi-column join conditions. Use @DbColumn annotations to specify custom column names for each component.

Java:


 record AuditLog(@PK Integer id,
                 String action,
                 @FK @DbColumn("audit_user_id") @DbColumn("audit_role_id") UserRole userRole
 ) implements Entity<Integer> {}
 

Kotlin:


 data class AuditLog(@PK val id: Int?,
                     val action: String,
                     @FK @DbColumn("audit_user_id") @DbColumn("audit_role_id") val userRole: UserRole
 ) : Entity<Int>
 

Foreign Keys Overlapping with Composite Primary Key

In join tables for many-to-many relationships, the FK columns often overlap with the composite PK columns. Use @Persist to indicate that the FK fields are only used for loading related entities, not for insert/update operations.

Java:


 record UserRolePk(int userId, int roleId) {}

 record UserRole(@PK UserRolePk pk,
                 @FK @Persist(insertable = false, updatable = false) User user,
                 @FK @Persist(insertable = false, updatable = false) Role role
 ) implements Entity<UserRolePk> {}
 

Kotlin:


 data class UserRolePk(val userId: Int, val roleId: Int)

 data class UserRole(@PK val pk: UserRolePk,
                     @FK @Persist(insertable = false, updatable = false) val user: User,
                     @FK @Persist(insertable = false, updatable = false) val role: Role
 ) : Entity<UserRolePk>
 

Primary Key as Foreign Key

A foreign key can also serve as the primary key, which is useful for dependent one-to-one relationships, extension tables, or table-per-subtype inheritance. Use both @PK and @FK on the same field.

Java:


 record UserProfile(@PK(generation = NONE) @FK User user,
                    String bio
 ) implements Entity<User> {}
 

Kotlin:


 data class UserProfile(@PK(generation = NONE) @FK val user: User,
                        val bio: String
 ) : Entity<User>
 

Column name resolution when both @PK and @FK are present:

  1. Explicit name in @PK (e.g., @PK("user_profile_id"))
  2. Explicit name in @DbColumn
  3. Foreign key naming convention (default)
See Also:
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    boolean
    Indicates whether a corresponding foreign key constraint is expected to exist in the database.
    The database column name for the foreign key.
    The database column name for the foreign key.
  • Element Details

    • value

      String value
      The database column name for the foreign key. Acts as an alias for name().
      Default:
      ""
    • name

      String name
      The database column name for the foreign key. Acts as an alias for value().
      Default:
      ""
    • constraint

      boolean constraint
      Indicates whether a corresponding foreign key constraint is expected to exist in the database.

      When true (the default), schema validation will warn if no matching foreign key constraint is found in the database. Set to false when the database intentionally omits the foreign key constraint, for example for performance reasons or because referential integrity is enforced at the application level.

      Setting this to false only suppresses the constraint check during schema validation. The field is still fully functional as a foreign key for Storm's query generation and relationship resolution.

      Returns:
      true if the foreign key constraint is expected in the database, false to skip the check.
      Since:
      1.10
      Default:
      true