• No se han encontrado resultados

El acusativo preposicional en perspectiva comparativa romance

We'll work with the verson of SourceMedia.java shown in Example 6-1. Our custom type will allow this class to be persisted without any changes from its original form. In other words, the design of our data classes can be dictated by the needs and semantics of the application alone, and we can move the persistence support into a separate class focused on that sole purpose. This is a much better division of labor.

We'll call our new class SourceMediaType. Our next decision is whether it needs to implement UserType or CompositeUserType. The reference documentation doesn't provide much guidance on this question, but the API documentation confirms the hint contained in the interface names: the CompositeUserType interface is only needed if your custom type implementation is to expose internal structure in the form of named properties that can be accessed individually in queries (as in our ZIP code example). For SourceMedia, a simple UserType implementation is

sufficient. The source for a mapping manager meeting our needs is shown in Example 7-1.

Example 7-1. SourceMediaType.java, our custom type mapping handler

package com.oreilly.hh; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import net.sf.hibernate.UserType; import net.sf.hibernate.Hibernate; import net.sf.hibernate.HibernateException; import net.sf.hibernate.type.Type; /**

* Manages persistence for the {@link SourceMedia} typesafe enumeration. */

public class SourceMediaType implements UserType {

/**

* Indicates whether objects managed by this type are mutable. *

* @return <code>false</code>, since enumeration instances are immutable * singletons.

*/

public boolean isMutable() { return false;

}

/**

* Return a deep copy of the persistent state, stopping at * entities and collections.

*

* @param value the object whose state is to be copied.

* @return the same object, since enumeration instances are singletons. * @throws ClassCastException for non {@link SourceMedia} values.

*/

public Object deepCopy(Object value) { return (SourceMedia)value;

} /**

* Compare two instances of the class mapped by this type for persistence * "equality".

*

* @param x first object to be compared. * @param y second object to be compared.

* @return <code>true</code> iff both represent the same SourceMedia type. * @throws ClassCastException if x or y isn't a {@link SourceMedia}.

*/

public boolean equals(Object x, Object y) {

// We can compare instances, since SourceMedia are immutable singletons return (x == y);

}

/**

* Determine the class that is returned by {@link #nullSafeGet}. *

* @return {@link SourceMedia}, the actual type returned * by {@link #nullSafeGet}.

*/

public Class returnedClass() { return SourceMedia.class; }

/**

* Determine the SQL type(s) of the column(s) used by this type mapping. *

* @return a single VARCHAR column. */

public int[] sqlTypes() {

// Allocate a new array each time to protect against callers changing // its contents. int[] typeList = { Types.VARCHAR }; return typeList; } /**

* Retrieve an instance of the mapped class from a JDBC {@link ResultSet}. *

* @param rs the results from which the instance should be retrieved. * @param names the columns from which the instance should be retrieved. * @param owner the entity containing the value being retrieved.

* @return the retrieved {@link SourceMedia} value, or <code>null</code>. * @throws HibernateException if there is a problem performing the mapping. * @throws SQLException if there is a problem accessing the database.

*/

public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException

{

// Start by looking up the value name

String name = (String) Hibernate.STRING.nullSafeGet(rs, names[0]); if (name == null) {

return null; }

// Then find the corresponding enumeration value try {

return SourceMedia.getInstanceByName(name); }

catch (java.util.NoSuchElementException e) {

throw new HibernateException("Bad SourceMedia value: " + name, e); }

}

/**

* Write an instance of the mapped class to a {@link PreparedStatement}, * handling null values.

*

* @param st a JDBC prepared statement.

* @param value the SourceMedia value to write.

* @param index the parameter index within the prepared statement at which * this value is to be written.

* @throws HibernateException if there is a problem performing the mapping. * @throws SQLException if there is a problem accessing the database.

*/

public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException

{

String name = null; if (value != null)

name = ((SourceMedia)value).getName(); Hibernate.STRING.nullSafeSet(st, name, index); }

}

All of the methods in this class are required by the UserType interface. Our implementations are quite brief and straightforward, as befits the simple mapping we've undertaken. The first three methods don't need any discussion beyond what's in the JavaDoc and inline comments.

The sqlTypes() method reports to Hibernate the number of columns that will be needed to store values managed by this custom type and the SQL types. We indicate that our type uses a single VARCHAR column.

Since the API specifies that this information is to be returned as an array, safe coding practices dictate that we create and return a new array on each call, to protect against malicious or buggy code that might manipulate the contents of the array. (Java has no support for immutable arrays. It would have been slightly preferable if the UserType interface declared this method to return a Collection or List, since these can be immutable.)

In nullSafeGet() we translate database results into the corresponding MediaSource enumeration value. Since we know we stored the value as a string in the database, we can delegate the actual retrieval to Hibernate's utility method for loading strings from database results. You'll be able to do something like this in most cases. Then it's just a matter of using the enumeration's own instance lookup capability.

Mapping the other direction is handled by nullSafeSet(). Once again we can rely on built-in features of the

enumeration to translate from a MediaSource instance to its name, and then use Hibernate's utilities to store this string in the database.

In all the methods dealing with values, it's important to write your code in a way that will not crash if any of the arguments are null, as they often will be. The 'nullSafe' prefix in some method names is a reminder of this, but even the equals() method must be careful. Blindly delegating to x.equals(y) would blow up if x is null.

7.2 Using a Custom Type Mapping

All right, we've created a custom type persistence handler, and it wasn't so bad! Now it's time to actually use it to persist our enumeration data the way we want it.