Working with tables
In this topic, we will explain how to define, configure, and create tables. All examples use the H2 database to generate SQL.
Table Types
Table
In Exposed, the Table class is the core abstraction for defining database tables. This class provides methods to define various column types, constraints, and other table-specific properties.
Table is located in the org.jetbrains.exposed.v1.core package of the exposed-core module.
IdTable
Apart from the core Table class, Exposed provides the base IdTable class and its subclasses through the DAO API.
The IdTable class extends Table and is designed to simplify the definition of tables that use a standard id column as the primary key. These tables can be declared without explicitly including the id column, as IDs of the appropriate type are automatically generated when creating new table rows.
IdTable and its subclasses are located in the org.jetbrains.exposed.dao.id package of the exposed-core module.
For more information on IdTable types, see DAO Table Types.
Defining tables
A database table is represented by an object inherited from a Table class.
Exposed supports a variety of column types, including basic types, date and time, arrays, binary data, and more. Each column is defined by calling the appropriate method on the Table object.
The following example defines a table with an auto-incrementing integer id column and custom columns sequel_id, name, and director:
The IntIdTable class automatically generates an auto-incrementing integer id column, which serves as the primary key for the table. Therefore, there is no need to explicitly define the id column in the StarWarsFilmsTable object.
Creating the table with the above definition will result in the following SQL equivalent:
Configuring a custom table name
By default, Exposed generates the table name from the full class name.
If the object name contains a 'Table' suffix, Exposed will omit the suffix from the generated table name:
To configure a custom name for a table, which will be used in actual SQL queries, pass it to the name parameter of the Table constructor.
Some databases, like H2, fold unquoted identifiers to upper case. To keep table name case-sensitivity, manually quote the provided argument:
Depending on what DBMS you use, the types of columns could be different in actual SQL queries.
Constraints
Nullable
The NOT NULL SQL constraint restricts the column to accept the null value. By default, Exposed applies this constraint to all the columns. To allow the column to be nullable, apply the .nullable() method to a definition of an appropriate column.
For example, to make the population column nullable, use the following code:
Default
The DEFAULT SQL constraint provides the default value for the column. Exposed supports three methods for configuring default values:
.default(defaultValue: T)accepts a value with a type of the column..defaultExpression(defaultValue: Expression<T>)accepts an expression..clientDefault(defaultValue: () -> T)accepts a function.
For example, to configure the default value for the name column, use the following code:
Exposed also supports marking a column as databaseGenerated if the default value of the column is not known at the time of table creation and/or if it depends on other columns. It makes it possible to omit setting a value for the column when inserting a new record, without getting an error. The value for the column can be set by creating a TRIGGER or with a DEFAULT clause, for example.
For example:
Index
The INDEX SQL constraint makes traversing through tables quicker. Exposed supports the .index() method. It has six parameters, most of which are optional:
customIndexName: String? = nullA custom name for the index, which will be used in actual SQL queries.
unique: BooleanDefines whether the index is unique or not.
columns: List<Column<*>>Defines a column set.
functions: List<ExpressionWithColumnType<*>>? = nullDefines functional key parts.
indexType: String? = nullA custom type. Can be
"BTREE"or"HASH".filterCondition: (() -> Op<Boolean>)? = nullDefines a condition used to create a partial index.
The simplest way to create an index is to use an extension function directly on a column. For example, to apply a non-unique INDEX constraint to the name column, use the following code:
If the customIndexName parameter is not set, the name of the index is determined by the table and column names.
Complex indexes
If you have a frequent query for two columns, Exposed can perform it more efficiently. It creates a tree from the first column with the references to the second one. For example, to create a non-unique complex index on the name and population columns, paste the following code:
Index with a custom type
Exposed also supports creating an index with a custom type. For example, to retrieve data from the name column faster with a hash function for traversing, use the following code:
Some databases support functional key parts that index expressions instead of columns directly:
Operator expressions, like plus(), are also accepted by the functions parameter.
Some databases support creating a partial index by defining a filter expression to improve querying performance. The created index will only contain entries for the table rows that match this predicate:
Access indices
Once a table has been created, the list of its indices can be accessed using the property Table.indices. Table indices are represented by the data class Index, so its properties can be checked in the following way:
Unique
The UNIQUE SQL constraint restricts duplicates within this column. Exposed supports the .uniqueIndex() method which creates a unique index for the column. This method is the composition of UNIQUE and INDEX constraint, the quicker modification of UNIQUE constraint.
For example, to apply UNIQUE and INDEX constraint to the name column, use the following code:
Primary Key
The PRIMARY KEY SQL constraint applied to a column means each value in that column identifies the row. This constraint is the composition of NOT NULL and UNIQUE constraints. To change the column set, add columns, or change the primary key name to a custom one, override this field of the table class.
For example, to define the name column as the primary key, use the following code. The "Cities_name" string will be used as the constraint name in the actual SQL query, if provided; otherwise a name will be generated based on the table's name.
It is also possible to define a primary key on a table using multiple columns:
Except for CompositeIdTable, each available class in Exposed that inherits from IdTable has the primaryKey field automatically defined. For example, the IntIdTable by default has an auto-incrementing integer column, id, which is defined as the primary key.
An IdTable that requires a primary key with multiple columns can be defined using CompositeIdTable. In this case, each column that is a component of the table's ID should be identified by .entityId():
If any of the key component columns have already been marked by .entityId() in another table, they can still be identified using addIdColumn(). This might be useful for key columns that reference another IdTable:
Foreign Key
The FOREIGN KEY SQL constraint links two tables. A foreign key is a column from one table that refers to the primary key or columns with a unique index from another table. To configure a foreign key on a column, use reference() or optReference() methods. The latter lets the foreign key accept a null value. To configure a foreign key on multiple columns, use the foreignKey() function directly within an init block.
reference() and optReference() methods have several parameters:
name: StringA name for the foreign key column, which will be used in actual SQL queries.
ref: Column<T>A target column from another parent table.
onDelete: ReferenceOption? = nullAn action for when a linked row from a parent table will be deleted.
onUpdate: ReferenceOption? = nullAn action for when a value in a referenced column will be changed.
fkName: String? = nullA name for the foreign key constraint.
Enum class ReferenceOption has five values:
RESTRICTAn option that restricts changes on a referenced column, and the default option for most dialects.
NO_ACTIONThe same as
RESTRICTin some, but not all, databases, and the default option for Oracle and SQL Server dialects.CASCADEAn option that allows updating or deleting the referring rows.
SET_NULLAn option that sets the referring column values to
null.SET_DEFAULTAn option that sets the referring column values to the default value.
Consider the following Citizens table. This table has the name and city columns. If the Cities table has configured the name column as the primary key, the Citizens table can refer to it by its city column, which is a foreign key. To configure such reference and make it nullable, use the optReference() method:
If any Cities row will be deleted, the appropriate Citizens row will be deleted too.
If instead the Cities table has configured multiple columns as the primary key (for example, both id and name columns as in the above section), the Citizens table can refer to it by using a table-level foreign key constraint. In this case, the Citizens table must have defined matching columns to store each component value of the Cities table's primary key:
In the above example, the order of the referencing columns in foreignKey() must match the order of columns defined in the target primary key. If this order is uncertain, the foreign key can be defined with explicit column associations instead:
Check
The CHECK SQL constraint checks that all values in a column match some condition. Exposed supports the .check() method. You apply this method to a column and pass the appropriate condition to it.
For example, to check that the name column contains strings that begin with a capital letter, use the following code:
Some databases, like older MySQL versions, may not support CHECK constraints. For more information, consult the relevant documentation.
Creating tables
To create a table within a database, you need to use the SchemaUtils.create() method within a transaction:
This will generate the SQL necessary to create the table based on your definition.