Simple types are those that can be mapped using a single column
(such as Color
, above). For these we subclass
from
org.starobjects.jpa.applib.usertypes.ImmutableUserType
.
Here's the implementation of ColorType
:
package org.starobjects.jpa.applib.usertypes; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.hibernate.Hibernate; import org.nakedobjects.applib.value.Color; public class ColorType extends ImmutableUserType { public Object nullSafeGet( final ResultSet rs, final String[] names, final Object owner) throws SQLException { final int color = rs.getInt(names[0]); if (rs.wasNull()) { return null; } return new Color(color); } public void nullSafeSet( final PreparedStatement st, final Object value, final int index) throws SQLException { if (value == null) { st.setNull(index, Hibernate.INTEGER.sqlType()); } else { st.setInt(index, ((Color) value).intValue()); } } public Class<Color> returnedClass() { return Color.class; } public int[] sqlTypes() { return new int[] { Hibernate.INTEGER.sqlType() }; } }
The nullSafeGet()
method is used to
extract the value from the SQL ResultSet
and
instantiate the Color
object. Conversely the
nullSafeSet()
method is used to read data
from the provided Color and set up the SQL
PreparedStatement
so the value can be inserted
or updated. The other two methods describe the structure of the data
being read/written.
Composite types are those that are mapped using more than one
column (such as Money
, above). For these we
subclass from
org.starobjects.jpa.applib.usertypes.ImmutableCompositeType
.
Here's the implementation of MoneyType
:
package org.starobjects.jpa.applib.usertypes; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import org.hibernate.Hibernate; import org.hibernate.engine.SessionImplementor; import org.hibernate.type.Type; import org.nakedobjects.applib.value.Money; public class MoneyType extends ImmutableCompositeType { public Class<Money> returnedClass() { return Money.class; } public Object nullSafeGet( final ResultSet resultSet, final String[] names, final SessionImplementor session, final Object owner) throws SQLException { final BigDecimal amount = resultSet.getBigDecimal(names[0]); if (resultSet.wasNull()) { return null; } final String currency = resultSet.getString(names[1]); return new Money(amount.doubleValue(), currency); } public void nullSafeSet( final PreparedStatement statement, final Object value, final int index, final SessionImplementor session) throws SQLException { if (value == null) { statement.setNull(index, Hibernate.BIG_DECIMAL.sqlType()); statement.setNull(index + 1, Hibernate.STRING.sqlType()); } else { final Money amount = (Money) value; statement.setBigDecimal(index, amount.getAmount()); statement.setString(index + 1, amount.getCurrency()); } } public String[] getPropertyNames() { return new String[] { "amount", "currency" }; } public Type[] getPropertyTypes() { return new Type[] { Hibernate.BIG_DECIMAL, Hibernate.STRING }; } public Object getPropertyValue(final Object component, final int property) { final Money monetaryAmount = (Money) component; if (property == 0) { return monetaryAmount.getAmount(); } else { return monetaryAmount.getCurrency(); } } public void setPropertyValue(final Object component, final int property, final Object value) { throw new UnsupportedOperationException("Money is immutable"); } }
This works in broadly the same way, with
nullSafeGet()
and
nullSafeSet()
used to read values from
SQL/write values to SQL. The
getPropertyNames()
and
getPropertyTypes()
again describe the
structure of the value. The
getPropertyValue()
allow specific properties
of the value (such as the Money
's currency) to
be read; like reading a single column in the database. The
setPropertyValue()
meanwhile should always
thrown an exception because value types should be immutable (replaced
in their entirety rather than modified in-situ).