Arquitectura RMI con Hibernate: Guía práctica de Controllers, DTO y Persistencia en Java
Arquitectura RMI con Hibernate en Java
Este documento describe la estructura de una aplicación cliente-servidor desarrollada con Java RMI y Hibernate, incluyendo el patrón Business Delegate, los controladores, los objetos DTO y el mapeo de relaciones de persistencia.
1. Lado del cliente
1.1 Controller
(en cliente) CONTROLLER (los métodos tienen remote y business exception)
- Instanciar el Controller.
- Crear el BusinessDelegate servidor: en el Controller, dentro de un
try, invocarsetServidor(new BusinessDelegate()); en los métodos agregarBusinessExceptionyRemoteException. - Ejemplo:
public Integer obtenerCantidadAlumnos() {
Integer resultado = null;
try {
resultado = getServidor().cantidadAlumnos();
} catch (RemoteException | BusinessException e) {
e.printStackTrace();
}
return resultado;
}1.2 Business Delegate (en repositorio)
- La clase implementa
IAppRemote. - Posee un atributo privado
IAppRemote businessService. - Constructor que establece la conexión con el servidor mediante
Naming.lookup:
public BusinessDelegate() throws BusinessException {
super();
try {
setBusinessService((IAppRemote) Naming.lookup(IAppRemote.URL_SERVICIO));
} catch (Exception e) {
throw new BusinessException(e.getMessage());
}
}- Ejemplo de método delegado:
public Integer cantidadAlumnos() throws RemoteException, BusinessException {
return getBusinessService().cantidadAlumnos();
}1.3 BusinessException (en repositorio)
BusinessException extiende de Exception e implementa Serializable. Su constructor recibe un mensaje: BusinessException(String mensaje) { super(mensaje); }.
2. Lado del servidor
2.1 Clase Server
- Instancia única de
Server. - Atributo privado
ObjetoRemoto objetoRemoto.
private Server() {
iniciar();
}
public void iniciar() {
try {
this.setObjetoRemoto(new ObjetoRemoto());
LocateRegistry.createRegistry(IAppRemote.PORT_SERVICIO);
Naming.bind(IAppRemote.URL_SERVICIO, getObjetoRemoto());
System.out.println("Fíjate en: " + IAppRemote.URL_SERVICIO + " puerto: " + IAppRemote.PORT_SERVICIO);
} catch (Exception e) {
e.printStackTrace();
}
}2.2 ServerMain (carpeta main del servidor)
public class ServerMain {
public static void main(String[] args) {
Server.getInstancia();
}
}2.3 DTO
Los DTO deben ser serializables y contener únicamente los atributos que se van a mostrar, con sus respectivos get y set.
2.4 DAO
Se instancia y contiene únicamente los métodos de acceso a datos. Flujo de datos: Base de datos → Entity → DAO → Objeto de negocio → Controller → DTO.
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("FROM AlumnoEntity a WHERE a.idAlumno = :id");
query.setParameter("id", id);
// query.list() o query.uniqueResult()2.5 Interfaz IAppRemote (repositorio)
IAppRemote extiende Remote, IAlumnoRemote, etc.
private static final String URL_SERVICIO = "localhost/parcial";
private static final Integer PORT_SERVICIO = 1099;
ClienteDTO altaCliente(ClienteDTO cliente) throws RemoteException, BusinessException;
ClienteDTO modificacionCliente(ClienteDTO cliente) throws RemoteException, BusinessException;2.6 ObjetoRemoto (carpeta RMI)
ObjetoRemoto extiende UnicastRemoteObject e implementa IAppRemote.
public class ObjetoRemoto extends UnicastRemoteObject implements IAppRemote {
public ObjetoRemoto() throws RemoteException {
super();
}
public Integer cantidadAlumnos() {
return AlumnoController.getInstancia().cantidadAlumno();
}
public List obtenerAlumnosXnota(Integer nota) {
return AlumnoController.getInstancia().alumnosPorNota(nota);
}
}2.7 Controller en el servidor
Posee una instancia del controlador y recibe objetos de negocio, devolviendo DTO.
public AlumnoDTO obtenerAlumnoPorId(Integer id) {
Alumno alumnoObjetoNegocio = AlumnoDao.getInstancia().obtenerAlumnoPorId1(id);
return alumnoObjetoNegocio.crearDto();
}
public List alumnosPorNota(Integer nota) {
List listaA = AlumnoDao2.getInstancia().obtenerAlumnosPorNotas(nota);
List listaAdto = null;
if (!listaA.isEmpty()) {
for (Alumno a : listaA) {
listaAdto.add(a.crearDto());
}
return listaAdto;
} else {
return null;
}
}Los métodos de creación de DTO se ubican en la clase de negocio (por ejemplo, Alumno).
public AlumnoDTO crearDto() {
AlumnoDTO dto = new AlumnoDTO();
return dto;
}
public AlumnoDTO toDto() {
AlumnoDTO dto = new AlumnoDTO();
return dto;
}2.8 Conversión de Entity a objeto de negocio
public Auto(AutoEntity entity) {
if (entity != null) {
setNumeroAuto(entity.getNumeroAuto());
setChasis(new Chasis(entity.getChasis()));
setMotor(new Motor(entity.getMotor()));
setEscuderia(new Escuderia(entity.getEscuderia()));
}
}Del mismo modo, los objetos de negocio deben tener un constructor que reciba su DTO, por ejemplo public Alumno(AlumnoDTO alumno), asignando setNombre(alumno.getNombre()) y setEstado(alumno.getEstado()), y lo mismo para la entidad AlumnoEntity.
3. Persistencia con Hibernate / JPA
3.1 OneToOne
Unidireccional: @OneToOne y @JoinColumn(name="numeroMotor"), donde numeroMotor es el nombre de la columna en la tabla que referencia a motor.
Bidireccional: igual que lo anterior, y en la otra clase se agrega @OneToOne(mappedBy="nombreDeLaClaseEnLaOtra").
3.2 ManyToOne (unidireccional o bidireccional)
@ManyToOne
@JoinColumn(name="ID_otra_clase")
private Departamento departamento;3.3 OneToMany (bidireccional con ManyToOne)
@OneToMany(mappedBy="departamento")
private List personas;3.4 ManyToMany
No existe @JoinColumn. Ejemplo sin tabla intermedia explícita: en la clase Empleado se define @ManyToMany private Collection projects;, y en la clase Proyecto, @ManyToMany(mappedBy="projects") private Collection employees;.
Con tabla intermedia explícita:
@ManyToMany
@JoinTable(name="aditivacion",
joinColumns=@JoinColumn(name="identificadorProducto"),
inverseJoinColumns=@JoinColumn(name="identificadorAditivo"))
private List aditivos;aditivacion: nombre de la tabla intermedia en la BD.identificadorProducto: columna del id de producto en la tabla intermedia.identificadorAditivo: columna del id de aditivo en la tabla intermedia.
3.5 Clave compuesta con entidad asociativa
AEntity
@OneToMany(cascade=CascadeType.ALL, mappedBy="key.entidadA")
private List listaAB;BEntity
@OneToMany(cascade=CascadeType.ALL, mappedBy="key.entidadB")
private List listaAB;ABEntity
@EmbeddedId
private ABEntityKey key;ABEntityKey
@Embeddable
public class ABEntityKey implements Serializable {
@ManyToOne(cascade=CascadeType.ALL, optional=false)
private AEntity entidadA;
@ManyToOne(cascade=CascadeType.ALL, optional=false)
private BEntity entidadB;
}4. HQL (Hibernate Query Language)
- Producto cartesiano:
FROM Usuario u, Permiso p WHERE u.id = 1 - Inner join:
FROM Usuario u INNER JOIN u.permisos AS p WITH p.estatus = 1 - Selección de relación:
SELECT dir FROM Usuario AS u INNER JOIN u.direccion AS dir - Conteo:
SELECT COUNT(*) FROM Ciclo - Filtro por nombre:
SELECT c.nombre FROM Ciclo c WHERE nombre = 'Desarrollo de aplicaciones Web' - Filtro por fecha:
SELECT tb FROM TiposBasicos tb WHERE dateDate = '2012-07-25' - Rango:
SELECT tb FROM TiposBasicos tb WHERE inte BETWEEN 1 AND 10 - Valores nulos:
SELECT tb FROM TiposBasicos tb WHERE dataDate IS NULL - Subconsulta:
SELECT c.nombre, c.horas FROM Ciclo c WHERE c.horas > (SELECT AVG(c2.horas) FROM Ciclo c2)
5. Configuración de Hibernate
hibernate.connection.driver_class=net.sourceforge.jtds.jdbc.Driver
hibernate.connection.url=jdbc:jtds:sqlserver://bd;databaseName=ParcialAD_22
hibernate.connection.username=ParcialAD_22
hibernate.connection.password=ParcialAD_22
hibernate.connection.pool_size=1
hibernate.dialect=org.hibernate.dialect.SQLServerDialect
hibernate.show_sql=false
hibernate.format_sql=true
hibernate.hbm2ddl.auto=validate
hibernate.transaction.flush_before_completion=true
hibernate.transaction.auto_close_session=true6. Clase HibernateUtil
public class HibernateUtil {
private static HibernateUtil instancia;
private SessionFactory sessionFactory;
private HibernateUtil() {
iniciar();
}
private void iniciar() {
try {
AnnotationConfiguration config = new AnnotationConfiguration();
config.addAnnotatedClass(MarcaEntity.class);
config.addAnnotatedClass(EscuderiaEntity.class);
sessionFactory = config.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
public static HibernateUtil getInstancia() {
if (instancia == null) {
instancia = new HibernateUtil();
}
return instancia;
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
}7. Resumen del flujo
Los controllers (por ejemplo, AlumnoController) son clases con instancia única. Sus métodos reciben objetos de negocio y devuelven DTO.