Vamos a montar un pequeño proyecto "Biblioteca" con un par de tablas, Autor y Libro relacionadas 1:N "un autor tiene varios libros". Lo montamos con diseño en tres capas con:
Hibernate
Spring
JPA
Maven2
JEE 1.6
Genericos Java
MySQL
La idea es trabajar lo mínimo, con lo que utilizaremos una clase
BaseDaoJpa para la capa de acceso a datos y una clase
GestorBaseImpl. Estas clases y sus correspondientes interfaces:
IBaseDao e
IGestorBase, las tenemos de proyectos anteriores o las podríamos tener en los arquetipos Maven2 que montásemos para construtir los esqueletos de nuestros proyectos.
Las clases son:
Capa de presentación:
AppTest.java. Clase con el código de la mini-aplicación. Vamos a entender este main como una posible capa de presentación. Se podría ver por ejemplo como un aplicativo Swing.
package org.dune.app;
import java.util.ArrayList;
import java.util.List;
import org.dune.model.Autor;
import org.dune.model.Libro;
import org.dune.negocio.IGestorAutor;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppTest {
public static void main(String[] args) {
//Carga del contexto
final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
//Recogemos el gestor de Autores
final IGestorAutor gestAutores = (IGestorAutor)ctx.getBean("gestorAutor");
//Damos de alta un autor
final Autor a = new Autor();
a.setNombre("nombre");
a.setApellido1("apellido1");
a.setApellido2("apellido2");
final Autor autorGrabado = gestAutores.save(a);
//Damos de alta algunos libros
final List<Libro> lstLibros = new ArrayList<Libro>();
final Libro lib1 = new Libro();
lib1.setTitulo("titulo1");
lib1.setAutor(autorGrabado);
final Libro lib2 = new Libro();
lib2.setTitulo("titulo1");
lib2.setAutor(autorGrabado);
lstLibros.add(lib1);
lstLibros.add(lib2);
autorGrabado.setLibros(lstLibros);
gestAutores.save(autorGrabado);
}
}
Capa de negocio:
IGestorAutor.java. Interfaz de los servicios de gestión de autores.
package org.dune.negocio;
import org.dune.model.Autor;
public interface IGestorAutor extends IGestorBase<Autor, Long> {
}
GestorAutorImpl.java. Implementación de los servicios de gestión de autores.
package org.dune.negocio.impl;
import org.dune.dao.IAutorDao;
import org.dune.model.Autor;
import org.dune.negocio.IGestorAutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("gestorAutor")
public class GestorAutorImpl extends GestorBaseImpl<Autor, Long> implements IGestorAutor {
private IAutorDao autorDao;
@Autowired
public GestorAutorImpl(IAutorDao autorDao) {
super(autorDao);
this.autorDao = autorDao;
}
}
IGestorBase.java. Interfaz base extendido por los restantes interfaces de servicios ofertados en la capa de negocio.
package org.dune.negocio;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import org.dune.dao.IBaseDao.Orden;
public interface IGestorBase<T, PK extends Serializable> {
public T get(PK id);
public List<T> getAll();
public List<T> getAllNoBaja(Date fechaBaja);
public void remove(PK id);
public T save(T obj);
public List<T> saveAll(List<T> l);
public List<T> getPaginado(int startPosition, int maxResult);
public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, Orden direccion);
}
GestorBaseImpl.java. Clase base de la que extienden todos los implementadores de servicios ofertados en la capa de nogocio.
package org.dune.negocio.impl;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dune.dao.IBaseDao;
import org.dune.dao.IBaseDao.Orden;
import org.dune.negocio.IGestorBase;
import org.springframework.transaction.annotation.Transactional;
public class GestorBaseImpl<T, PK extends Serializable> implements IGestorBase<T, PK> {
protected final Log log = LogFactory.getLog(getClass());
protected IBaseDao<T, PK> baseDao;
public GestorBaseImpl(IBaseDao<T, PK> baseDao) {
this.baseDao = baseDao;
}
@Transactional
public T get(PK id) {
return baseDao.get(id);
}
@Transactional
public List<T> getAll() {
return baseDao.getAll();
}
@Transactional
public List<T> getAllNoBaja(Date fechaBaja) {
return baseDao.getAllNoBaja(fechaBaja);
}
@Transactional
public void remove(PK id) {
baseDao.delete(id);
}
@Transactional
public T save(T obj) {
return baseDao.save(obj);
}
@Transactional
public List<T> saveAll(List<T> l) {
return baseDao.saveAll(l);
}
@Transactional
public List<T> getPaginado(int startPosition, int maxResult) {
return baseDao.getPaginado(startPosition, maxResult);
}
@Transactional
public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, Orden direccion) {
return baseDao.getPaginadoOrdenado(startPosition, maxResult, campo, direccion);
}
}
Capa de acceso a datos:
IAutorDao.java. Interfaz de la capa de acceso a datos para la tabla Autores.
package org.dune.dao;
import org.dune.model.Autor;
public interface IAutorDao extends IBaseDao<Autor, Long> {
}
AutorDaoJpa.java. Implementación JPA de la capa de acceso a datos para la tabla Autores.
package org.dune.dao.jpa;
import org.dune.dao.IAutorDao;
import org.dune.model.Autor;
import org.springframework.stereotype.Repository;
@Repository
public class AutorDaoJpa extends BaseDaoJpa<Autor, Long> implements IAutorDao {
public AutorDaoJpa() {
super(Autor.class);
}
}
IBaseDao.java. Interfaz base extendido por los restantes interfaces de la capa de acceso a datos.
package org.dune.dao;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public interface IBaseDao<T, PK extends Serializable> {
public static enum Orden {
ASC,
DESC;
};
public T get(PK id);
public List<T> getAll();
public List<T> getAllNoBaja(Date fechaBaja);
public T save(T t);
public List<T> saveAll(List<T> l);
public void delete(PK id);
public List<T> getPaginado(int startPosition, int maxResult);
public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, IBaseDao.Orden direccion);
}
BaseDaoJpa.java. Clase base de la que extienden todos los implementadores de la capa de acceso a datos.
package org.dune.dao.jpa;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.jpa.JpaTemplate;
import org.dune.dao.IBaseDao;
public class BaseDaoJpa<T, PK extends Serializable> implements IBaseDao<T, PK> {
protected final Log log = LogFactory.getLog(getClass());
private Class<T> persistentClass;
private JpaTemplate jpaTemplate;
public BaseDaoJpa(Class<T> persistentClass) {
this.persistentClass = persistentClass;
}
@Autowired
public void setEntityManager(EntityManager entityManager) {
this.jpaTemplate = new JpaTemplate(entityManager);
}
public JpaTemplate getJpaTemplate() {
return jpaTemplate;
}
public T get(PK id) {
final T t = (T) getJpaTemplate().find(persistentClass, id);
if (t == null) {
throw new ObjectRetrievalFailureException(persistentClass, id);
}
return t;
}
@SuppressWarnings("unchecked")
public List<T> getAll() {
final StringBuilder query = new StringBuilder("from ").append(persistentClass.getName()).append(" c ");
return (List<T>) getJpaTemplate().find(query.toString());
}
@SuppressWarnings("unchecked")
public List<T> getAllNoBaja(Date fechaBaja) {
final StringBuilder query = new StringBuilder("from ").append(persistentClass.getName()).append(" c").append(" where nvl(c.fechaBaja, to_date('31129999', 'DDMMYYYY')) > ?1");
return (List<T>) getJpaTemplate().find(query.toString(), fechaBaja);
}
public void delete(PK id) {
getJpaTemplate().remove(get(id));
}
public T save(T t) {
return (T) getJpaTemplate().merge(t);
}
public List<T> saveAll(List<T> l) {
if (l != null) {
for (T t : l) {
getJpaTemplate().merge(t);
}
}
return l;
}
@SuppressWarnings("unchecked")
public List<T> getPaginado(int startPosition, int maxResult) {
final StringBuilder sql = new StringBuilder("select c from ").append(persistentClass.getSimpleName()).append(
" c");
final Query query = getJpaTemplate().getEntityManager().createQuery(sql.toString());
query.setFirstResult(startPosition);
query.setMaxResults(maxResult);
return (List<T>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<T> getPaginadoOrdenado(int startPosition, int maxResult, String campo, IBaseDao.Orden direccion) {
final StringBuilder sql = new StringBuilder("select c from ").append(persistentClass.getSimpleName())
.append(" c order by c.").append(campo).append(" ").append(direccion);
final Query query = getJpaTemplate().getEntityManager().createQuery(sql.toString());
query.setFirstResult(startPosition);
query.setMaxResults(maxResult);
return (List<T>) query.getResultList();
}
}
Mapeos:
Autor.java. Clase que mapea la tabla Autor
package org.dune.model;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="AUTOR")
public class Autor {
protected Long id;
protected String nombre;
protected String apellido1;
protected String apellido2;
protected List<Libro> libros;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name="NOMBRE")
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
@Column(name="APE1")
public String getApellido1() {
return apellido1;
}
public void setApellido1(String apellido1) {
this.apellido1 = apellido1;
}
@Column(name="APE2")
public String getApellido2() {
return apellido2;
}
public void setApellido2(String apellido2) {
this.apellido2 = apellido2;
}
@OneToMany(mappedBy = "autor", cascade = CascadeType.ALL)
public List<Libro> getLibros() {
return libros;
}
public void setLibros(List<Libro> libros) {
this.libros = libros;
}
}
Libro.java. Clase que mapea la tabla Libro
package org.dune.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="LIBRO")
public class Libro {
protected Long id;
protected String titulo;
protected Autor autor;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name="TITULO")
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
@ManyToOne
@JoinColumn(name = "AUTOR")
public Autor getAutor() {
return autor;
}
public void setAutor(Autor autor) {
this.autor = autor;
}
}
Como elementos de configuración tendremos tres ficheros:
applicationContext.xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/biblioteca"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" <property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
</bean>
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven/>
<context:annotation-config base-package="org.dune" />
<context:component-scan base-package="org.dune" />
</beans>
persistence.xml.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="PERSISTENCE_UNIT" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>org.dune.model.Autor</class>
<class>org.dune.model.Libro</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
</properties>
</persistence-unit>
</persistence>
pom.xml.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dune</groupId>
<artifactId>Test</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>es.ine.sgtic.comun.lib.spring</groupId>
<artifactId>spring-2.5.0-lib</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.j2ee</groupId>
<artifactId>persistence</artifactId>
<version>9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.myfaces.1.2.5</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.0.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.comun.util.utilidades</groupId>
<artifactId>ine-util-cripto</artifactId>
<version>2.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.mysql.5.0.4</groupId>
<artifactId>mysql-connector-java-5.0.4-bin</artifactId>
<version>5.0.4</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.comun.lib.hibernate</groupId>
<artifactId>hibernate-3.2.5.GA-lib</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.jboss</groupId>
<artifactId>jboss-common</artifactId>
<version>1.2.1.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.jboss</groupId>
<artifactId>concurrent</artifactId>
<version>4.2.2.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.jboss</groupId>
<artifactId>javassist</artifactId>
<version>4.2.2.ga</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.jakarta-commons</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>es.ine.sgtic.lib.spring.2.5.0.j2ee</groupId>
<artifactId>jta</artifactId>
<version>1.1</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
</dependencies>
</project>