Skip to content
July 13, 2010 / damianmigo

Patrones de diseño: "The Builder Pattern"

A veces, cuando estamos desarrollando una aplicación, inevitablemente terminamos con clases que tienen muchas propiedades o atributos, haciendo muy díficil la lectura del código al momento de invocar los contructores. Por ejemplo, supongamos que estamos desarrollando una aplicación para clasificar archivos MP3 y que vamos a usar la información contenida en la etiqueta ID3 de la canción, para ello escribimos una clase que contiene los campos de dicha etiqueta:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public ID3(String title, String artist) {
    this.title = title;
    this.artist = artist;
  }

  public ID3(String header, String title, String artist) {
    this.header = header;
    this.title = title;
    this.artist = artist;
  }

  // ... Todos los demás posibles constructores
  
  public ID3(String header, String title, String artist, String album,
      int year, String comment, boolean zeroByte, int track, int genre) {
    this.header = header;
    this.title = title;
    this.artist = artist;
    this.album = album;
    this.year = year;
    this.comment = comment;
    this.zeroByte = zeroByte;
    this.track = track;
    this.genre = genre;
  }
  
  // ... Getters
    
}

La clase anterior la podriamos instanciar de la siguiente manera:

ID3 id3 = new ID3("TAG", "Billie Jean", "Michael Jackson", "Thriller", 2009, "", false, 6, 31);

Para los que somos fanáticos de Michael Jackson facilmente podriamos saber qué el segundo parámetro corresponde al título de la canción y el tercero al nombre del artista, sin embargo para saber que significan los últimos tres valores tendríamos que consultar la documentación de la clase.

Existes dos posibles enfoques para mejorar la legibilidad del código anterior:

1.- Usar el "JavaBeans Pattern"

ó

2.- Usar el "Builder Pattern"

Si usáramos el primer enfoque la clase ID3 se vería así:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public ID3(String title, String artist) {
    this.title = title;
    this.artist = artist;
  }

  public void setHeader(String header) {
    this.header = header;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public void setArtist(String artist) {
    this.artist = artist;
  }

  public void setAlbum(String album) {
    this.album = album;
  }

  public void setYear(int year) {
    this.year = year;
  }

  public void setComment(String comment) {
    this.comment = comment;
  }

  public void setZeroByte(boolean zeroByte) {
    this.zeroByte = zeroByte;
  }

  public void setTrack(int track) {
    this.track = track;
  }

  public void setGenre(int genre) {
    this.genre = genre;
  }
  
  // ... Getters
    
}

Y el código para instanciarla sería el siguiente:

ID3 id3 = new ID3("Billie Jean", "Michael Jackson");

id3.setHeader("TAG");

id3.setAlbum("Thriller");

id3.setYear(2009);

id3.setComment("");

id3.setZeroByte(false);

id3.setTrack(6);

id3.setGenre(31);

El código queda más legible y con suerte si somos fanáticos de Michael Jackson nos evitaremos cualquier consulta a la documentación, pues sabremos que el primer parámetro del constructor es el título de la canción y el segundo el nombre del artista.

Sin embargo este enfoque tiene un pequeño problema, la clase queda mutable y puede ser que en algún momento se vuelva inconsistente. Por ejemplo, supongamos que nuestra aplicación no soporta la edición de las etiquetas ID3 y que alguien invoca el "setter" para la pista. Esta última acción dejaría a nuestro objeto en un estado inconsiste, pues el archivo seguiría teniendo el número de pista original y no el que se estableció.

