This chapter describes how to use custom data types and database functions with Slick’s Scala API.
In the code examples below we assume the following imports:
If you’re new to Slick, please start with the Getting Started page.
If your database system supports a scalar function that is not available as a method in Slick you can define it as a SimpleFunction. There are predefined methods for creating unary, binary and ternary functions with fixed parameter and return types.
If you need more flexibility regarding the types (e.g. for varargs,
polymorphic functions, or to support Option and non-Option types in a single
function), you can use
SimpleFunction.apply to get an untyped instance and
write your own wrapper function with the proper type-checking:
For database functions that return complete tables or stored procedures please use Plain SQL Queries. Stored procedures that return multiple result sets are currently not supported.
If you need a custom column type you can implement ColumnType. The most common scenario is mapping an application-specific type to an already supported type in the database. This can be done much simpler by using MappedColumnType which takes care of all the boilerplate. It comes with the usual import from the profile.
You can also subclass MappedJdbcType for a bit more flexibility.
If you have a wrapper class (which can optionally be a case class and/or value
class) for an underlying value of some supported type, you can make it extend
MappedTo to get a macro-generated implicit
ColumnType for free. Such wrapper classes are commonly used for type-safe
table-specific primary key types:
Record types are data structures containing a statically known number of components with individually declared types. Out of the box, Slick supports Scala tuples (up to arity 22) and Slick’s own HList implementation. Record types can be nested and mixed arbitrarily.
In order to use custom record types (case classes, custom HLists, tuple-like types, etc.) in queries you need to tell Slick how to map them between queries and results. You can do that using a Shape extending MappedScalaProductShape.
The distinguishing feature of a polymorphic record type is that it abstracts over its element types, so you can use the same record type for both, lifted and plain element types. You can add support for custom polymorphic record types using an appropriate implicit Shape.
Here is an example for a type
The implicit method
pairShape in this example provides a Shape for a
Pair of two element types whenever Shapes for the individual element
types are available.
With these definitions in place, we can use the
Pair record type in every
location in Slick where a tuple or
HList would be acceptable:
Custom case classes are frequently used as monomorphic record types (i.e. record types where the element types are fixed). In order to use them in Slick, you need to define the case class for a record of plain values (as usual) plus an additional case class for a matching record of lifted values.
Note that this mechanism can be used as an alternative to client-side mappings
<> operator. It requires a bit more boilerplate but allows you to use
the same field names in both, plain and lifted records.
In the following example we are combining a mapped case class and the mapped
Pair type in another mapped case class.