Entity definition
An Entity
in Exposed maps a database table record to a Kotlin object. This ensures type safety and allows you to work with database records just like regular Kotlin objects, taking full advantage of Kotlin's language features.
When using the DAO approach, IdTable
needs to be associated with an Entity
, because every database record in this table needs to be mapped to an Entity
instance, identified by its primary key.
Defining an Entity class
You define an entity instance by creating a class. In the following example, StarWarsFilmEntity
is the entity class linked to the table StarWarsFilmsTable
:
Entity type
The entity type determines how the Entity
class interacts with the table’s primary key. Since StarWarsFilmsTable
is an IntIdTable
, the StarWarsFilmsEntity
class extends from IntEntity
, which indicates that the id
and primary key of StarWarsFilmsTable
is of type Int
.
The following entity types are supported:
IntEntity
Base class for an entity instance identified by an id comprised of a wrapped
Int
value.LongEntity
Base class for an entity instance identified by an id comprised of a wrapped
Long
value.UIntEntity
Base class for an entity instance identified by an id comprised of a wrapped
UInt
value.ULongEntity
Base class for an entity instance identified by an id comprised of a wrapped
ULong
value.UUIDEntity
Base class for an entity instance identified by an id comprised of a wrapped
UUID
value.CompositeEntity
Base class for an entity instance identified by an id comprised of multiple wrapped values.
EntityClass
The EntityClass
in the companion object
block is responsible for managing Entity
instances, such as creating, querying, and deleting records. It also maintains the relationship between the entity class (StarWarsFilmEntity
in this example) and the database table (StarWarsFilmsTable
).
Each entity type is supported by a corresponding EntityClass
, which accepts the following parameters:
- table
The
IdTable
containing rows mapped to entities managed by this class.- entityType
(Optional) The expected type of the
Entity
class. This can be omitted if it is the class of the type argument provided to thisEntityClass
instance.- entityCtor
(Optional) The function that instantiates an
Entity
using the providedEntityID
. If not provided, reflection is used to determine the primary constructor on the first access (which may affect performance).
In the example above, IntEntityClass
is specified in the companion object:
This setup enables you to use functions provided by IntEntityClass
to manage instances of StarWarsFilmEntity
effectively.
Properties
Each column in the table is represented as a property in the entity class, where the by
keyword ensures the data is fetched or updated from the corresponding column when accessed.
Once the entity class is defined, instances of this class allow you to manipulate individual records from the corresponding table. This could involve creating a new record, retrieving a row based on its primary key, updating values, or deleting records.
Immutable entities
For defining entities that are immutable, Exposed provides the additional ImmutableEntityClass
and ImmutableCachedEntityClass
.
The ImmutableCachedEntityClass
uses an internal cache to store entity loading states by the associated database. This ensures that entity updates are synchronized with this class as the lock object.
To create an immutable entity, use either ImmutableEntityClass
or ImmutableCachedEntityClass
as a companion object in your entity class. For example, here’s how to define a CityEntity
class:
Properties are defined using
val
instead ofvar
. This enforces immutability, asval
properties cannot be reassigned after initial assignment.The primary function of the entity class in this context is to query data from the associated table, not to modify it. Therefore, inserts can only be performed using the DSL
insert()
method and updates can be done either through the DSLupdate()
or theImmutableEntityClass.forceUpdateEntity()
methods.
Class method overrides
You can use override methods in both the Entity
class and the EntityClass
companion object to extend functionality or manage entity behavior.
For example, here’s how to override the delete()
method in a User
entity:
In this example, a custom message is printed before the delete()
function of the superclass (IntEntity
) completes the deletion.
Field transformations
As databases typically store only basic types, such as integers and strings, it's not always convenient to keep the same simplicity on Data Access Object (DAO) level.
For example, you might need to parse JSON from a VARCHAR
column, or retrieve values from a cache based on data from the database. In such cases, the preferred approach is to use column transformations.
Example: Defining an Unsigned Integer field
Suppose that you want to define an unsigned integer field on an entity, but Exposed doesn't have such column type yet. You can achieve this by using the following implementation:
The transform
function accept two lambdas that convert values to and from the original column type. In this case, you make sure to store only UInt
instances in the uint
field.
Although it is still possible to insert or update values with negative integers via DAO, this approach assures a cleaner business logic.
Memoized transformations
If your transformation function involves complex operations that impact performance, you can use memoizedTransform
to cache the result of the transformation.
With memorized transformation, the value is unwrapped only once and then cached for future reads. This cache remains valid for the lifetime of the entity. If the transaction's entity cache is cleared or the entity is reloaded in a new transaction (creating a new cache without the existing value), the value will be wrapped again. However, if the original entity is kept alive outside of the transaction, the cached value persists to avoid re-wrapping.