Thursday, 14 May 2009

Persistencia en Java con Hibernate (Parte I)

Hibernate es un framework ORM (Object Relational Mapping) que nos permite convertir datos en el sistema de tipos utilizado en la POO y el utilizado en las BD relacionales. De esta manera conseguimos utilizar las propiedades de la POO sobre la BD relacional. Por lo tanto conseguiremos tener persistencia sobre nuestros objetos utilizando una BD mediante ficheros XML. Éste genera las sentencias SQL necesarias para la gestión de la conversación entre los 2 tipos, y además incrementa ligeramente el tiempo de ejecución.

Un ejemplo muy sencillo, es el de crear una clase persona con 4 atributos (dni, name, surname y phone number), y conectaremos con una BD MySQL. Por lo tanto generaremos objetos de tipo "User", y se irán almacenando en la BD (persisten en la BD). Cada vez que modifiquemos una propiedad del objeto esta se verá reflejada en la BD.

  • Que necesito para empezar?
Tener instalado el MySQL (hay varios posts de como hacerlo), luego descargar el framework del Hibernate, concretamente el Hibernate Core (la última versión). En mi caso utilizaré la versión Hibernate core 3.3.1 GA.
Una vez tenemos descargado el hibernate, lo dejamos en una ruta conocida. En mi caso y siguiendo los últimos ejemplos que he posteado, lo voy a dejar en la ruta: C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA, de esta distribución utilizaré las siguientes librerías para mi proyecto:

C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\hibernate3.jar
C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\lib\required\antlr-2.7.6.jar
C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\lib\required\commons-collections-3.1.jar
C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\lib\required\dom4j-1.6.1.jar
C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\lib\required\javassist-3.4.GA.jar
C:\Archivos de programa\dev\hibernate-distribution-3.3.1.GA\lib\required\jta-1.1.jar

Además necesitaremos el SQL Connector para MySQL, que ya lo tengo descargado y posteé el cómo hacerlo en el post: trabajando con jdbc.
En mi caso, lo tengo en :

C:\Archivos de programa\MySQL\mysql-connector-java-5.0.8\mysql-connector-java-5.0.8-bin.jar

Además, necesito descargar el SLF4J (Simple loggin facade for java), que lo encontraremos en la web www.slf4j.org, podéis descargar la última versión directamente desde aquí: slf4j-1.5.6.zip. Y como he comentado anteriormente descomprimiremos el paquete en algún lugar conocido de nuestro sistema, en mi caso lo he dejado en la ruta: C:\Archivos de programa\dev\slf4j-1.5.6, y los ficheros necesarios para nuestro proyecto son:

C:\Archivos de programa\dev\slf4j-1.5.6\slf4j-simple-1.5.6.jar
C:\Archivos de programa\dev\slf4j-1.5.6\slf4j-api-1.5.6.jar


  • Creando un proyecto simple
Ahora crearemos un pequeño proyecto muy simple, y lo vamos a hacer todo de una forma bastante manual, para ver de donde salen todos los ficheros que vamos a generar, y luego ya veremos como lo hacemos con las Hibernate tools para Eclipse.

Primero desde el Eclipse (3.4.1 Ganymede), creo un nuevo proyecto el cual voy a llamar HibernateUserMySQLTest, que contendrá una clase "User" que tendrá las propiedades de los usuarios y una clase "UserManager", que es la que se encargará de gestionar los usuarios. Luego crearé una carpeta lib, que será donde copiemos todas las librerías necesarias para nuestro proyecto y los packages mySQL.user y mySQL.data, y luego ya iremos poniendo los ficheros que toquen dentro de cada package.

En este caso la clase básica es la siguiente:

Tendremos un campo ID de tipo Long (Evitar siempre los tipos simples), y este será autoincremental y lo gestionará el mismo hibernate. Además el método setID tiene que ser privado ya que se lo gestiona él mismo.

Aquí os dejo el código fuente de las 2 clases que gestionaran el proyecto:

User.java



package mySQL;