El segundo enfoque para construir clases con muchos atributos se llama "Builder Pattern" y tiene las dos característacas que buscamos, legibilidad e inmutabilidad:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public static class Builder {
    
    // Required parameters
    private final String title;
    private final String artist;
    
    // Optional parameters
    private String header;
    private String album;
    private int year;
    private String comment;
    private boolean zeroByte;
    private int track;
    private int genre;
    
    public Builder(String title, String artist) {
      this.title = title;
      this.artist = artist;
    }
    
    public Builder header(String header) {
      this.header = header;
      return this;
    }
    
    public Builder album(String album) {
      this.album = album;
      return this;
    }
    
    public Builder year(int year) {
      this.year = year;
      return this;
    }
    
    public Builder comment(String comment) {
      this.comment = comment;
      return this;
    }
    
    public Builder zeroByte(boolean zeroByte) {
      this.zeroByte = zeroByte;
      return this;
    }
    
    public Builder track(int track) {
      this.track = track;
      return this;
    }
    
    public Builder genre(int genre) {
      this.genre = genre;
      return this;
    }
    
    public ID3 build() {
      return new ID3(this);
    }
    
  }
  
  public ID3(Builder builder) {
    title = builder.title;
    artist = builder.artist;
    header = builder.header;
    album = builder.album;
    year = builder.year;
    comment = builder.comment;
    zeroByte = builder.zeroByte;
    track = builder.track;
    genre = builder.genre;
  }
  
  // ... Getters
    
}

La instanciación de la clase quedaría así:

ID3 id3 = new ID3.Builder("Billie Jean", "Michael Jackson").header("TAG").

     album("Thriller").year(2009).comment("").zeroByte(false).track(6).genre(31);


Este artículo esta basado en el "Item" 2 del libro "Effective Java 2nd Edition" de Joshua Bloch.

July 7, 2010 / damianmigo

Tutorial: JPA + Hibernate + Spring ORM (usando Eclipse)

Requisitos:

7/13/2010: Anexo el código fuente para descargar

Pasos:

1.- Crear el proyecto

  • Proyect Explorer (clic derecho) -> New -> Project… -> Web -> Dynamic Web Project -> Next
  • Project name: com.example
  • Target runtime: Apache Tomcat v6.0
  • Finish

Pantalla1 

  • Project Explorer -> com.example (clic derecho) -> Properties -> Project Facets -> Java persistence (checar) -> Futher configuration available…

Pantalla2

  • Plafform: Generic
  • JPA implementation, Type: Disable Library Configuration
  • Connection: <None>
  • Persistent class management, Annotated classes must be listed in persistence.xml (checar)
  • Create orm.xml (checar)
  • OK
  • OK

Pantalla3

  • Arrastrar las siguientes líbrerías al directorio com.example\Java Resources: src\WebContent\WEB-INF\lib:

hibernate-distribution-3.5.1-Final\lib\required\

antlr-2.7.6.jar
commons-collections-3.1.jar
dom4j-1.6.1.jar
javassist-3.9.0.GA.jar
jta-1.1.jar
slf4j-api-1.5.8.jar

hibernate-distribution-3.5.1-Final\lib\jpa\

hibernate-jpa-2.0-api-1.0.0.Final.jar

hibernate-distribution-3.5.1-Final\

hibernate3.jar

hibernate-distribution-3.5.1-Final\lib\bytecode\cglib\

cglib-2.2.jar

slf4j-1.5.11\

slf4j-simple-1.5.11.jar

spring-framework-2.5\dist\

spring.jar

mysql-connector-java-5.1.12\

mysql-connector-java-5.1.12-bin.jar

commons-logging-1.1.1\

commons-logging-1.1.1.jar

asm-3.2\lib\

asm-3.2.jar

2.- Crear los POJOs

  • Project Explorer -> com.example -> Java Resources: src (clic derecho) -> New -> Package
  • Name: com.example.domain
  • Finish
  • Project Explorer -> com.example ->Java Resources: src (clic derecho) -> New -> Class
  • Package: com.example.domain
  • Name: User
  • Finish
  • Código fuente de la clase User:
package com.example.domain;  
  
import static javax.persistence.CascadeType.ALL;  
import static javax.persistence.GenerationType.AUTO;  
  
import java.util.Collection;  
  
import javax.persistence.Column;  
import javax.persistence.Entity;  
import javax.persistence.GeneratedValue;  
import javax.persistence.Id;  
import javax.persistence.OneToMany;  
import javax.persistence.Table;  
  
@Entity  
@Table(name = "USERS")  
public class User {  
  
  @Id  
  @GeneratedValue(strategy = AUTO)  
  @Column(name = "ID_USER")  
  private Integer id;  
    
  @Column(name = "USERNAME", nullable = false)  
  private String username;  
    
  @Column(name = "PASSWORD", nullable = false)  
  private String password;  
    
