Movatterモバイル変換


[0]ホーム

URL:


Hibernate.orgCommunity Documentation

Chapter 6. Types

Table of Contents

6.1. Value types
6.1.1. Basic value types
6.1.2. Composite types
6.1.3. Collection types
6.2. Entity types
6.3. Significance of type categories
6.4. Custom types
6.4.1. Custom types usingorg.hibernate.type.Type
6.4.2. Custom types usingorg.hibernate.usertype.UserType
6.4.3. Custom types usingorg.hibernate.usertype.CompositeUserType
6.5. Type registry

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 hasProduct object with a number of attributes such as asku,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 aHibernate type, which is an implementation of theorg.hibernate.type.Type interface. In addition, aHibernate type describes various aspects of behavior of the Java type such as "how is equality checked?" or "how are values cloned?".

Important

A Hibernate type is neither a Java type nor a SQL datatype; it provides a information about both.

When you encounter the termtype 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 (seeSection 6.1, “Value types”) and entity types (seeSection 6.2, “Entity types”).

6.1. Value 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 (seeSection 6.1.1, “Basic value types”), composite types (seeSection 6.1.2, “Composite types”) amd collection types (seeSection 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 understring andjava.lang.String in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.MaterializedClob

Maps a string to a JDBC CLOB type

Registered undermaterialized_clob in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.TextType

Maps a string to a JDBC LONGVARCHAR type

Registered undertext in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CharacterType

Maps a char orjava.lang.Character to a JDBC CHAR

Registered underchar andjava.lang.Character in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BooleanType

Maps a boolean to a JDBC BIT type

Registered underboolean andjava.lang.Boolean in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.NumericBooleanType

Maps a boolean to a JDBC INTEGER type as 0 = false, 1 = true

Registered undernumeric_boolean in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.YesNoType

Maps a boolean to a JDBC CHAR type as ('N' | 'n') = false, ( 'Y' | 'y' ) = true

Registered underyes_no in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.TrueFalseType

Maps a boolean to a JDBC CHAR type as ('F' | 'f') = false, ( 'T' | 't' ) = true

Registered undertrue_false in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.ByteType

Maps a byte orjava.lang.Byte to a JDBC TINYINT

Registered underbyte andjava.lang.Byte in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.ShortType

Maps a short orjava.lang.Short to a JDBC SMALLINT

Registered undershort andjava.lang.Short in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.IntegerTypes

Maps an int orjava.lang.Integer to a JDBC INTEGER

Registered underint andjava.lang.Integerin the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.LongType

Maps a long orjava.lang.Long to a JDBC BIGINT

Registered underlong andjava.lang.Long in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.FloatType

Maps a float orjava.lang.Float to a JDBC FLOAT

Registered underfloat andjava.lang.Float in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.DoubleType

Maps a double orjava.lang.Double to a JDBC DOUBLE

Registered underdouble andjava.lang.Double in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BigIntegerType

Maps ajava.math.BigInteger to a JDBC NUMERIC

Registered underbig_integer andjava.math.BigInteger in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BigDecimalType

Maps ajava.math.BigDecimal to a JDBC NUMERIC

Registered underbig_decimal andjava.math.BigDecimal in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.TimestampType

Maps ajava.sql.Timestamp to a JDBC TIMESTAMP

Registered undertimestamp,java.sql.Timestamp andjava.util.Date in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.TimeType

Maps ajava.sql.Time to a JDBC TIME

Registered undertime andjava.sql.Time in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.DateType

Maps ajava.sql.Date to a JDBC DATE

Registered underdate andjava.sql.Date in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CalendarType

Maps ajava.util.Calendar to a JDBC TIMESTAMP

Registered undercalendar,java.util.Calendar andjava.util.GregorianCalendar in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CalendarDateType

Maps ajava.util.Calendar to a JDBC DATE

Registered undercalendar_date in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CurrencyType

Maps ajava.util.Currency to a JDBC VARCHAR (using the Currency code)

Registered undercurrency andjava.util.Currency in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.LocaleType

Maps ajava.util.Locale to a JDBC VARCHAR (using the Locale code)

Registered underlocale andjava.util.Locale in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.TimeZoneType

Maps ajava.util.TimeZone to a JDBC VARCHAR (using the TimeZone ID)

Registered undertimezone andjava.util.TimeZone in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.UrlType

Maps ajava.net.URL to a JDBC VARCHAR (using the external form)

Registered underurl andjava.net.URL in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.ClassType

Maps ajava.lang.Class to a JDBC VARCHAR (using the Class name)

Registered underclass andjava.lang.Class in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BlobType

Maps ajava.sql.Blob to a JDBC BLOB

Registered underblob andjava.sql.Blob in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.ClobType

Maps ajava.sql.Clob to a JDBC CLOB

Registered underclob andjava.sql.Clob in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BinaryType

Maps a primitive byte[] to a JDBC VARBINARY

Registered underbinary andbyte[] in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.MaterializedBlobType

Maps a primitive byte[] to a JDBC BLOB

Registered undermaterialized_blob in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.ImageType

Maps a primitive byte[] to a JDBC LONGVARBINARY

Registered underimage in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.BinaryType

Maps a java.lang.Byte[] to a JDBC VARBINARY

Registered underwrapper-binary,Byte[] andjava.lang.Byte[] in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CharArrayType

Maps a char[] to a JDBC VARCHAR

Registered undercharacters andchar[] in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.CharacterArrayType

Maps a java.lang.Character[] to a JDBC VARCHAR

Registered underwrapper-characters,Character[] andjava.lang.Character[] in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.UUIDBinaryType

Maps a java.util.UUID to a JDBC BINARY

Registered underuuid-binary andjava.util.UUID in the type registry (seeSection 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 underuuid-char in the type registry (seeSection 6.5, “Type registry”).

org.hibernate.type.PostgresUUIDType

Maps a java.util.UUID to the PostgreSQL UUID data type (throughTypes#OTHER which is how the PostgreSQL JDBC driver defines it).

Registered underpg-uuid in the type registry (seeSection 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 underjava.io.Serializable. Additionally it gets registered under the specificjava.io.Serializable implementation class names.

Note

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.

Important

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 inChapter 7,Collection mapping.

6.2. Entity types

The definition of entities is covered in detail inChapter 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 aMembership, both theUser andGroup entities remain.

Note

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 inChapter 8,Association Mappings.

6.3. Significance of type categories

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 canupdate 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 associationto that Product sku. Youcan define an association to Productbased on its sku, assuming sku is unique, but that is totally different.

TBC...

6.4. Custom types

Hibernate makes it relatively easy for developers to create their ownvalue types. For example, you might want to persist properties of typejava.lang.BigInteger toVARCHAR columns. Custom types are not limited to mapping values to a single table column. So, for example, you might want to concatenate togetherFIRST_NAME,INITIAL andSURNAME columns into ajava.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 ajava.math.BigDecimal andjava.util.Currency together into a customMoney class.

The first approach is to directly implement theorg.hibernate.type.Type interface (or one of its derivatives). Probably, you will be more interested in the more specificorg.hibernate.type.BasicType contract which would allow registration of the type (seeSection 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 beMoney, which is the key we would use to register our type in the registry:

Example 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...;

Important

It is important that we registered the typebefore adding mappings.

Note

Bothorg.hibernate.usertype.UserType andorg.hibernate.usertype.CompositeUserType were originally added to isolate user code from internal changes to theorg.hibernate.type.Type interfaces.

The second approach is the use theorg.hibernate.usertype.UserType interface, which presents a somewhat simplified view of theorg.hibernate.type.Type interface. Using aorg.hibernate.usertype.UserType, ourMoney custom type would look as follows:

Example 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 theorg.hibernate.type.Type example and theorg.hibernate.usertype.UserType example, but that is only because of the snippets shown. If you choose theorg.hibernate.type.Type approach there are quite a few more methods you would need to implement as compared to theorg.hibernate.usertype.UserType.

The third and final approach is the use theorg.hibernate.usertype.CompositeUserType interface, which differs fromorg.hibernate.usertype.UserType in that it gives us the ability to provide Hibernate the information to handle the composition within theMoney class (specifically the 2 attributes). This would give us the capability, for example, to reference theamount attribute in an HQL query. Using aorg.hibernate.usertype.CompositeUserType, ourMoney custom type would look as follows:

Example 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 );        }    }    ...}

6.5. Type registry

Internally Hibernate uses a registry of basic types (seeSection 6.1.1, “Basic value types”) when it needs to resolve the specificorg.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 theregisterTypeOverride method of theorg.hibernate.cfg.Configuration class when bootstrapping Hibernate. For example, lets say you want Hibernate to use your customSuperDuperStringType; during bootstrap you would call:

Example 6.4. Overriding the standardStringType

Configuration cfg = ...;cfg.registerTypeOverride( new SuperDuperStringType() );

The argument toregisterTypeOverride is aorg.hibernate.type.BasicType which is a specialization of theorg.hibernate.type.Type we saw before. It adds a single method:

Example 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 extendsorg.hibernate.type.StringType); another is to use delegation.



[8]ページ先頭

©2009-2025 Movatter.jp