Mapear directamente una clase local time de JDK 8 en un bean de entidad

Con el JDK 8 se introdujo una nueva API para gestionar el tiempo, mucho mas completa que la anterior (clase Date y clase Calendar).

Con esta nueva versión de la API se aumenta la precisión de milisegundos a nanosegundos (En la API anterior esta la clase clase Timestamp, que permitia la gestión de nanosegundos).

Esta nueva API nos añade principalmente las siguientes clases:

  • LocalDateTime que permite gestionar la fecha y la hora hasta los nanosegundos sin zona horaria. Se asume la zona horaria local del sistema.
  • LocalDate que permite gestionar la fecha sin zona horaria. Se asume la zona horaria local del sistema.
  • LocalTime que permite gestionar la hora hasta los nanosegundos sin zona horaria. Se asume la zona horaria local del sistema.

Para mapear estas nuevas clases directamente en los bean de entidad se han definido los siguientes tipos Hibernate:

  • LocalDateTimeUserType: permite mapear objetos LocalDateTime a tipo TIMESTAP o DATETIME de SQL.
  • LocalDateUserType: permite mapear objetos LocalDate a tipo DATE de SQL.
  • LocalDateFromTimestampUserType: permite mapear objetos LocalDate a tipo TIMESTAMP de SQL.
  • LocalDateUserType: permite mapear objetos LocalDate a tipo DATE de SQL.
  • LocalTimeUserType: permite mapear objetos LocalTime a tipo TIME de SQL.

Podes utilizar los anteriores tipos de conversores de Hibernate de dos maneras: * Utilizando la anotación en la definición de la columna del bean de entidad. * Utilizando el fichero package-info.java para registrar los tipos en el paquete. De esta forma no ahce falta registrarlo en la definición de la columna.

Utilizar un Tipo en la Definición de la Columna

Veamos varios ejemplos, uno por cada tipo de conversor. Empecemos por el LocalDateTimeUserType, que lo hemos registrado en el método getDateTime() del bean de entidad LocalDateTimeUserEntity con la anotación @Type( type = LocalDateTimeUserType.TYPE ).

package es.um.atica.hibernate.type.entities;
 
import java.time.LocalDateTime;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
 
import org.hibernate.annotations.Type;
 
@Entity
@Table( name = "LOCAL_DATE_TIME" )
public class LocalDateTimeUserEntity {
 
 
	private int id;
	private LocalDateTime dateTime; // yyyy-MM-dd hh:mm:ss.nnnnnnnnn
 
	public LocalDateTimeUserEntity() {
 
	}
 
	public LocalDateTimeUserEntity( int id, LocalDateTime dateTime ) {
		this.id = id;
		this.dateTime = dateTime;
	}
 
	@Id
	@Column( name = "ID", nullable = false )
	public int getId() {
		return id;
	}
 
	public void setId( int id ) {
		this.id = id;
	}
 
	@Type( type = LocalDateTimeUserType.TYPE )
	@Column( name = "LOCAL_DATE_TIME", nullable = false )
	public LocalDateTime getDateTime() {
		return dateTime;
	}
 
	public void setDateTime( LocalDateTime dateTime ) {
		this.dateTime = dateTime;
	}
 
	@Override
	public String toString() {
		return "LocalDateTimeUserEntity [id=" + id + ", dateTime=" + dateTime + "]";
	}
}

Continuamos por el LocalDateUserType, que lo hemos registrado en el método getDate() del bean de entidad LocalDateUserEntity con la anotación @Type( type = LocalDateUserType.TYPE ).

package es.um.atica.hibernate.type.entities;
 
import java.time.LocalDate;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
 
import org.hibernate.annotations.Type;
 
@Entity
@Table( name = "LOCAL_DATE" )
public class LocalDateUserEntity {
 
	private int id;
	private LocalDate date; // yyyy-MM-dd
 
	public LocalDateUserEntity() {
 
	}
 
	public LocalDateUserEntity( int id, LocalDate date ) {
		this.id = id;
		this.date = date;
	}
 