  @Column(name = "EMAIL", nullable = true)  
  private String email;  
  
  @OneToMany(cascade = ALL, mappedBy = "user")  
  private Collection<Photo> photos;  
    
  { ... Getters y Setters ... }  
    
}
  • Project Explorer -> com.example -> Java Resources: src (clic derecho) -> New -> Class
  • Package: com.example.domain
  • Name: Photo
  • Finish
  • Código fuente de la clase Photo:
package com.example.domain;  
   
import static javax.persistence.GenerationType.AUTO;  
   
import javax.persistence.Basic;  
import javax.persistence.Column;  
import javax.persistence.Entity;  
import javax.persistence.FetchType;  
import javax.persistence.GeneratedValue;  
import javax.persistence.Id;  
import javax.persistence.JoinColumn;  
import javax.persistence.Lob;  
import javax.persistence.ManyToOne;  
import javax.persistence.Table;  
   
@Entity  
@Table(name = "PHOTOS")  
public class Photo {  
     
  @Id  
  @GeneratedValue(strategy = AUTO)  
  @Column(name = "ID_PHOTO")  
  private Integer id;  
     
  @ManyToOne  
  @JoinColumn(name = "ID_USER", nullable = false)  
  private User user;  
     
  @Column(name = "CONTENT_TYPE", nullable = false)  
  private String contentType;  
    
  @Basic(fetch = FetchType.LAZY)  
  @Lob  
  @Column(name = "FILE", nullable = false)  
  private byte[] file;  
  
  { ... Getters y Setters ... }
  
}  

3.- Modificar los archivos de configuración de JPA

  • Modificar el contenido del archivo orm.xml localizado en com.example/Java Resources: src/META-INF:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">
  <entity class="com.example.domain.User"></entity>
  <entity class="com.example.domain.Photo"></entity>
</entity-mappings>
  • Modificar el contenido del archivo persistence.xml localizado en com.example/Java Resources: src/META-INF:
<?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="example">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.hbm2ddl.auto" value="update" />
      <property name="hibernate.show_sql" value="true" />
      <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
      <property name="hibernate.connection.username" value="root" />
      <property name="hibernate.connection.password" value="password" />
      <property name="hibernate.connection.url" value="jdbc:mysql://localhost/lunar?autoReconnect=true" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
      <property name="hibernate.c3p0.min_size" value="5" />
      <property name="hibernate.c3p0.max_size" value="20" />
      <property name="hibernate.c3p0.timeout" value="300" />
      <property name="hibernate.c3p0.max_statements" value="50" />
      <property name="hibernate.c3p0.idle_test_period" value="3000" />
    </properties>
  </persistence-unit>
</persistence> 

4.- Configurar Spring

  • Project Explorer –> Java Resources: src (click derecho) –> New –> XML
  • Selecciona la carpeta com.example/src
  • Filename: beans-jpa.xml
  • Finish
  • Contenido del archivo beans-jpa.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:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<tx:annotation-driven />
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="example" />
	</bean>
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
	<bean name="photoDao" class="com.example.domain.dao.PhotoDao">
		<property name="jpaTemplate" ref="jpaTemplate" />
	</bean>
	<bean name="userDao" class="com.example.domain.dao.UserDao">
		<property name="jpaTemplate" ref="jpaTemplate" />
	</bean>
</beans>

5.- Crear los DAOs (Data Access Objects)

  • Project Explorer –> Java Resources: src (clic derecho) –> New –> Class
  • Package: com.example.domain.dao
  • Class: GenericDao
  • Finish
  • Contenido de la clase GenericDao:
package com.example.domain.dao;

import java.io.Serializable;
import java.util.List;

import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.transaction.annotation.Transactional;

public abstract class GenericDao<T, ID extends Serializable>  {

  private JpaTemplate jpaTemplate;
  
  private Class<T> persistentClass;
  
  public GenericDao(Class<T> persistentClass) {
    this.persistentClass = persistentClass;
  }
  
  public void setJpaTemplate(JpaTemplate jpaTemplate) {
    this.jpaTemplate = jpaTemplate;
  }
  
  protected JpaTemplate getJpaTemplate() {
    return jpaTemplate;
  }
  
