Ejemplo de 3 capas con java

Iniciado por Rijhording, 25 Abril 2010, 08:21 AM

0 Miembros y 1 Visitante están viendo este tema.

sapito169

Bueno la respuesta que te voy a dar es algo rara
Algunos me van a criticar por querer programar todo lo más orientado a objetos porque creo que los patrones orientados a objetos pueden ser una gran fuente de solución de problemas que pueden reducir el código basura y te dan la posibilidad de crear código reutlsable incluso sin recompilar es por eso de descubrir que el trabajo en como yo lo e implementado no es muy orientado a objetos y es mas lo conocen como modelo anémico de objetos  :laugh: que ironía
Algunos me van a decir que tengo una opinión casi religiosa de la santa oo

Es por eso que voy a hacer el post un poco diferente
Otra cosa es que no puede hacer un código con el manejo de error que yo quisiera sin poner mucho código basura debido al uso de interfaces no se si alguien sabe algún patrón para solucionar eso

Cambiar a la forma en que trabajaba antes no va a ser tan difícil tampoco






sapito169

#21
Primero como cualquier talibán de la oo comienzan creando un paquete paquete que diga algo como entidad dominio lógica en mí caso estoy creando una aplicación de venta de platos para el restaurant de la tía veneno creo e paquete com.tiaveneno.ventas.compartido.modelo

Dentro creo la clase
Orden

Siguiendo algunas recomendaciones de mi compañero y aprendiz joshua bloch
Utilizo lo mas que pueda la palabra reservada final así me evito problemas de que algún despistado por error vuelva a re instanciar la clase y haga que explote todo
También evito crear relaciones de tipo gallina huevo por evitar problemas de loops infinitos como por ejemplo seria difícil crear el método tostring si la clase detalle de orden tenga una relación a orden además evito lo más posible utilizar el punto más de 2 veces por qué hace el código difícil de testear y crear clases mal diseñadas que hacen cosas que no le incumben evito poner cosas como

orden().getDetalle().getPlato().getPrecio

package com.tiaveneno.ventas.compartido.modelo;

Código (java) [Seleccionar]

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Orden {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private short numero;
@ManyToOne(cascade = CascadeType.ALL)
private Personal cajero;
@ManyToOne(cascade = CascadeType.ALL)
private Personal mozo;
@Temporal(TemporalType.DATE)
private Date fecha;

@OneToMany(cascade = CascadeType.ALL)
private final List<DetalleOrden> detalles = new ArrayList<DetalleOrden>();

public Orden(int id, short numero, Personal cajero, Personal mozo,
Date fecha) {
this.id = id;
this.numero = numero;
this.cajero = cajero;
this.mozo = mozo;
this.fecha = fecha;

}

public Orden() {
}

public static Orden nueva() {
return new Orden();
}

public Orden detalles(DetalleOrden... detalleOrdens) {
for (DetalleOrden detalleOrden : detalleOrdens) {
this.con(detalleOrden);
}

return this;
}

public Orden con(DetalleOrden detalleOrden) {

detalles.add(detalleOrden);
return this;
}

public double obtenerTotal() {
double total = 0;
for (DetalleOrden detalleOrden : detalles) {
total = total + detalleOrden.subtotal();
}
return total;
}

//orrible codigo boilepart inebitable getter y setters

public List<DetalleOrden> getDetalles() {
return detalles;
}

@Override
public String toString() {
return "Orden [id=" + id + ", numero=" + numero + ", cajero=" + cajero
+ ", mozo=" + mozo + ", fecha=" + fecha + ", detalles="
+ detalles + "]";
}

}



sapito169

Todas las clases persistentes deben estar anotadas con
@Entity

Todas las entidades debe tener un llave primaria en mi caso creo que la mejor opción es que sea de tipo identity no me gusta que el usuario tenga que siquiera ver las claves

@Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;

Tengo que mapear las relaciones entre clases creo que es más natural pensar que cuando guardamos una orden se deberían guardar automáticamente todos sus detalles por eso pongo cascadetype.all

@ManyToOne(cascade = CascadeType.ALL)
   private Personal cajero;