	@Id
	@Column( name = "ID", nullable = false )
	public int getId() {
		return id;
	}
 
	public void setId( int id ) {
		this.id = id;
	}
 
	@Type( type = LocalDateUserType.TYPE )
	@Column( name = "LOCAL_DATE", nullable = false )
	public LocalDate getDate() {
		return date;
	}
 
	public void setDate( LocalDate date ) {
		this.date = date;
	}
 
	@Override
	public String toString() {
		return "LocalTimesUserEntity [id=" + id + ", date=" + date + "]";
	}
 
}

Para terminar, vemos el LocalTimeUserType, que lo hemos registrado en el método getTime() del bean de entidad LocalTimeUserEntity con la anotación @Type( type = LocalTimeUserType.TYPE ).

package es.um.atica.hibernate.type.entities;
 
import java.time.LocalTime;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
 
import org.hibernate.annotations.Type;
 
@Entity
@Table( name = "LOCAL_TIME" )
public class LocalTimeUserEntity {
 
	private int id;
	private LocalTime time; // hh:mm:ss.nnnnnnnnn
 
	public LocalTimeUserEntity() {
 
	}
 
	public LocalTimeUserEntity( int id, LocalTime time ) {
		this.id = id;
		this.time = time;
	}
 
	@Id
	@Column( name = "ID", nullable = false )
	public int getId() {
		return id;
	}
 
	public void setId( int id ) {
		this.id = id;
	}
 
	@Type( type = LocalTimeUserType.TYPE )
	@Column( name = "LOCAL_TIME", nullable = false )
	public LocalTime getTime() {
		return time;
	}
 
	public void setTime( LocalTime time ) {
		this.time = time;
	}
 
	@Override
	public String toString() {
		return "LocalTimesUserEntity [id=" + id + ", time=" + time + "]";
	}
 
}

Utilizar el fichero package-info.java

Utilizando el fichero package-info.java podemos registrar los tipos de conversor de hibernate para un paquete do código, y ya no haría falta utilizar la anotación @Type en la definición de la columna. Veamos un ejemplo con el paquete de código es.um.atica.hibernate.type.entities:

@TypeDefs( {
		@TypeDef( name = "localDateType", defaultForType = LocalDate.class, typeClass = LocalDateUserType.class ),
		@TypeDef(	name = "localDateTimeType",
					defaultForType = LocalDateTime.class,
					typeClass = LocalDateTimeUserType.class ),
		@TypeDef( name = "localTimeType", defaultForType = LocalTime.class, typeClass = LocalTimeUserType.class )
} )
package es.um.atica.hibernate.type.entities;
 
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
 
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;
 
import es.um.atica.hibernate.type.LocalDateTimeUserType;
import es.um.atica.hibernate.type.LocalDateUserType;
import es.um.atica.hibernate.type.LocalDateFromTimestampUserType;
import es.um.atica.hibernate.type.LocalTimeUserType;

En aplicaciones FundeWeb 1.5 el import de las clases viene del paquete org.umu.atica.jpa.types, quedando:

import org.umu.atica.jpa.types.LocalDateTimeUserType;
import org.umu.atica.jpa.types.LocalDateUserType;
import org.umu.atica.jpa.types.LocalDateFromTimestampUserType;
import org.umu.atica.jpa.types.LocalTimeUserType;

Aunque Eclipse deberia de importarlas automaticamente.

Ahora nos queda añadir la siguiente propiedad packagesToScan en el fichero persistence.xml:

	<persistence-unit name="defaultPU" transaction-type="JTA">
		...
 
		<properties>
			<!-- Hibernate properties -->
 			...			
 
 			<property name="packagesToScan" value="es.um.atica.hibernate.type.entities" />
 
		</properties>
	</persistence-unit>

Ya no es necesario hacer nada más.

  • fdw2.0/fundeweb2.0/gt/jpa/mapeos_local_time_jdk8.txt
  • Última modificación: 20/05/2022 11:33
  • por JUAN MIGUEL BERNAL GONZALEZ