Hibernate.org Community Documentation
As an Object/Relational Mapping solution, Hibernate deals with both the Java and JDBC representations of application data. An online catalog application, for example, most likely has Product
object with a number of attributes such as a sku
, name
, etc. For these individual attributes, Hibernate must be able to read the values out of the database and write them back. This 'marshalling' is the function of a Hibernate type, which is an implementation of the org.hibernate.type.Type
interface. In addition, a Hibernate type describes various aspects of behavior of the Java type such as "how is equality checked?" or "how are values cloned?".
重要
A Hibernate type is neither a Java type nor a SQL datatype; it provides a information about both.
When you encounter the term type in regards to Hibernate be aware that usage might refer to the Java type, the SQL/JDBC type or the Hibernate type.
Hibernate categorizes types into two high-level groups: value types (see 第 6.1 节 “Value types”) and entity types (see 第 6.2 节 “Entity types”).
The main distinguishing characteristic of a value type is the fact that they do not define their own lifecycle. We say that they are "owned" by something else (specifically an entity, as we will see later) which defines their lifecycle. Value types are further classified into 3 sub-categories: basic types (see 第 6.1.1 节 “Basic value types”), composite types (see 第 6.1.2 节 “Composite types”) amd collection types (see 第 6.1.3 节 “Collection types”).
The norm for basic value types is that they map a single database value (column) to a single, non-aggregated Java type. Hibernate provides a number of built-in basic types, which we will present in the following sections by the Java type. Mainly these follow the natural mappings recommended in the JDBC specification. We will later cover how to override these mapping and how to provide and use alternative type mappings.
-
org.hibernate.type.StringType
-
Maps a string to the JDBC VARCHAR type. This is the standard mapping for a string if no Hibernate type is specified.
Registered under
string
andjava.lang.String
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.MaterializedClob
-
Maps a string to a JDBC CLOB type
Registered under
materialized_clob
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.TextType
-
Maps a string to a JDBC LONGVARCHAR type
Registered under
text
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.CharacterType
-
Maps a char or
java.lang.Character
to a JDBC CHARRegistered under
char
andjava.lang.Character
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BooleanType
-
Maps a boolean to a JDBC BIT type
Registered under
boolean
andjava.lang.Boolean
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.NumericBooleanType
-
Maps a boolean to a JDBC INTEGER type as 0 = false, 1 = true
Registered under
numeric_boolean
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.YesNoType
-
Maps a boolean to a JDBC CHAR type as ('N' | 'n') = false, ( 'Y' | 'y' ) = true
Registered under
yes_no
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.TrueFalseType
-
Maps a boolean to a JDBC CHAR type as ('F' | 'f') = false, ( 'T' | 't' ) = true
Registered under
true_false
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.ByteType
-
Maps a byte or
java.lang.Byte
to a JDBC TINYINTRegistered under
byte
andjava.lang.Byte
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.ShortType
-
Maps a short or
java.lang.Short
to a JDBC SMALLINTRegistered under
short
andjava.lang.Short
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.IntegerTypes
-
Maps an int or
java.lang.Integer
to a JDBC INTEGERRegistered under
int
andjava.lang.Integer
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.LongType
-
Maps a long or
java.lang.Long
to a JDBC BIGINTRegistered under
long
andjava.lang.Long
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.FloatType
-
Maps a float or
java.lang.Float
to a JDBC FLOATRegistered under
float
andjava.lang.Float
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.DoubleType
-
Maps a double or
java.lang.Double
to a JDBC DOUBLERegistered under
double
andjava.lang.Double
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BigIntegerType
-
Maps a
java.math.BigInteger
to a JDBC NUMERICRegistered under
big_integer
andjava.math.BigInteger
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BigDecimalType
-
Maps a
java.math.BigDecimal
to a JDBC NUMERICRegistered under
big_decimal
andjava.math.BigDecimal
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.TimestampType
-
Maps a
java.sql.Timestamp
to a JDBC TIMESTAMPRegistered under
timestamp
,java.sql.Timestamp
andjava.util.Date
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.TimeType
-
Maps a
java.sql.Time
to a JDBC TIMERegistered under
time
andjava.sql.Time
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.DateType
-
Maps a
java.sql.Date
to a JDBC DATERegistered under
date
andjava.sql.Date
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.CalendarType
-
Maps a
java.util.Calendar
to a JDBC TIMESTAMPRegistered under
calendar
,java.util.Calendar
andjava.util.GregorianCalendar
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.CalendarDateType
-
Maps a
java.util.Calendar
to a JDBC DATERegistered under
calendar_date
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.CurrencyType
-
Maps a
java.util.Currency
to a JDBC VARCHAR (using the Currency code)Registered under
currency
andjava.util.Currency
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.LocaleType
-
Maps a
java.util.Locale
to a JDBC VARCHAR (using the Locale code)Registered under
locale
andjava.util.Locale
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.TimeZoneType
-
Maps a
java.util.TimeZone
to a JDBC VARCHAR (using the TimeZone ID)Registered under
timezone
andjava.util.TimeZone
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.UrlType
-
Maps a
java.net.URL
to a JDBC VARCHAR (using the external form)Registered under
url
andjava.net.URL
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.ClassType
-
Maps a
java.lang.Class
to a JDBC VARCHAR (using the Class name)Registered under
class
andjava.lang.Class
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BlobType
-
Maps a
java.sql.Blob
to a JDBC BLOBRegistered under
blob
andjava.sql.Blob
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.ClobType
-
Maps a
java.sql.Clob
to a JDBC CLOBRegistered under
clob
andjava.sql.Clob
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BinaryType
-
Maps a primitive byte[] to a JDBC VARBINARY
Registered under
binary
andbyte[]
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.MaterializedBlobType
-
Maps a primitive byte[] to a JDBC BLOB
Registered under
materialized_blob
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.ImageType
-
Maps a primitive byte[] to a JDBC LONGVARBINARY
Registered under
image
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.BinaryType
-
Maps a java.lang.Byte[] to a JDBC VARBINARY
Registered under
wrapper-binary
,Byte[]
andjava.lang.Byte[]
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.CharArrayType
-
Maps a char[] to a JDBC VARCHAR
Registered under
characters
andchar[]
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.CharacterArrayType
-
Maps a java.lang.Character[] to a JDBC VARCHAR
Registered under
wrapper-characters
,Character[]
andjava.lang.Character[]
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.UUIDBinaryType
-
Maps a java.util.UUID to a JDBC BINARY
Registered under
uuid-binary
andjava.util.UUID
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.UUIDCharType
-
Maps a java.util.UUID to a JDBC CHAR (though VARCHAR is fine too for existing schemas)
Registered under
uuid-char
in the type registry (see 第 6.5 节 “Type registry”). -
org.hibernate.type.PostgresUUIDType
-
Maps a java.util.UUID to the PostgreSQL UUID data type (through
Types#OTHER
which is how the PostgreSQL JDBC driver defines it).Registered under
pg-uuid
in the type registry (see 第 6.5 节 “Type registry”).
-
org.hibernate.type.SerializableType
-
Maps implementors of java.lang.Serializable to a JDBC VARBINARY
Unlike the other value types, there are multiple instances of this type. It gets registered once under
java.io.Serializable
. Additionally it gets registered under the specificjava.io.Serializable
implementation class names.
注意
The Java Persistence API calls these embedded types, while Hibernate traditionally called them components. Just be aware that both terms are used and mean the same thing in the scope of discussing Hibernate.
Components represent aggregations of values into a single Java type. For example, you might have an Address class that aggregates street, city, state, etc information or a Name class that aggregates the parts of a person's Name. In many ways a component looks exactly like an entity. They are both (generally speaking) classes written specifically for the application. They both might have references to other application-specific classes, as well as to collections and simple JDK types. As discussed before, the only distinguishing factory is the fact that a component does not own its own lifecycle nor does it define an identifier.
重要
It is critical understand that we mean the collection itself, not its contents. The contents of the collection can in turn be basic, component or entity types (though not collections), but the collection itself is owned.
Collections are covered in 第 7 章 集合映射(Collection mappings).
The definition of entities is covered in detail in 第 4 章 持久化类(Persistent Classes). For the purpose of this discussion, it is enough to say that entities are (generally application-specific) classes which correlate to rows in a table. Specifically they correlate to the row by means of a unique identifier. Because of this unique identifier, entities exist independently and define their own lifecycle. As an example, when we delete a Membership
, both the User
and Group
entities remain.
注意
This notion of entity independence can be modified by the application developer using the concept of cascades. Cascades allow certain operations to continue (or "cascade") across an association from one entity to another. Cascades are covered in detail in 第 8 章 关联关系映射.
Why do we spend so much time categorizing the various types of types? What is the significance of the distinction?
The main categorization was between entity types and value types. To review we said that entities, by nature of their unique identifier, exist independently of other objects whereas values do not. An application cannot "delete" a Product sku; instead, the sku is removed when the Product itself is deleted (obviously you can update the sku of that Product to null to make it "go away", but even there the access is done through the Product).
Nor can you define an association to that Product sku. You can define an association to Product based on its sku, assuming sku is unique, but that is totally different.
TBC...
Hibernate makes it relatively easy for developers to create their own value types. For example, you might want to persist properties of type java.lang.BigInteger
to VARCHAR
columns. Custom types are not limited to mapping values to a single table column. So, for example, you might want to concatenate together FIRST_NAME
, INITIAL
and SURNAME
columns into a java.lang.String
.
There are 3 approaches to developing a custom Hibernate type. As a means of illustrating the different approaches, lets consider a use case where we need to compose a java.math.BigDecimal
and java.util.Currency
together into a custom Money
class.
The first approach is to directly implement the org.hibernate.type.Type
interface (or one of its derivatives). Probably, you will be more interested in the more specific org.hibernate.type.BasicType
contract which would allow registration of the type (see 第 6.5 节 “Type registry”). The benefit of this registration is that whenever the metadata for a particular property does not specify the Hibernate type to use, Hibernate will consult the registry for the exposed property type. In our example, the property type would be Money
, which is the key we would use to register our type in the registry:
例 6.1. Defining and registering the custom Type
public class MoneyType implements BasicType { public String[] getRegistrationKeys() {
return new String[] { Money.class.getName() };
}
public int[] sqlTypes(Mapping mapping) {
// We will simply use delegation to the standard basic types for BigDecimal and Currency for many of the
// Type methods...
return new int[] {
BigDecimalType.INSTANCE.sqlType(),
CurrencyType.INSTANCE.sqlType(),
};
// we could also have honored any registry overrides via...
//return new int[] {
// mappings.getTypeResolver().basic( BigDecimal.class.getName() ).sqlTypes( mappings )[0],
// mappings.getTypeResolver().basic( Currency.class.getName() ).sqlTypes( mappings )[0]
//};
}
public Class getReturnedClass() {
return Money.class;
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException {
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SessionImplementor session)
throws SQLException {
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
BigDecimalType.INSTANCE.set( st, money.getAmount(), index );
CurrencyType.INSTANCE.set( st, money.getCurrency(), index+1 );
}
}
...
}
Configuration cfg = new Configuration();
cfg.registerTypeOverride( new MoneyType() );
cfg...;
重要
It is important that we registered the type before adding mappings.
注意
Both org.hibernate.usertype.UserType
and org.hibernate.usertype.CompositeUserType
were originally added to isolate user code from internal changes to the org.hibernate.type.Type
interfaces.
The second approach is the use the org.hibernate.usertype.UserType
interface, which presents a somewhat simplified view of the org.hibernate.type.Type
interface. Using a org.hibernate.usertype.UserType
, our Money
custom type would look as follows:
例 6.2. Defining the custom UserType
public class MoneyType implements UserType {
public int[] sqlTypes() {
return new int[] {
BigDecimalType.INSTANCE.sqlType(),
CurrencyType.INSTANCE.sqlType(),
};
}
public Class getReturnedClass() {
return Money.class;
}
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
BigDecimalType.INSTANCE.set( st, money.getAmount(), index );
CurrencyType.INSTANCE.set( st, money.getCurrency(), index+1 );
}
}
...
}
There is not much difference between the org.hibernate.type.Type
example and the org.hibernate.usertype.UserType
example, but that is only because of the snippets shown. If you choose the org.hibernate.type.Type
approach there are quite a few more methods you would need to implement as compared to the org.hibernate.usertype.UserType
.
The third and final approach is the use the org.hibernate.usertype.CompositeUserType
interface, which differs from org.hibernate.usertype.UserType
in that it gives us the ability to provide Hibernate the information to handle the composition within the Money
class (specifically the 2 attributes). This would give us the capability, for example, to reference the amount
attribute in an HQL query. Using a org.hibernate.usertype.CompositeUserType
, our Money
custom type would look as follows:
例 6.3. Defining the custom CompositeUserType
public class MoneyType implements CompositeUserType {
public String[] getPropertyNames() {
// ORDER IS IMPORTANT! it must match the order the columns are defined in the property mapping
return new String[] { "amount", "currency" };
}
public Type[] getPropertyTypes() {
return new Type[] { BigDecimalType.INSTANCE, CurrencyType.INSTANCE };
}
public Class getReturnedClass() {
return Money.class;
}
public Object getPropertyValue(Object component, int propertyIndex) {
if ( component == null ) {
return null;
}
final Money money = (Money) component;
switch ( propertyIndex ) {
case 0: {
return money.getAmount();
}
case 1: {
return money.getCurrency();
}
default: {
throw new HibernateException( "Invalid property index [" + propertyIndex + "]" );
}
}
}
public void setPropertyValue(Object component, int propertyIndex, Object value) throws HibernateException {
if ( component == null ) {
return;
}
final Money money = (Money) component;
switch ( propertyIndex ) {
case 0: {
money.setAmount( (BigDecimal) value );
break;
}
case 1: {
money.setCurrency( (Currency) value );
break;
}
default: {
throw new HibernateException( "Invalid property index [" + propertyIndex + "]" );
}
}
}
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws SQLException {
assert names.length == 2;
BigDecimal amount = BigDecimalType.INSTANCE.get( names[0] ); // already handles null check
Currency currency = CurrencyType.INSTANCE.get( names[1] ); // already handles null check
return amount == null && currency == null
? null
: new Money( amount, currency );
}
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws SQLException {
if ( value == null ) {
BigDecimalType.INSTANCE.set( st, null, index );
CurrencyType.INSTANCE.set( st, null, index+1 );
}
else {
final Money money = (Money) value;
BigDecimalType.INSTANCE.set( st, money.getAmount(), index );
CurrencyType.INSTANCE.set( st, money.getCurrency(), index+1 );
}
}
...
}
Internally Hibernate uses a registry of basic types (see 第 6.1.1 节 “Basic value types”) when it needs to resolve the specific org.hibernate.type.Type
to use in certain situations. It also provides a way for applications to add extra basic type registrations as well as override the standard basic type registrations.
To register a new type or to override an existing type registration, applications would make use of the registerTypeOverride
method of the org.hibernate.cfg.Configuration
class when bootstrapping Hibernate. For example, lets say you want Hibernate to use your custom SuperDuperStringType
; during bootstrap you would call:
例 6.4. Overriding the standard StringType
Configuration cfg = ...;
cfg.registerTypeOverride( new SuperDuperStringType() );
The argument to registerTypeOverride
is a org.hibernate.type.BasicType
which is a specialization of the org.hibernate.type.Type
we saw before. It adds a single method:
例 6.5. Snippet from BasicType.java
/**
* Get the names under which this type should be registered in the type registry.
*
* @return The keys under which to register this type.
*/
public String[] getRegistrationKeys();
One approach is to use inheritance (SuperDuperStringType
extends org.hibernate.type.StringType
); another is to use delegation.
版权 © 2004 Red Hat, Inc.