Hibernate.orgCommunity Documentation

Chapter 3. Basic Types

Table of Contents

3.1. Hibernate-provided BasicTypes
3.2. The @Basic annotation
3.3. The @Column annotation
3.4. BasicTypeRegistry
3.5. Explicit BasicTypes
3.6. Custom BasicTypes
3.7. Mapping enums
3.7.1. @Enumerated
3.7.2. AttributeConverter
3.7.3. Custom type
3.8. Mapping LOBs
3.9. Mapping Nationalized Character Data
3.10. Mapping UUID Values
3.10.1. UUID as binary
3.10.2. UUID as (var)char
3.10.3. PostgeSQL-specific UUID
3.10.4. UUID as identifier
3.11. Mapping Date/Time Values
3.12. JPA 2.1 AttributeConverters

Basic value types usually map a single database value, or column, to a single, non-aggregated Java type. Hibernate provides a number of built-in basic types, which follow the natural mappings recommended in the JDBC specifications.

Internally Hibernate uses a registry of basic types when it needs to resolve the specific org.hibernate.type.Type to use in certain situations.

Table 3.1. Standard BasicTypes

Hibernate type (org.hibernate.type package)JDBC typeJava typeBasicTypeRegistry key(s)
StringTypeVARCHARjava.lang.Stringstring, java.lang.String
MaterializedClobCLOBjava.lang.Stringmaterialized_clob
TextTypeLONGVARCHARjava.lang.Stringtext
CharacterTypeCHARchar, java.lang.Characterchar, java.lang.Character
BooleanTypeBITboolean, java.lang.Booleanboolean, java.lang.Boolean
NumericBooleanTypeINTEGER, 0 is false, 1 is trueboolean, java.lang.Booleannumeric_boolean
YesNoTypeCHAR, 'N'/'n' is false, 'Y'/'y' is true. The uppercase value is written to the database.boolean, java.lang.Booleanyes_no
TrueFalseTypeCHAR, 'F'/'f' is false, 'T'/'t' is true. The uppercase value is written to the database.boolean, java.lang.Booleantrue_false
ByteTypeTINYINTbyte, java.lang.Bytebyte, java.lang.Byte
ShortTypeSMALLINTshort, java.lang.Shortshort, java.lang.Short
IntegerTypesINTEGERint, java.lang.Integerint, java.lang.Integer
LongTypeBIGINTlong, java.lang.Longlong, java.lang.Long
FloatTypeFLOATfloat, java.lang.Floatfloat, java.lang.Float
DoubleTypeDOUBLEdouble, java.lang.Doubledouble, java.lang.Double
BigIntegerTypeNUMERICjava.math.BigIntegerbig_integer, java.math.BigInteger
BigDecimalTypeNUMERICjava.math.BigDecimalbig_decimal, java.math.bigDecimal
TimestampTypeTIMESTAMPjava.sql.Timestamptimestamp, java.sql.Timestamp
TimeTypeTIMEjava.sql.Timetime, java.sql.Time
DateTypeDATEjava.sql.Datedate, java.sql.Date
CalendarTypeTIMESTAMPjava.util.Calendarcalendar, java.util.Calendar
CalendarDateTypeDATEjava.util.Calendarcalendar_date
CurrencyTypejava.util.CurrencyVARCHARcurrency, java.util.Currency
LocaleTypeVARCHARjava.util.Localelocale, java.utility.locale
TimeZoneTypeVARCHAR, using the TimeZone IDjava.util.TimeZonetimezone, java.util.TimeZone
UrlTypeVARCHARjava.net.URLurl, java.net.URL
ClassTypeVARCHAR (class FQN)java.lang.Classclass, java.lang.Class
BlobTypeBLOBjava.sql.Blobblog, java.sql.Blob
ClobTypeCLOBjava.sql.Clobclob, java.sql.Clob
BinaryTypeVARBINARYbyte[]binary, byte[]
MaterializedBlobTypeBLOBbyte[]materized_blob
ImageTypeLONGVARBINARYbyte[]image
WrapperBinaryTypeVARBINARYjava.lang.Byte[]wrapper-binary, Byte[], java.lang.Byte[]
CharArrayTypeVARCHARchar[]characters, char[]
CharacterArrayTypeVARCHARjava.lang.Character[]wrapper-characters, Character[], java.lang.Character[]
UUIDBinaryTypeBINARYjava.util.UUIDuuid-binary, java.util.UUID
UUIDCharTypeCHAR, can also read VARCHARjava.util.UUIDuuid-char
PostgresUUIDTypePostgreSQL UUID, through Types#OTHER, which complies to the PostgreSQL JDBC driver definitionjava.util.UUIDpg-uuid
SerializableTypeVARBINARYimplementors of java.lang.Serializable Unlike the other value types, multiple instances of this type are registered. It is registered once under java.io.Serializable, and registered under the specific java.io.Serializable implementation class names.
StringNVarcharTypeNVARCHARjava.lang.Stringnstring
NTextTypeLONGNVARCHARjava.lang.Stringntext
NClobTypeNCLOBjava.sql.NClobnclob, java.sql.NClob
MaterializedNClobTypeNCLOBjava.lang.Stringmaterialized_nclob
PrimitiveCharacterArrayNClobTypeNCHARchar[]N/A
CharacterNCharTypeNCHARjava.lang.Characterncharacter
CharacterArrayNClobTypeNCLOBjava.lang.Character[]N/A


