This is an internal documentation. There is a good chance you’re looking for something else. See Disclaimer.

Custom user types

Per default Hibernate can map all primitive types (and its wrapper classes) as well as references to other entities. For all other classes that need to be mapped to the database an UserType must be implemented (for immutable types the base class ImmutableUserType can be used). The user type contains the logic how a specific object should be read from the ResultSet and written to the PreparedStatement.

For example the Login class is mapped with a custom user type (LoginUserType).

There are two ways to register a new user type:

It can be registered with a bootstrap contribution (see Participate in the bootstrap process). This way should be used if there is a distinct object which should be mapped (for example Login):

classLoaderService.addContribution(TypeContributor.class, ((typeContributions, serviceRegistry) -> {
    typeContributions.contributeType(new BinaryUserType(binaryAccessProvider, binaryHashingService), Binary.class.getName());
    typeContributions.contributeType(new LoginUserType(), Login.class.getName());
    typeContributions.contributeType(new UuidToStringUserType(), UUID.class.getName());

The alternative is to bind the user type to a field type (like phone) using a contribution:

public class PhoneUserTypeContribution extends UserTypeFieldContribution<PhoneUserType> {

    private final L10N l10n;
    private final PhoneFormatter phoneFormatter;

    public PhoneUserTypeContribution(L10N l10n,
                                     PhoneFormatter phoneFormatter) {
        this.l10n = l10n;
        this.phoneFormatter = phoneFormatter;

    public String getTypeName() {
        return "phone";

    protected Class<PhoneUserType> userTypeClass() {
        return PhoneUserType.class;

    protected PhoneUserType userType() {
        return new PhoneUserType(l10n, phoneFormatter);


User types are going to be deprecated in Hibernate 6+ and some refactoring will be required. One option would be to use an AttributeConverter.

Simple user types

Most user types are relatively simple and only map between a string or number and an object:

phone type

The PhoneUserType is applied for all field of the virtual phone type. This user type does not convert between different objects, but formats the phone number using the PhoneFormatter whenever a phone value is written to the database.

html type

Like the PhoneUserType, the HtmlUserType does not convert between different objects but does some string formatting for html fields.

The formatting behaviour can be contributed using a HtmlUserTypeExtension. Currently there is only the PreserveFreemarkerOperatorsHtmlUserTypeExtension which handles escaping in freemarker expressions.

binary type

The BinaryUserType handles the Binary class. The column of a binary field contains the hash code of the binary and references the _nice_binary table.

In addition to the mapping of the hash code this user type also calls the configured BinaryAccessProvider which stores the binary data if necessary.

User types are also used to map query parameters. If a Binary object is used as a query parameter, it should obviously not be attempted to write it to the binary data store! Therefore the binary content is only saved if Binary#mayBeStored() returns true. If a hash code is used as a query parameter for a binary field, the string is converted to a BinaryQueryParameter by the StringToBinaryParameterConverter. BinaryQueryParameter#mayBeStored() returns false so it can safely be used in queries.

See chapter Large objects for more details about large objects.

compressed-text type

The CompressedTextUserType is a sub-type of the string type. It compresses and decompresses the string data when writing and reading the field from the database. Zstd compression is used, the compression level can be configured using the persist.core.zstd.compression.level property (default value is 19).


This is useful for storing large text fields, but keep in mind that the content of the string cannot be used in a query, as only the compressed data is available on the database.