Mostrando entradas con la etiqueta entidad. Mostrar todas las entradas
Mostrando entradas con la etiqueta entidad. Mostrar todas las entradas

viernes, 9 de mayo de 2014

Usando Hibernate para acceder a DB2. Lecciones aprendidas

A la hora de desarrollar una aplicación Java con Hibernate y que acceda a una base de datos DB2 se deben tener en cuenta ciertas características de esta Base de Datos que la hacen especial y pueden ocasionar ciertos problemas.

1)DB2 siempre añade espacios al final de las cadenas.

Solución:
Se puede crear un UserType de Hibernate que actue como "traductor" entre las cadenas recién extraídas mediante la consulta SQL y el String final que se obtendrá en Java. Mas detalles de este tema pueden ser vistos en el articulo previo.

2)Se obtiene un 

THE STRING REPRESENTATION OF A DATETIME VALUE IS NOT A VALID DATETIME VALUE

DB2 SQL Error: SQLCODE=-181, SQLSTATE=22007, SQLERRMC=null, DRIVER=3.62.56 

cuando se intenta guardar una fecha de tipo java.util.Date

Solución:
Hibernate y su dialecto para DB2 mapean los objetos de tipo java.util.Date como Sql Timestamp. Esto tiene como consecuencia que se produce un desbordamiento al almacenar la fecha en una columna de tipo Date debido a que los Timestamp poseen mayor precision que Date. La solución es indicar de alguna forma en la entidad de Java que no se desea que implicitamente se mapee a Timestamp. Esto puede ser conseguido de varias maneras. 

Una de ellas sería cambiar el tipo de las fechas en la entidad a java.sql.Date, aunque esto implicaría que nuestro modelo de datos fuese dependiente de las fechas del package sql. 

La otra y mas recomendable es añadir @Type(type="date") a la fecha declarada en la entidad. Esto modificaría la conversión por defecto haciendo que en lugar de enviar un Timestamp al Sql se envie un Date.

lunes, 24 de febrero de 2014

Creando un UserType de hibernate que se aplique a todas las entidades.

Hibernate posee un mecanismo muy potente para controlar la manera en que ocurre la conversion de valores obtenidos de la fuente de datos a valores Java. Se trata de los UserTypes. Por ejemplo, si utilizas una Base de Datos muy antigua que mantiene siempre al final de cada cadena espacios en blanco hasta rellenar la longitud de la columna, mediante un UserType puedes redefinir la manera en que la cadena es leida de la base de datos y escrita en el String de destino de Java. Para manipular dicho proceso basta con implementar una clase heredera que redefina algunos métodos y dejar que el framework realice el resto

package es.hib.usertype;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

public class TrimmedString implements UserType {

public TrimmedString() {
super();
}

@Override
public int[] sqlTypes() {
return new int[] { Types.VARCHAR };
}

@Override
public Class returnedClass() {
return String.class;
}

@Override
public boolean equals(Object x, Object y) throws HibernateException {
return (x == y) || (x != null && y != null && (x.equals(y)));
}

@Override
public Object deepCopy(Object value) throws HibernateException {
if (value == null)
return null;
return new String((String) value);
}

@Override
public boolean isMutable() {
return false;
}

@Override
public Object assemble(Serializable cached, Object owner) throws

 HibernateException {
return cached;
}

@Override
public Serializable disassemble(Object obje) throws 

HibernateException {
return (Serializable) obje;
}

@Override
public int hashCode(Object obje) throws HibernateException {
return obje.hashCode();
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, 

SessionImplementor arg2, Object owner) throws HibernateException,
SQLException {

String val = rs.getString(names[0]);

if (null == val) {

return (null);
}
return val.trim();
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, 

int index, SessionImplementor arg3) throws HibernateException,
SQLException {
st.setString(index, (String) value);
}

@Override
public Object replace(Object original, Object target, Object owner) 

throws HibernateException {

return original;
}
}

Ya teniendo el UserType implementado puede decidirse si usarse localmente

@Type(type="es.hib.usertype.TrimmedString")
@Column(name = "DESCRIPCION")
private String descripcion;

o globalmente, o sea, aplicarlo a la conversion de todos los VARCHAR en String de la aplicación. Para esto se debe crear en el paquete que contiene las entidades el siguiente archivo package-info.java. El mismo es una anotación a nivel de paquetes que surtirá el mismo efecto que si se anotaran una a una todas las clases del paquete dado.

@org.hibernate.annotations.TypeDef(name = "trimmedString", 
defaultForType = String.class, typeClass = TrimmedString.class)
package es.hib.entities;

import es.hib.usertype.TrimmedString;

Después de creado el package-info.java, ya solo se necesita que hibernate lo note. Para esto debe de agregarse,  programaticamente cuando se esten cargando las configuraciones añadir la siguiente sentencia.

configuration.addPackage("es.hib.entities")