  @Transactional
  public void merge(T entity) {
    jpaTemplate.merge(entity);
  }
  
  @Transactional
  public void persist(T entity) {
    jpaTemplate.persist(entity);
  }
  
  @Transactional
  public void remove(T entity) {
    jpaTemplate.remove(entity);
  }
  
  @Transactional(readOnly = true)
  public T findById(ID id) {
    return jpaTemplate.find(getPersistentClass(), id);
  }
  
  @SuppressWarnings("unchecked")
  @Transactional(readOnly = true)
  public List<T> findAll() {
    return jpaTemplate.find("from " + getPersistentClass().getSimpleName());
  }
  
  public Class<T> getPersistentClass() {
        return persistentClass;
    }
  
}
  • Project Explorer –> Java Resources: src (clic derecho) –> New –> Class
  • Package: com.example.domain.dao
  • Class: UserDao
  • Finish
  • Contenido de la clase UserDao:
package com.example.domain.dao;

import com.example.domain.User;

public class UserDao extends GenericDao<User, Integer> {
  
  public UserDao() {
    super(User.class);
  }

}
  • Project Explorer –> Java Resources: src (clic derecho) –> New –> Class
  • Package: com.example.domain.dao
  • Class: PhotoDao
  • Finish
  • Contenido de la clase PhotoDao:
package com.example.domain.dao;

import com.example.domain.Photo;

public class PhotoDao extends GenericDao<Photo, Integer> {
  
  public PhotoDao() {
    super(Photo.class);
  }

}
  • Project Explorer –> Java Resources: src (clic derecho) –> New –> Class
  • Package: com.example.domain.dao
  • Class: DaoFactory
  • Finish
  • Contenido de la clase DaoFactory:
package com.example.domain.dao;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DaoFactory {

  private static ApplicationContext applicationContext;

  private static PhotoDao photoDao;
  
  private static UserDao userDao;
  
  static {
    applicationContext = new ClassPathXmlApplicationContext("beans-jpa.xml");
    photoDao = (PhotoDao)applicationContext.getBean("photoDao");
    userDao = (UserDao)applicationContext.getBean("userDao");
  }

  private DaoFactory() {

  }

  public static PhotoDao getPhotoDao() {
    return photoDao;
  }

  public static UserDao getUserDao() {
    return userDao;
  }

}

6.- Crear la base de datos

mysql -h localhost -u root -ppassword
mysql> CREATE DATABASE `example` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

7.- Servlet de prueba

  • Project Explorer –> Java Resources: src –> New –> Servlet
  • Java package: com.example.web
  • Class name: ExampleServlet
  • Finish
  • Código fuente de la clase ExampleServlet:
package com.example.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.example.domain.User;
import com.example.domain.dao.DaoFactory;
import com.example.domain.dao.UserDao;

public class ExampleServlet extends HttpServlet {
  
  private static final long serialVersionUID = 1L;

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    UserDao userDao = DaoFactory.getUserDao();
    
    User user1 = new User();
    user1.setEmail("john.doe@yahoo.com");
    user1.setPassword("d41d8cd98f00b204e9800998ecf8427e");
    user1.setUsername("john");    
    userDao.persist(user1);
    
    User user2 = new User();
    user2.setEmail("juan.perez@hotmail.com");
    user2.setPassword("d41d8cd98f00b204e9800998ecf8427e");
    user2.setUsername("jperez");    
    userDao.persist(user2);
    
    User user3 = new User();
    user3.setEmail("arnulfo@gmail.com");
    user3.setPassword("d41d8cd98f00b204e9800998ecf8427e");
    user3.setUsername("agonzalez");   
    userDao.persist(user3);
    
    PrintWriter printWriter = response.getWriter();
    
    List<User> users = userDao.findAll();
    for (User user : users) {
      printWriter.println(user.getEmail());
    }
  }

}
April 16, 2010 / damianmigo

Sun Certified Programmer for Java 6 Study Guide

Ajunto el libro de estudio para la certificación SCJP:

SCJP Sun Certified Programmer for Java 6 Exam 310-065

August 1, 2007 / damianmigo

Welcome !

It’s hardware that makes a machine fast. It’s software that makes a fast machine slow.

Craig Bruce