public class User {
private Long id;
private String DNI;
private String name;
private String surname;
private String phoneNumber;

public void setSurname(String surname) {
this.surname = surname;
}

public String getSurname() {
return surname;
}

@SuppressWarnings("unused")
private void setId(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public void setDNI(String dNI) {
DNI = dNI;
}

public String getDNI() {
return DNI;
}

public void setName(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

public String getPhoneNumber() {
return phoneNumber;
}
}




UserManager.java



package mySQL;

import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;

public class UserManager {

private SessionFactory sf;

public UserManager() {
try {
System.out.println("Initiating Hibernate");
sf = new Configuration().configure().buildSessionFactory();
System.out.println("Hibernate alive!");
} catch (HibernateException e) {
e.printStackTrace();
}
}

public void addNewUser(String DNI, String name, String surname,
String phoneNumber) {
try {
System.out.println("Insert data");

Session session = sf.openSession();
Transaction tx = session.beginTransaction();
User user = new User();
user.setDNI(DNI);
user.setName(name);
user.setSurname(surname);
user.setPhoneNumber(phoneNumber);
session.save(user);
tx.commit();
System.out.println("Data inserted");
} catch (HibernateException e) {
e.printStackTrace();
}
}

public void deleteUser(String DNI) {
try {
System.out.println("Delete data");

Session session = sf.openSession();
Transaction tx = session.beginTransaction();

List<User> ld = listUsers();
User d;

for (int i = 0; i < (ld.size() - 1); i++) {
d = ld.get(i);

if (d.getDNI().compareTo(DNI) == 0) {
session.delete(d);
}
}

tx.commit();
System.out.println("Data Delete");
} catch (HibernateException e) {
e.printStackTrace();
}
}

@SuppressWarnings("unchecked")
public List<User> listUsers() {
List<User> datos = null;

try {
System.out.println("Listing data");

Session session = sf.openSession();
Transaction tx = session.beginTransaction();
datos = session.createQuery("from User").list();
tx.commit();
session.close();
System.out.println("data listed");
} catch (HibernateException e) {
e.printStackTrace();
}

return datos;
}

@SuppressWarnings("unchecked")
public List<User> listUsers(String dni) {
List<User> datos = null;

try {
System.out.println("Listing data");

Session session = sf.openSession();
Transaction tx = session.beginTransaction();
datos = session.createQuery("from User Where dni=" + dni).list();
tx.commit();
session.close();
System.out.println("data listed");
} catch (HibernateException e) {
e.printStackTrace();
}

return datos;
}


public static void main(String[] args) {
UserManager um = new UserManager();
um.addNewUser("40345123R", "John", "Smith", "+0034876459823");
um.addNewUser("40873423D", "Ramon", "Smith", "+00348765459823");
um.addNewUser("40323453W", "Pepe", "Smith", "+00348567459823");
um.addNewUser("42345153Z", "Matias", "Smith", "+0034876478823");

List<User> ld = um.listUsers();
User d;
for (int i = 0; i < (ld.size()); i++) {
d = ld.get(i);
System.out.println("User: DNI=" + d.getDNI() + " Name=" +
d.getName() + " Surname=" + d.getSurname() +
" Phonenumber=" + d.getPhoneNumber());
}
}

}





A partir de aquí, solo falta configurar todo el entorno para que hibernate trabaje. Ahora tenemos que configurar el proyecto de la siguiente manera, como muestra la imagen a continuación:

Tenemos que crear los siguientes ficheros: user.hbm.xml que es el fichero del mapping de la clase user hacia la BD y configura los diferentes campos que tenemos. hibernate.cfg.xml, fichero que establece la configuración del hibernate, así como la conexión con la BD. log2j.properties, establece las propiedades del log4j, y build.xml és el fichero ant que establece como se generará nuestro proyecto y es el fichero que iniciaremos desde la vista ant en el Eclipse.

Los ficheros son los siguientes:

user.hbm.xml:



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//hibernate/hibernate Mapping DTD//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="mySQL.User" table="Users">
<id name="id" column="id" type="long">
<generator class="increment"/>
</id>
<property name="DNI" type="string"/>
<property name="name" type="string"/>
<property name="surname" type="string"/>
<property name="phoneNumber" type="string"/>
</class>
</hibernate-mapping>




hibernate.cfg.xml:



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
org.gjt.mm.mysql.Driver
</property>
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/Webshop</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">1234</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="show_sql">true</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="mySQL/data/User.hbm.xml"/>
</session-factory>
</hibernate-configuration>




log2j.properties:
log4j.rootCategory=INFO, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-5p - %m%n

build.xml:



<project name="Test Users MySQL" default="run">
<target name="init">
<property name="src.dir" value="src"/>
<property name="classes.dir" value="bin"/>
<property name="lib.dir" value="lib"/>
<property name="mainclass" value="mySQL.UserManager"/>
<path id="classpath">
<pathelement location="${classes.dir}"/>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
</target>
<target name="prepare" depends="init">
<mkdir dir="${classes.dir}"/>
<mkdir dir="${lib.dir}"/>
</target>
<target name="compile" depends="copy-resources">
<javac srcdir="${src.dir}" destdir="${classes.dir}">
<classpath refid="classpath"/>
</javac>
</target>
<target name="copy-resources" depends="prepare">
<copy todir="${classes.dir}">
<fileset dir="${src.dir}">
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="run" depends="compile">
<java classname="${mainclass}" fork="true">
<classpath refid="classpath"/>
</java>
</target>
</project>




una vez tenemos generado nuestro proyecto sin ningún error, lo ejecutamos de la siguiente manera:

Nos dirigimos a la vista Ant en nuestro IDE Eclipse (Window -> Show View -> Ant):



Agregamos el build file de nuestro proyecto como aparece en la imagen, y luego para iniciar el proyecto solo tenemos que hacer doble click sobre el "run". Luego si miramos la consola nos aparecerá toda la información del hibernate, y mostrará todas las acciones que estamos haciendo sobre nuestroso objetos:

Buildfile: D:\workspace\HibernateUserMySQLTest\build.xml
init:
prepare:
copy-resources:
compile:
run:
[java] Initiating Hibernate
[java] 46 [main] INFO org.hibernate.cfg.Environment - Hibernate 3.3.1.GA
[java] 62 [main] INFO org.hibernate.cfg.Environment - hibernate.properties not found
[java] 62 [main] INFO org.hibernate.cfg.Environment - Bytecode provider name : javassist
[java] 62 [main] INFO org.hibernate.cfg.Environment - using JDK 1.4 java.sql.Timestamp handling
[java] 125 [main] INFO org.hibernate.cfg.Configuration - configuring from resource: /hibernate.cfg.xml
[java] 125 [main] INFO org.hibernate.cfg.Configuration - Configuration resource: /hibernate.cfg.xml
[java] 250 [main] INFO org.hibernate.cfg.Configuration - Reading mappings from resource : mySQL/data/User.hbm.xml
[java] 312 [main] INFO org.hibernate.cfg.HbmBinder - Mapping class: mySQL.User -> Users
[java] 328 [main] INFO org.hibernate.cfg.Configuration - Configured SessionFactory: null
[java] 390 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Using Hibernate built-in connection pool (not for production use!)
[java] 390 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - Hibernate connection pool size: 20
[java] 390 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - autocommit mode: false
[java] 421 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - using driver: org.gjt.mm.mysql.Driver at URL: jdbc:mysql://127.0.0.1:3306/Webshop
[java] 421 [main] INFO org.hibernate.connection.DriverManagerConnectionProvider - connection properties: {user=root, password=****}
[java] 765 [main] INFO org.hibernate.cfg.SettingsFactory - RDBMS: MySQL, version: 5.0.81-community-nt
[java] 765 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-5.0.8 ( Revision: ${svn.Revision} )
[java] 812 [main] INFO org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.MySQLDialect
[java] 812 [main] INFO org.hibernate.transaction.TransactionFactoryFactory - Transaction strategy: org.hibernate.transaction.JDBCTransactionFactory
[java] 812 [main] INFO org.hibernate.transaction.TransactionManagerLookupFactory - No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic flush during beforeCompletion(): disabled
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic session close at end of transaction: disabled
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC batch size: 15
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC batch updates for versioned data: disabled
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - Scrollable result sets: enabled
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC3 getGeneratedKeys(): enabled
[java] 812 [main] INFO org.hibernate.cfg.SettingsFactory - Connection release mode: auto
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Maximum outer join fetch depth: 2
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Default batch fetch size: 1
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Generate SQL with comments: disabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL updates by primary key: disabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL inserts for batching: disabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
[java] 828 [main] INFO org.hibernate.hql.ast.ASTQueryTranslatorFactory - Using ASTQueryTranslatorFactory
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Query language substitutions: {}
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - JPA-QL strict compliance: disabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Second-level cache: enabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Query cache: disabled
[java] 828 [main] INFO org.hibernate.cfg.SettingsFactory - Cache region factory : org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge
[java] 828 [main] INFO org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge - Cache provider: org.hibernate.cache.HashtableCacheProvider
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Optimize cache for minimal puts: disabled
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Structured second-level cache entries: disabled
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Echoing all SQL to stdout
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Statistics: disabled
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Deleted entity synthetic identifier rollback: disabled
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Default entity-mode: pojo
[java] 843 [main] INFO org.hibernate.cfg.SettingsFactory - Named query checking : enabled
[java] 953 [main] INFO org.hibernate.impl.SessionFactoryImpl - building session factory
[java] 1218 [main] INFO org.hibernate.impl.SessionFactoryObjectFactory - Not binding factory to JNDI, no JNDI name configured
[java] 1234 [main] INFO org.hibernate.tool.hbm2ddl.SchemaUpdate - Running hbm2ddl schema update
[java] 1234 [main] INFO org.hibernate.tool.hbm2ddl.SchemaUpdate - fetching database metadata
[java] 1234 [main] INFO org.hibernate.tool.hbm2ddl.SchemaUpdate - updating schema
[java] 1359 [main] INFO org.hibernate.tool.hbm2ddl.TableMetadata - table found: Webshop.users
[java] 1359 [main] INFO org.hibernate.tool.hbm2ddl.TableMetadata - columns: [phone, name, surname, dni]
[java] 1359 [main] INFO org.hibernate.tool.hbm2ddl.TableMetadata - foreign keys: []
[java] 1359 [main] INFO org.hibernate.tool.hbm2ddl.TableMetadata - indexes: []
[java] 1656 [main] INFO org.hibernate.tool.hbm2ddl.SchemaUpdate - schema update complete
[java] Hibernate alive!
[java] Insert data
[java] Hibernate: select max(id) from Users
[java] Hibernate: insert into Users (DNI, name, surname, phoneNumber, id) values (?, ?, ?, ?, ?)
[java] Data inserted
[java] Insert data
[java] Hibernate: insert into Users (DNI, name, surname, phoneNumber, id) values (?, ?, ?, ?, ?)
[java] Data inserted
[java] Insert data
[java] Hibernate: insert into Users (DNI, name, surname, phoneNumber, id) values (?, ?, ?, ?, ?)
[java] Data inserted
[java] Insert data
[java] Hibernate: insert into Users (DNI, name, surname, phoneNumber, id) values (?, ?, ?, ?, ?)
[java] Data inserted
[java] Listing data
[java] Hibernate: select user0_.id as id0_, user0_.DNI as DNI0_, user0_.name as name0_, user0_.surname as surname0_, user0_.phoneNumber as phoneNum5_0_ from Users user0_
[java] data listed
[java] User: DNI=40345123R Name=John Surname=Smith Phonenumber=+0034876459823
[java] User: DNI=40873423D Name=Ramon Surname=Smith Phonenumber=+00348765459823
[java] User: DNI=40323453W Name=Pepe Surname=Smith Phonenumber=+00348567459823
[java] User: DNI=42345153Z Name=Matias Surname=Smith Phonenumber=+0034876478823
BUILD SUCCESSFUL
Total time: 2 seconds

Podemos ver como añade los usuarios que he indicado en el código java y luego los listo. En el siguiente post, crearemos los ficheros de configuración con la herramienta del eclipse, de esta a manera veremos la diferéncia y la reducción de tiempo configurando los parámetros a mano.



0 comments:

Post a Comment