Un constructor sin parámetro es obligatorio

Además creo un método que le concierne a la orden y otros que solo sirven como azúcar sintáctico (solo te evitan escribir un poco)

detalles(DetalleOrden... detalleOrdens)
Orden con(DetalleOrden detalleOrden)
double obtenerTotal()


sapito169

#23
Luego creó una abstracción la base de datos para así poder cambiarme de proveedor de persistencia sin crear tanto problema por ejemplo es fácil cambiar de jpa ibatis o lo que sea

tener en cuenta que uso generics la k es la clase de la llave primaria y e es la clase de la entidad en concreto
Código (java) [Seleccionar]

package com.tiaveneno.ventas.compartido.servicio;

import java.util.List;
import java.util.Map;

public interface dao<K, E> {

public abstract void remove(E... entities);

public abstract void remove(final List<E> entities);

public abstract void remove(final E entity);

public abstract void persist(final E... entities);

public abstract void persist(final List<E> entities);

public abstract void persist(final E entity);

public abstract E findById(final K id);

public abstract void close();

public abstract void update(final K key, final E entity);

public abstract void update(final Map<K, E> update);

public abstract void update(final Map.Entry<K, E>... entries);
}



y una interfas comcreta para un dao cualquiera
Código (java) [Seleccionar]

package com.tiaveneno.ventas.compartido.servicio;

import com.tiaveneno.ventas.compartido.modelo.Orden;

public interface OrderDao extends dao<Integer, Orden> {
// aqui pon codigo que sea propio de las orden como busca orden con un plato x
}



sapito169

Finalmente pongo una implementación concreta de el proveedor de persistencia como en este caso jpa pero puedes poner el que mas te guste


Código (java) [Seleccionar]

package com.tiaveneno.ventas.datos.jpa;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.persistence.EntityManager;
import org.apache.commons.beanutils.BeanUtils;

import com.tiaveneno.ventas.compartido.servicio.dao;

