Hibernate Annotations con Spring
Para poder usar Annotations con Hibernate hay que incluir las siguientes librerías en el proyecto:
- hibernate-commons-annotations.jar
- hibernate-annotations.jar
- ejb3-persistence.jar
Estas librerías se encuentran en la distribucion hibernate-annotations-3.3.1.GA.zip que es la versión compatible para la actual version de Hibernate Core que estamos usando (3.2.6).
Utilizando annotations evitamos tener un archivo XML aparte por cada mapeo de objeto-tablas.
Las annotations se utilizan en los POJO's y pueden ubicarse en los miembros de la clase o en los getters de cada miembro.
Con Annotations no solo tenemos la ventaja de tener el mapeo cercano al codigo evitando problemas de sincronizacion entre el codigo y los archivos XML, sino que tambien nos evita escritura ya que cada propiedad (miembro) es considerada por default como persistente, por lo tanto si en la base de datos se llama igual que en la clase, no hay necesidad de explicitarlo.
Para utilizar las anotaciones de Hibernate junto con Spring Framework tenemos que reemplazar en el archivo application-hibernate.xml la clase org.springframework.orm.hibernate3.LocalSessionFactoryBean por org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean:
Contenido
Ejemplo de uso
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>com.app.domain.Cliente</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean>
Querys con Spring y Mapeos
@Entity @Table (name="cliente") //DECLARO LOS QUERIES @NamedQueries( {@NamedQuery(name="buscarPorIdCliente", query="select a from Cliente a where a.idCliente= ?"), @NamedQuery(name="buscarPorIdSucursal", query="select a from miTabla a where a.idSucursal= ?")}) public class Cliente{ @Id @Column (name="COD_CLIENTE") private Long id; @Column (name="COD_SUCURSAL") private String idSucursal; ................. }
DAO
public class ClienteDaoImpl extends HibernateDaoSupport implements ClienteDao { public Collection buscarPorIdCliente(String idCliente) { return getHibernateTemplate(). findByNamedQuery("buscarPorIdCliente", idCliente); } public Collection buscarPorIdSucursal(String idSucursal) { return getHibernateTemplate(). findByNamedQuery("buscarPorIdSucursal", idSucursal); } }
Mapeo de Recursos y Mapeo de Clases Anotadas
En el archivo de configuración application-hibernate.xml se puede mezclar el mapeo a archivos xml y a clases anotadas sin problemas. Esto nos será muy útil al momento de "migrar" una aplicación de mapeo en archivos xml a clases anotadas, pudiendo hacer la migración en forma progresiva.
La clase del SessionFactory que se debe utilizar en estos casos tambien es org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean.
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="annotatedClasses"> <list> <value>com.app.domain.Cliente</value> </list> </property> <property name="mappingResources"> <list> <value>com/app/domain/Cliente</value> </list> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.show_sql">true</prop> </props> </property> </bean>
Noten que mientras los mapeos por archivos XML DEBEN nombrarse con / (barra) entre los directorios, las clases anotadas DEBEN nombrarse con . (punto).
Anotando Colecciones
La forma de anotar una relación "uno a muchos" es la siguiente:
@Entity
@Table(name = "CLASE_PADRE")
public class ClasePadre {
@OneToMany(fetch = FetchType.EAGER) @JoinColumn(name = "ID_CLASE_PADRE") @Fetch(FetchMode.SELECT) private Collection<ClaseHija> hijas;
...
@Entity @Table(name = "CLASE_HIJA") public class ClaseHija {
@ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "ID_CLASE_PADRE") private ClasePadre padre;
El fetch type puede ser Lazy o Eager. Cuándo se setea en Eager, existe la posibilidad de que se obtengan los registros duplicados. Para evitar este comportamiento, existe el FetchMode.SELECT.
Anotaciones de fechas
@Column(name = "FEC_ALTA")
@Temporal(TemporalType.TIMESTAMP)
@org.hibernate.annotations.Generated(org.hibernate.annotations.GenerationTime.INSERT)
private Date fechaAlta;
Con la clase Generated se estará asignando la fecha default. En este caso, el default del campo es sysdate. Entonces, el campo FEC_ALTA quedará actualizado con el default del campo en la base de datos. Si la entidad se hubiere seteado con new Date, esa asignación no se tendría en cuenta y la fecha de la base de datos es la que si se tendría en cuenta.