Chapter 6. Supporting Custom Value Types

Table of Contents

6.1. Naked Objects AppLib Value Types
6.2. Custom Value Types
6.2.1. Simple Types (ImmutableUserType)
6.2.2. Composite Types (ImmutableCompositeUserType)

While Naked Objects supports a similar set of value types to JPA, it also allows custom value types to be defined using the @Value annotation. In addition to the built-in and custom value types, Naked Objects also has its own set of its own value types, such as Money and Percentage . These reside in the Naked Objects applib, in the org.nakedobjects.applib.value package.

So long as a value type is serializable, then JPA will be able to save the value in the database. However, the value will be stored as a blob, meaning for example it won't be possible to query on it within repositories. And if the value type is not serializable, then JPA will not be able to save it at all.

Hibernate provides a solution to this by allowing us to write (what it calls) user-defined types, through either the org.hibernate.usertype.UserType interface (for values that are persisted in a single column) or the org.hiberate.usertype.CompositeUserType (for more complex values that are persisted to multiple columns). These are analogous to the org.nakedobjects.applib.adapters.ValueSemanticsProviders that accompany Naked Objects' own @Value annotation: a ValueSemanticsProvider instructs Naked Objects viewers how to interact with a custom value, while a UserType instructs Hibernate how to persist/retrieve a value into a database table.

The JPA Objects' Application Library (or AppLib, see Section 1.1.3, “The JPA Objects AppLib”) defines several convenience superclasses to help write these UserType implementations. There are also out-of-the-box implementations to support Naked Objects' own value types (such as org.nakedobjects.applib.value.Money). Let's start off with these.

6.1. Naked Objects AppLib Value Types

Suppose we want to capture a Person's favorite colour, and choose to do this using Naked Objects built-in org.nakedobjects.applib.value.Color value type:

import org.nakedobjects.applib.value.Color;

@Entity
@DiscriminatorValue("PRS")
public class Person {

    ...
    public Color getFavoriteColor() { ... }
    ...
}

Only a single column is needed to encode the value, so the JPA Objects applib provides org.starobjects.jpa.applib.usertypes.ColorType, a UserType implementation to persist Colors. Here's how we use it:

import org.nakedobjects.applib.value.Color;
import org.starobjects.jpa.applib.usertypes.ColorType;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
...

@Entity
@DiscriminatorValue("PRS")
@TypeDefs({
    @TypeDef(name="nofcolor", typeClass=ColorType.class)
})
public class Person {

    ...
    @Type(type="nofcolor")
    public Color getFavoriteColor() { ... }
    ...
}

This sets up the "nofcolor" as an alias to the ColorType, and then says to use this alias for the favoriteColor property.

We'll have a look at the ColorType implementation in the below; if you skip ahead you'll see that the value stored is in fact an integer (corresponding to Color#intValue() method and the #Color(int) constructor).

Most of the other Naked Objects value types are also mapped using simple UserTypes. The exception is Money, which is mapped as a composite (a string column for the currency iso code, and a numeric amount). For example, if there is also a property of type Money for our fictitious Person class, then we would have:

import org.nakedobjects.applib.value.Money;
import org.starobjects.jpa.applib.usertypes.MoneyType;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
...

@Entity
@DiscriminatorValue("PRS")
@TypeDefs({
    @TypeDef(name="nofcolor", typeClass=ColorType.class),
    @TypeDef(name="nofmoney", typeClass=MoneyType.class)
})
public class Person {

    ...
    @Type(type="nofmoney")
    public Money getSalary() { ... }
    ...
}

Now we've seen how to use JPA Objects' predefined user types, let's see how to write them for our own value types.