public abstract class JpaDao<K, E> implements dao<K, E> {
private final Class<E> entityClass;

private final EntityManager entityManager;

@SuppressWarnings("unchecked")
public JpaDao(EntityManager entityManager) {
ParameterizedType genericSuperclass = (ParameterizedType) getClass()
.getGenericSuperclass();
this.entityClass = (Class<E>) genericSuperclass
.getActualTypeArguments()[1];
this.entityManager = entityManager;

}

@Override
public void persist(final E entity) {
try {
entityManager.getTransaction().begin();
entityManager.persist(entity);
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new RuntimeException(e);
}

}

@Override
public void persist(final List<E> entities) {
try {
entityManager.getTransaction().begin();
for (E e : entities) {
entityManager.persist(e);
}
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
throw new RuntimeException(e);
}

}

@Override
public void persist(E... entities) {
List<E> es = new ArrayList<E>();
for (E e : entities) {
es.add(e);
}
persist(es);
}

@Override
public void remove(final E entity) {

try {
entityManager.getTransaction().begin();
entityManager.remove(entity);
entityManager.getTransaction().commit();
} catch (Exception e) {
entityManager.getTransaction().rollback();
}

}

@Override
public void remove(final List<E> entities) {

for (E e : entities) {
remove(e);
}

}

@Override
public void remove(E... entities) {
List<E> es = new ArrayList<E>();
for (E e : entities) {
es.add(e);
}
remove(es);
}

@Override
public E findById(final K id) {

return entityManager.find(entityClass, id);

}

@Override
public void update(final K key, final E entity) {
Map<K, E> map = new HashMap<K, E>();
map.put(key, entity);
update(map);
}

@Override
public void update(final Map<K, E> update) {

Iterator<Map.Entry<K, E>> iter = update.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<K, E> entry = iter.next();
K key = entry.getKey();
E entity = entry.getValue();
@SuppressWarnings("unchecked")
E origen = (E) entityManager.find(entity.getClass(), key);
try {
BeanUtils.copyProperties(origen, entity);
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}

}

@Override
public void update(final Map.Entry<K, E>... entries) {
Map<K, E> map = new HashMap<K, E>();
for (Entry<K, E> entry : entries) {
map.put(entry.getKey(), entry.getValue());
}
update(map);
};

@Override
public void close() {
if (entityManager != null) {
if (!entityManager.isOpen()) {
try {
entityManager.close();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}

}
}

}


Un poquito de voilepart pero esta vez te vas a quedar asi  :o porque no es ni la desina parte de lo que sería con implementado con jbc

Código (java) [Seleccionar]

package com.tiaveneno.ventas.datos.jpa;

import javax.persistence.EntityManager;

import com.tiaveneno.ventas.compartido.modelo.Orden;
import com.tiaveneno.ventas.compartido.servicio.OrderDao;

public class JpaOrderDao extends JpaDao<Integer, Orden> implements OrderDao {
public JpaOrderDao(EntityManager entityManager) {
super(entityManager);
}

}


Si es solo eso no falta nada ahora puedes cerrar la boca

sapito169

#25
finalmente código que veríamos en el cliente
Código (java) [Seleccionar]

package com.tiaveneno.ventas;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

import com.tiaveneno.ventas.compartido.modelo.DetalleOrden;
import com.tiaveneno.ventas.compartido.modelo.Orden;
import com.tiaveneno.ventas.compartido.modelo.Personal;
import com.tiaveneno.ventas.compartido.modelo.Plato;
import com.tiaveneno.ventas.compartido.servicio.OrderDao;
import com.tiaveneno.ventas.datos.jpa.JpaOrderDao;

public class Main {

public static void main(String[] args) {

EntityManager entityManager = Persistence. (
"jpa").createEntityManager();

OrderDao orderDao = new JpaOrderDao(entityManager);
DetalleOrden duno = new DetalleOrden((short) 1, new Plato(
"combinado fiedeos con chanfainita", 3.5));
DetalleOrden ddos = new DetalleOrden((short) 1, new Plato(
"combinado fiedeos con chanfainita", 3.5));

Orden orden = new Orden();
orden.setCajero(new Personal("cajero"));
orden.setMozo(new Personal("mozo"));

orden.detalles(duno, ddos);

orderDao.persist(orden);



}
}



Si está bien todo se guarda automáticamente en una sola transacción en caso de error hace rollback y muestra la pila por la salida estándar donde te dice la razón del error en que línea y de que clase es el error

Y me olvidaba tienes que poner un fichero de configuración en la carpeta META-INF al (costado de las clases) con el nombre de persistence.xml y agregar una librería de jipa en mi caso puse eclipse link nota no me pregunten cómo se hace eso en netbenas por que estoy enseñando a programar en java no en netbeans asi que si quieres saberlo pon el google por que poner siguiente siguiente en un wizar no es para hombres que tengan pelo en el pecho


Código (xml) [Seleccionar]

<?xml version="1.0" encoding="UTF-8" ?>
<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_2_0.xsd"
version="2.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL">

<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<class>com.tiaveneno.ventas.compartido.modelo.Cuenta</class>
<class>com.tiaveneno.ventas.compartido.modelo.DetalleOrden</class>
<class>com.tiaveneno.ventas.compartido.modelo.Orden</class>
<class>com.tiaveneno.ventas.compartido.modelo.Personal</class>
<class>com.tiaveneno.ventas.compartido.modelo.Plato</class>

<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/test" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="root" />

<!-- EclipseLink should create the database schema automatically -->
<property name="eclipselink.ddl-generation" value="none" />
<property name="eclipselink.ddl-generation.output-mode"
value="database" />
</properties>

</persistence-unit>
</persistence>



El fichero xml es un caso particular para eclipselink la primera ves que corras el programa debes reemplazar

<property name="eclipselink.ddl-generation" value="none" />

por

<property name="eclipselink.ddl-generation" value="DROP_AND_CREATE" />

ademas en
<persistence-unit name="jpa" transaction-type="RESOURCE_LOCAL">
Tener en cuenta que lo que dice úntenme debe estar en tu código para ser creado dentro del createEntityManagerFactory

En mi caso como dice jpa yo pongo

EntityManager entityManager = Persistence. (
            "jpa").createEntityManager();

Notese que estoy creando toda la aplicación desde el main  asi que si quiero cambiar de proveedor de persistencia solo cambio en el main y no en 20 clases distintas

si no te equicas nunca mas vas a tener que corregir la vase de datos la sincronisacion te la hace jpa



Uploaded with ImageShack.us


http://www.mediafire.com/download.php?6ff5dxyb6pb2f6j

cyberserver

Hola Sapito. muy bueno tu tema, principalmente en la primera parte.
Pero tengo una duda acerca de la programacion en 3 capas.
En general poniento depormedio tu codigo.

Las Arquitectura de 3 capas se divide en :

1.- Interface, la Parte visual de nuestra aplicacion
2.- Clases de mi logica. que analizan los parametros enviador por la interface.
3.- Las Clases Ajenas a nuestra logica. (Apis, clases Generales para reutilizar,Clases ya compiladas)

Es cierto esto? o la 3 capa solo es ocupada para acceso a una BD

Y digo analizando este modelo poniendo depormedio tu codigo, por lo siguiente
Tu primera practica la estructuraste masomenos asi.

1.- Interface
2.- Tu logica que relaciona a la 1° y 3° capa
3.- Clases para la conexión de BD.

Y en la 3° capa tambien pusiste la clase "DBdaoProducto" que es donde especificas lo que se tiene que modificar en la BD.




Pero si supongamos que yo en la parte 3 coloco una Clase (Ya compilada que alguien me paso alguna ves) llamada PatronDeConeccion, que permite la conexión y modificacion de la BD por el envio de Query´s a los metodos muy generales especificados dentro.

Cuando yo quiera enviarle los parametros Query a algun metodo dentro de la Clase PatronDeConeccionesta, esta ultima clase donde iria? en el nivel de mi logica por que yo defino mis consultas o junto a la Clase PatronDeConeccion por el solo hecho de que tiene contacto directo con la BD






cyberserver

Disculpen, una pregunta mas.
La arquitectura de 3 capas solo es para aplicaciones que implementan una Base de Datos.?

Y si tengo una aplicacion, que no ocupa Bd, que hago ? se puede usar esta arquitectura o no ?






JonathanR

Hola que tal, mi nombre es Jonathan Rojas. Curso 7mo Trimestre de Ingenieria de Sistemas en la Universidad Metropolitana en caracas. En estos momentos me encuentro de vacaciones haciendo bases de datos eficientes y mi pregunta es. Conozco la idea de la programación bajo las 3 capas. Tengo ya 3 proyectos programando así más aquellos que no me piden en la Universidad y quisiera saber bajo su tutela Qué capa inicia a qué capa? porque si nos ponemos a analizar las 3 capas:

1ra Capa: Interfaz de Usuario, visible para usuario y muestra lo que queremos que el usuario vea
2da Capa: Capa Lógica, yo la llamo motor del programa, la única capa que puede acceder directamente a la capa de datos, la 1ra Capa deberá pasar por la 2da para acceder a la 3ra.
4ra Capa: Capa de datos, Base de datos, Modelación de datos, excel o lo que quieras.

Una vez que analizamos la teoria vamos a las dudas de programación que todo el mundo se hace:
Qué capa se inicializa primero? Cuál lleva el famoso main?

Bueno en mi caso he hecho los dos metodos, que la 2da capa lleve el main o la primera lo lleve. Lo lógico y teórico es que la 2da lo lleve ya que si es el motor de tu programa. Pero es un poco dificil relacionar tus ideales con la programación, hay que ser un Az para dominar esa programación.
Poniendo el main en la primera capa es sencillo, ponemos el motor como un objeto dentro de la capa y listo. Pero no se vería bonito.

Necesito opiniones..
Saludos!
Jonathan Rojas


raao10

Hola!! q tal por aca me estoy iniciando en el mundo de los sistemas en 3 capas y me ha parecido fabuloso el aporte de todos.. pero me gustaria si me pudieran ayudar en la parte en donde muestra un arreglo para visualizar una tabla de los datos... q no he podido entender... aunque hice el ejemplo no me sale.