Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lect17-hibernate-orm / more / hibernate_reference.pdf
Скачиваний:
54
Добавлен:
18.03.2015
Размер:
2.2 Mб
Скачать

Chapter 6. Types

6.2. Entity types

The definition of entities is covered in detail in Chapter 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.

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 in Chapter 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 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...

6.4. Custom types

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.

6.4.1. Custom types using org.hibernate.type.Type

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

150

Custom types using org.hibernate.type.Type

org.hibernate.type.BasicType contract which would allow registration of the type (see

Section 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:

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 SQLE 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 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 );

}

}

151

Chapter 6. Types

...

}

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

Important

It is important that we registered the type before adding mappings.

6.4.2. Custom types using org.hibernate.usertype.UserType

Note

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:

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 );

152

Custom types using org.hibernate.usertype.CompositeUserType

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.

6.4.3. Custom types using org.hibernate.usertype.CompositeUserType

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:

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();

}

153

Chapter 6. Types

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 SQLE 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 SQL 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 );

}

}

...

}

154

Соседние файлы в папке more