abstract struct Athena::ORM::Types::Type
inherits Struct
#
Converts a mapped field's values between their Crystal representation and their database representation.
Every mapped field has a type, identified by its name, such as "string" or "datetime".
It's inferred from the property's Crystal type, or set explicitly with the type argument of AORMA::Column.
When an entity is loaded, its type converts each column into the value assigned to the property.
When an entity is written, its type converts the property's value into something the database driver can bind.
@[AORMA::Entity]
class Product < AORM::Entity
# Mapped as `bigint`, inferred from `Int64`.
@[AORMA::Column]
@[AORMA::ID]
@[AORMA::GeneratedValue]
property! id : Int64
# Mapped as `decimal`, set explicitly.
@[AORMA::Column(type: "decimal")]
property! price : String
end
Built-in Types#
| Name | Type | Crystal value | Inferred from |
|---|---|---|---|
string, text |
AORM::Types::String |
String |
String |
smallint |
AORM::Types::SmallInt |
Int16 |
Int16 |
integer |
AORM::Types::Integer |
Int32 |
Int32, and enums whose base type fits in an Int32 |
bigint |
AORM::Types::BigInt |
Int64 |
Int64, and enums based on Int64, UInt32 or UInt64 |
smallfloat |
AORM::Types::SmallFloat |
Float32 |
Float32 |
float |
AORM::Types::Float |
Float64 |
Float64 |
decimal |
AORM::Types::Decimal |
String |
|
number |
AORM::Types::Number |
BigDecimal |
BigDecimal |
boolean |
AORM::Types::Boolean |
Bool |
Bool |
datetime |
AORM::Types::Datetime |
Time |
Time |
guid |
AORM::Types::Guid |
UUID |
UUID |
binary |
AORM::Types::Binary |
Bytes |
|
blob |
AORM::Types::Blob |
Bytes |
Bytes |
A field whose Crystal type isn't inferred, and that has no explicit type, raises when its entity's metadata is built.
Enum fields are stored as their member's integer value; see AORMA::Column for details.
Note
The number type only exists when the program defines BigDecimal, e.g. via require "big", since BigDecimal requires linking libgmp.
Warning
MySQL and MariaDB read DECIMAL columns as Float64, so on those databases decimal and number values are limited to Float64 precision and lose their scale, e.g. "12.3400" reads back as "12.34".
SQLite stores them with REAL affinity, with the same limitation.
Only Postgres round-trips them exactly.
Custom Types#
A custom type can store any Crystal value, such as a value object, in a column.
Define a struct inheriting from this type, implementing the conversions in both directions, then register it under a name with .add_type.
# A value object holding an email address.
record Email, address : String
# Stores an `Email` as its address.
struct EmailType < AORM::Types::Type
NAME = "email"
def sql_declaration(column : AORM::Schema::Column, platform : AORM::Platforms::Platform) : String
platform.string_type_declaration_sql column
end
def to_db(value : _, platform : AORM::Platforms::Platform)
value.is_a?(Email) ? value.address : value
end
def to_crystal_value(value : _, platform : AORM::Platforms::Platform) : Email?
case value
when Nil, Email then value
when String then Email.new value
else raise "Cannot convert #{value.class} to an Email."
end
end
def to_crystal_value(value : DB::ResultSet, platform : AORM::Platforms::Platform) : Email?
value.read(String?).try { |address| Email.new address }
end
end
AORM::Types::Type.add_type EmailType::NAME, EmailType.new
The type can then be used by passing its name to AORMA::Column:
@[AORMA::Entity]
class User < AORM::Entity
@[AORMA::Column]
@[AORMA::ID]
@[AORMA::GeneratedValue]
property! id : Int64
@[AORMA::Column(type: "email")]
property! email : Email
end
#to_dbconverts a property's value into the value bound to the query. It must return something the driver can bind (DB::Any), otherwise the query raises.#to_crystal_value(value : DB::ResultSet, platform)reads the next column from the result set while an entity is hydrated.#to_crystal_value(value, platform)converts a value that has already been read, such as a database-generated identifier.#sql_declarationreturns the SQL type used to declare a column of this type.
Warning
A type that defines the to_crystal_value(value : _, platform) overload must also define the DB::ResultSet overload.
Otherwise the value : _ overload would also receive the result set itself, since a subclass's overload takes precedence over the more specific one defined on this type.
The ORM makes a few assumptions about the values a type produces:
- Changes are detected by comparing a field's value with
==to the value it was loaded with. Value objects should therefore be immutable and compare by value, as arecorddoes. Modifying a mutable object in place isn't detected; assign a new instance instead. #to_dbis only called for values being written or compared: the fields anINSERTwrites, the changed fields anUPDATEwrites, and identifiers and criteria values inWHEREclauses.- Entities are kept in the identity map under the string form of their identifier, so a value object used as an identifier needs a
#to_sthat differs for distinct values.
Todo
Value-object identifiers aren't accepted everywhere yet.
AORM::EntityManager#find ids, repository criteria and AORM::NativeQuery#set_parameter take driver-bindable values (DB::Any) rather than value objects.
Direct known subclasses
Athena::ORM::Types::BigInt
Athena::ORM::Types::Binary
Athena::ORM::Types::Blob
Athena::ORM::Types::Boolean
Athena::ORM::Types::Datetime
Athena::ORM::Types::Decimal
Athena::ORM::Types::Float
Athena::ORM::Types::Guid
Athena::ORM::Types::Integer
Athena::ORM::Types::SmallFloat
Athena::ORM::Types::SmallInt
Athena::ORM::Types::String
Constructors#
.get_type(name : ::String) : self#
Returns the type registered as name.
Raises if no type is registered with that name.
Class methods#
.add_type(name : ::String, type : AORM::Types::Type) : Nil#
Registers type as name, so fields can be mapped to it with @[AORMA::Column(type: name)].
Raises if a type is already registered with that name; use .override_type to replace one.
.override_type(name : ::String, type : AORM::Types::Type) : Nil#
Replaces the type registered as name with type, e.g. to change how a built-in type converts its values.
Raises if no type is registered with that name.
.type_map : Hash(::String, AORM::Types::Type.class)#
Returns the name of every registered type, mapped to its Type struct.
.type_registry : Athena::ORM::Types::TypeRegistry#
Returns the registry holding an instance of every known type, keyed by name. It starts out with the built-in types.
Methods#
#from_db_sql(sql_expression : ::String, platform : AORM::Platforms::Platform) : ::String#
Returns the SQL expression that converts sql_expression, a selected column, from its database representation. Returns it unchanged by default.
Applied to the columns of a SELECT.
abstract #sql_declaration(column : Schema::Column, platform : AORM::Platforms::Platform) : ::String#
Returns the SQL used to declare column as this type on platform, e.g. VARCHAR(255).
Built-in types delegate to the matching declaration method of AORM::Platforms::Platform, which custom types can reuse.
Todo
Nothing in the ORM generates schema yet, so this isn't called by the ORM itself.
#to_crystal_value(value : DB::ResultSet, platform : Platforms::Platform)#
Reads the next column from value and converts it into the Crystal value this type represents.
Returns nil for a NULL value.
Used while hydrating entities. Advances the cursor by exactly one column.
abstract #to_crystal_value(value : _, platform : Platforms::Platform)#
Converts value, as read from the database, into the Crystal value this type represents.
Returns nil for a NULL value, and raises if value can't be converted.
Used for values that have already been read, such as a database-generated identifier.
Each type defines this once, as the place for its translation logic (parsing, narrowing, decoding, etc.).
It accepts any input, since drivers disagree on the Crystal type of some columns, and validates it inside the body, e.g. with case value.
#to_db(value : _, platform : AORM::Platforms::Platform)#
Converts value, a property's Crystal value, into the value bound to the query. Returns value unchanged by default.
The returned value must be one the database driver can bind (DB::Any), otherwise executing the query raises.
#to_db_sql(sql_expression : ::String, platform : AORM::Platforms::Platform) : ::String#
Returns the SQL expression that converts sql_expression, a bound parameter's placeholder, to its database representation. Returns it unchanged by default.
Applied to the placeholders of values written by an INSERT or UPDATE, and of criteria values.