These mappings are managed by a service inside Hibernate called the org.hibernate.type.BasicTypeRegistry, which essentially maintains a map of org.hibernate.type.BasicType (a org.hibernate.type.Type specialization) instances keyed by a name. That is the purpose of the "BasicTypeRegistry key(s)" column in the previous tables. We will revisit this detail later.

Strictly speaking, a basic type is denoted with the javax.persistence.Basic annotation. Generally speaking the @Basic annotation can be ignored. Both of the following examples are ultimately the same.



The @Basic annotation defines 2 attributes.

  • optional - boolean (defaults to true) - Defines whether this attribute allows nulls. JPA defines this as "a hint", which essentially means that it affect is specifically required. As long as the type is not primitive, Hibernate takes this to mean that the underlying column should be NULLABLE.

  • fetch - FetchType (defaults to EAGER) - Defines whether this attribute should be fetched eagerly or lazily. JPA says that EAGER is a requirement to the provider (Hibernate) that the value should be fetched when the owner is fetched but that LAZY is merely a hint that the value be fetched when the attribute is accessed. Hibernate ignores this setting for basic types unless you are using bytecode enhancement. See the Hibernate User Guide for additional information on fetching and on bytecode enhancement.

JPA defines rules for implicitly determining the name of tables and columns. For a detailed discussion of implicit naming see ???.

For basic type attributes, the implicit naming rule is that the column name is the same as the attribute name. If that implicit naming rule does not meet your requirements, you can explicitly tell Hibernate (and other providers) the column name to use.


Here we use @Column to explicitly map the description attribute to the NOTES column, as opposed to the implicit column name description.

The @Column annotation defines other mapping information as well. See its javadocs for details.

We said before that a Hibernate type is not a Java type, nor a SQL type, but that it understands both and performs the marshalling between them. But looking at the basic type mappings from the previous examples, how did Hibernate know to use its org.hibernate.type.StringType for mapping for java.lang.String attributes or its org.hibernate.type.IntegerType for mapping java.lang.Integer attributes?

The answer lies in a service inside Hibernate called the org.hibernate.type.BasicTypeRegistry, which essentially maintains a map of org.hibernate.type.BasicType (a org.hibernate.type.Type specialization) instances keyed by a name.

We will see later (Section 3.5, “Explicit BasicTypes”) that we can explicitly tell Hibernate which BasicType to use for a particular attribute. But first let's explore how implicit resolution works and how applications can adjust implicit resolution.

Note

A thorough discussion of the BasicTypeRegistry and all the different ways to contribute types to it is beyond the scope of this documentation. Please see Integrations Guide for complete details.

As an example, take a String attribute such as we saw before with Product#sku. Since there was no explicit type mapping, Hibernate looks to the BasicTypeRegistry to find the registered mapping for java.lang.String. This goes back to the "BasicTypeRegistry key(s)" column we saw in the tables at the start of this chapter.

As a baseline within BasicTypeRegistry, Hibernate follows the recommended mappings of JDBC for Java types. JDBC recommends mapping Strings to VARCHAR, which is the exact mapping that StringType handles. So that is the baseline mapping within BasicTypeRegistry for Strings.

Applications can also extend (add new BasicType registrations) or override (replace an exiting BasicType registration) using one of the MetadataBuilder#applyBasicType methods or the MetadataBuilder#applyTypes method during bootstrap. For more details, see Section 3.6, “Custom BasicTypes”

Sometimes you want a particular attribute to be handled differently. Occasionally Hibernate will implicitly pick a BasicType that you do not want (and for some reason you do not want to adjust the BasicTypeRegistry).

In these cases you must explicitly tell Hibernate the BasicType to use, via the org.hibernate.annotations.Type annotation.


This tells Hibernate to store the Strings as nationalized data. This is just for illustration purposes; for better ways to indicate nationalized character data see Section 3.9, “Mapping Nationalized Character Data”

Additionally the description is to be handled as a LOB. Again, for better ways to indicate LOBs see Section 3.8, “Mapping LOBs”.

The org.hibernate.annotations.Type#type attribute can name any of the following:

  • FQN of any org.hibernate.type.Type implementation

  • Any key registered with BasicTypeRegistry

  • The name of any known "type definitions"

