Package st.orm

Annotation Interface UK


Marks a field as a unique key. Use this annotation on fields that have a unique constraint in the database. Fields annotated with @UK indicate that the corresponding column contains unique values, making them suitable for single-result lookups and for use as keyset pagination cursors.

The @PK annotation is meta-annotated with @UK, so primary key fields are automatically recognized as unique without needing an explicit @UK annotation.

NULL Handling

In standard SQL, NULL != NULL, so a UNIQUE constraint typically allows multiple rows with NULL values. This breaks the uniqueness guarantee that scrolling (scroll) relies on, because WHERE key > cursor silently excludes NULL rows.

Some databases treat NULLs as equal for uniqueness purposes: PostgreSQL 15+ supports NULLS NOT DISTINCT, and SQL Server allows only one NULL by default. Since Storm cannot determine the database constraint behavior from the code alone, you can declare it explicitly via nullsDistinct().

When a nullable field is annotated with @UK and nullsDistinct is true (the default), the metamodel processor emits a compile-time warning, and scroll methods throw a PersistenceException at runtime. To suppress the warning and enable keyset pagination, either make the field non-nullable (use a primitive type or add @Nonnull), or set @UK(nullsDistinct = false) to indicate that the database constraint prevents duplicate NULLs.

Usage example (Java):


 record User(@PK Integer id,
             @UK String email,
             String name
 ) implements Entity<Integer> {}
 

Usage example (Kotlin):


 data class User(@PK val id: Int?,
                 @UK val email: String,
                 val name: String
 ) : Entity<Int>
 

Compound Unique Keys

For compound unique constraints that need a metamodel key (e.g., for keyset pagination or type-safe lookups), use an inline record annotated with @UK:

Java:


 record UserEmailUk(int userId, String email) {}

 record SomeEntity(@PK Integer id,
                   @FK User user,
                   String email,
                   @UK @Persist(insertable = false, updatable = false) UserEmailUk uniqueKey
 ) implements Entity<Integer> {}
 

The @Persist(insertable = false, updatable = false) annotation prevents the inline record's columns from being persisted separately when they overlap with other fields on the entity.

Compound unique constraints that do not require a metamodel key do not need to be modeled in the entity. Schema validation does not warn about unmodeled compound constraints.

The metamodel processor generates Metamodel.Key instances for fields annotated with @UK, enabling type-safe keyset pagination and unique field lookups via repository methods like findBy(Metamodel.Key, value) and getBy(Metamodel.Key, value).

Since:
1.9
See Also:
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    boolean
    Indicates whether a corresponding unique constraint is expected to exist in the database.
    boolean
    Indicates whether NULL values are considered distinct for the purpose of the UNIQUE constraint.
  • Element Details

    • nullsDistinct

      boolean nullsDistinct
      Indicates whether NULL values are considered distinct for the purpose of the UNIQUE constraint.

      When true (the default, matching the SQL standard), the database allows multiple rows with NULL values in the unique column. This means the uniqueness guarantee is broken for nullable fields, which makes keyset pagination unsafe.

      Set to false when the database treats NULLs as equal (e.g., PostgreSQL 15+ with NULLS NOT DISTINCT, or SQL Server which allows only one NULL by default). This tells Storm that the nullable field is safe for keyset pagination.

      This attribute has no effect on fields that are already non-nullable (primitives, @PK, or fields annotated with @Nonnull).

      Returns:
      true if NULLs are distinct (SQL standard), false if the database prevents duplicate NULLs.
      Since:
      1.9
      Default:
      true
    • constraint

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

      When true (the default), schema validation will warn if no matching unique constraint is found in the database. Set to false when the database intentionally omits the unique constraint, for example because uniqueness 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 unique key for keyset pagination and unique lookups.

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