Hibernate makes it relatively easy for developers to create their own basic type mappings type. For example, you might want to persist properties of type java.lang.BigInteger to VARCHAR columns, or support completely new types.

There are 2 approaches to developing a custom BasicType. As a means of illustrating the different approaches, lets consider a use case where we need to support a class called Fizzywig from a third party library. Lets assume that Fizzywig naturally stores as a VARCHAR.

The first approach is to directly implement the BasicType interface.


The second approach is to implement the UserType interface.


For additional information on developing and registering custom types, see the Hibernate Integration Guide.

Hibernate supports the mapping of Java enums as basic value types in a number of different ways.

You can also map enums using a Hibernate custom type mapping. Let's again revisit the Gender enum example, this time using a custom Type to store the more standardized 'M' and 'F' codes.

Example 3.10. Enum mapping with custom Type example

import org.hibernate.type.descriptor.java.CharacterTypeDescriptor;

@Entity
public class Person {
	...
	@Basic
	@Type( type = GenderType.class )
	public Gender gender;
}

public enum Gender {
	MALE( 'M' ),
	FEMALE( 'F' );

	private final char code;

	private Gender(char code) {
		this.code = code;
	}

	public char getCode() {
		return code;
	}

	public static Gender fromCode(char code) {
		if ( code == 'M' || code == 'm' ) {
			return MALE;
		}
		if ( code == 'F' || code == 'f' ) {
			return FEMALE;
		}
		throw ...
	}
}

@Converter
public class GenderType
		extends AbstractSingleColumnStandardBasicType<Gender> {

	public static final GenderType INSTANCE = new GenderType();

	private GenderType() {
		super(
				CharTypeDescriptor.INSTANCE,
				GenderJavaTypeDescriptor.INSTANCE
		);
	}

	public String getName() {
		return "gender";
	}

	@Override
	protected boolean registerUnderJavaType() {
		return true;
	}
}

public static class GenderJavaTypeDescriptor
		extends AbstractTypeDescriptor<Gender> {
	public static final GenderJavaTypeDescriptor INSTANCE = new GenderJavaTypeDescriptor();

	public String toString(Gender value) {
		return value == null ? null : value.name();
	}

	public Gender fromString(String string) {
		return string == null ? null : Gender.valueOf( string );
	}

	public <X> X unwrap(Gender value, Class<X> type, WrapperOptions options) {
		return CharacterTypeDescriptor.INSTANCE.unwrap(
				value == null ? null : value.getCode(),
				type,
				options
		);
	}

	public <X> Gender wrap(X value, WrapperOptions options) {
		return CharacterTypeDescriptor.INSTANCE.wrap( value, options );
	}
}

Again, the gender column is defined as a CHAR type and would hold:

  • NULL - null

  • 'M' - MALE

  • 'F' - FEMALE

For additional details on using custom types, see Section 3.6, “Custom BasicTypes”.

Mapping LOBs (database Large OBjects) come in 2 forms, those using the JDBC locator types and those materializing the LOB data.

The JDBC LOB locator types include:

  • java.sql.Blob

    java.sql.Clob

    java.sql.NClob

Mapping materialized forms of these LOB values would use more familiar Java types such as String, char[], byte[], etc. The trade off for "more familiar" is usually performance.

For a first look lets assume we have a CLOB column that we would like to map (NCLOB character LOB data will be covered in Section 3.9, “Mapping Nationalized Character Data”).


Let's first map this using the JDBC locator.


We could also map a materialized form.


Note

How JDBC deals with LOB data varies from driver to driver. Hibernate tries to handle all these variances for you. However some drivers do not allow Hibernate to always do that in an automatic fashion (looking directly at you PostgreSQL JDBC drivers). In such cases you may have to do some extra to get LOBs working. Such discussions are beyond the scope of this guide however.

We might even want the materialized data as a char array (for some crazy reason).


We'd map BLOB data in a similar fashion.


Let's first map this using the JDBC locator.


We could also map a materialized BLOB form.


JDBC 4 added the ability to explicitly handle nationalized character data. To this end it added specific nationalized character data types.

  • NCHAR

    NVARCHAR

    LONGNVARCHAR

    NCLOB

To map a specific attribute to a nationalized variant datatype, Hibernate defines the @Nationalized annotation.




If you application and database are entirely nationalized you may instead want to enable nationalized character data as the default. You can do this via the hibernate.use_nationalized_character_data setting or by calling MetadataBuilder#enableGlobalNationalizedCharacterDataSupport during bootstrap.

Hibernate also allows you to map UUID values, again in a number of ways.

Note

The default UUID mapping is as binary because it represents more efficient storage. However many applications prefer the readability of character storage. To switch the default mapping, simply call MetadataBuilder.applyBasicType( UUIDCharType.INSTANCE, UUID.class.getName() )

blah blah blah

blah blah blah