Sunday, 31 May 2009

Diseño orientado a interfaces (parte I)

Las interfaces surgen como una evolución de la Programación Orientada a Objetos con la necesidad de agrupar y reutilizar las distintas funcionalidades de un objeto en una interfaz más manejable. El diseño Orientado a interfaces pretende que el desarrollo del software genere implementaciones de programas bien estructurados. Mediante las interfaces, podremos crear mejores diseños sin basarnos tanto en la POO. Podemos encontrar una buena explicación sobre este tipo de programación en el libro Interface-OrientedDesign , donde Ken pugh nos hace una muy buena argumentación del porqué debemos utilizar mas las iterfaces para mejorar nuestro diseño y evitar los anti-patrones de diseño sobre OO como por ejemplo utilizar demasiado la herencia, fuerte acoplamiento, etc. La lista de los anti-patrones la podéis encontrar en la wikipedia. Una de las cosas que más me ha gustado sobre el libro, es el capítulo sobre los contratos de las interfaces y sobre las 3 leyes que deben cumplir las interfaces. Estas 3 leyes se basan en las 3 leyes de la robótica por Isaac Asimov (si recordáis la película Yo, Robot, allí se mencionaban). Estas 3 leyes son las siguientes:

  1. Un robot no puede hacer daño a un ser humano o, por su inacción, permitir que un ser humano sufra daño.
  2. Un robot debe obedecer las órdenes dadas por los seres humanos, excepto si estas órdenes entrasen en conflicto con la Primera Ley.
  3. Un robot debe proteger su propia existencia en la medida en que esta protección no entre en conflicto con la Primera o la Segunda Ley.
Bien, como se comentan en estas 3 leyes, algo parecido le ocurre a las interfaces, donde el autor comenta:
1. La implementación de una interfaz debe hacer lo que sus métodos dicen que hacen.
Esta ley indica que los nombres de los métodos deben corresponder a la acción que hacen, y que cada método debe devolver un valor que indique si la operación ha tenido éxito o si al contrario hay error.
2. Una interfaz no debe interferir otros módulos de un programa o con otros programas.
En este caso, se expone que una interfaz no debe utilizar recursos de otros módulos, es decir si uno de los propósitos de la interfaz es leer un dato de un fichero, no necesita una conexión a una base de datos.
3. Si una implementación no es capaz de realizar su responsabilidad, debe notificar a quien lo llamó.
La implementación debe reportar siempre el problema que encuentra, si no es capaz de realizar la acción, debe informar a su creador o invocador e informarle del problema mediante un código de error o un estado.

La verdad es que este tipo de diseño, abre un nuevo camino a explorar y sobretodo permite comprobar si el diseño de nuestras aplicaciones es correcto o no desde el punto del aprovechamiento de las propiedades de la OO, sabiendo aplicar correctamente la herencia, el polimorfismo, etc.

En un segundo post, pondré varios ejemplos utilizando las interfaces con los diferentes patrones de diseño que se utilizan.

Friday, 29 May 2009

Refactoring

Hace tiempo que me preguntan que es todo este tema del Refactoring y aquí hago una pequeña explicación de lo que es. También os recomiendo el libro "Refactoring -> Improving the Design of Existing code" que lo explica muy bien. Está escrito por Martin Fowler, y ya tengo varios de sus libros en mis estanterías, así que os recomiendo su lectura, al igual que el que comenté en su día "Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series)". El tema del Refactoring, es una técnica que se utiliza en la ingenieria del software para reestructurar el código fuente, alterando su estructura interna, sin cambiar su comportamiento externo. Por lo tanto hacer que el código sea más fácil de entender y extender. Evitamos soluciones Quick & Dirty i mejoramos el código sin cambiar la funcionalidad. Para aplicar Refactoring, no consiste ir formateando el código para que quede más bonito, consiste en analizar muy bien el código e ir encontrando patrones dentro de lo que escribimos, es decir encontrar lo que llamamos "Bad Smells in code".

Los ejemplos que se me ocurren ahora són por ejemplo el Shotgun Surgery y la envidia de capacidades. El shotgun Surgery por ejemplo consiste en que si tenemos que modificar algo, tenemos que hacer lo mismo en N clases, y por lo tanto puede ser que algún día se nos olvide hacerlo, con el consiguiente fallo. Y la envidia de capacidades consiste en que si tenemos una clase que accede mucho a los métodos de otra clase, a lo mejor es que tiene mucha envidia, y realmente esos métodos tendrían que ir en la primera clase.

En los enlaces de interés encontrareis buenos resúmenes sobre los "Code smell", pero simplemente consiste en tenerlos muy claros, y saber identificarlos. Los más comunes por ejemplo Data Clumps, Switch, Clase perezosa y Comentarios.

Data Clumps: Consiste en que si siempre pasamos los mismos 4 argumentos a nuestra función, a lo mejor es que hay una relación más estrecha y necesitamos generar un tipo de datos.

Switch: La POO es una ataque al "case". La Programación estructurada no soporta el polimorfismo, y se pueden reemplazar por tipos con métodos virtuales. El uso de Switch es un sintoma de que no estoy aprovechando el polimorfismo.

Clase perezosa: Es una clase que no hace nada, que no tiene comportamiento. A lo mejor sus métodos están en otras clases y a lo mejor las otras clases están padeciendo envidia de capacidades????. Esto es un síntoma de que algo no va bien.

Comentarios: Poner el comentario que es un candidato a una función, crea la función!. A lo mejor crear comentarios puede ser que nuestro código no sea tan bueno. Hay que comentar el propósito de las cosas, no comentar lo que hacer el código porque eso ya se puede leer en él. Hay que evitar hacer cosas redundantes y sobretodo el DRY.

Algunas categorías de refactorings:

Simplificar llamadas a métodos, de esta manera conseguimos que se pueda leer mejor el código. Análisis intuitivo y que cada objeto tenga su rol y seguir el diálogo entre objetos. Crear métodos deprecated, y sobretodo tener en cuenta que el Refactoring no es cambiar el código. Como podéis ver hay toda una filosofía detrás de la refactorización de código, y podréis encontrar muchos ejemplos ilustrados en el libro de Martin fowler.

  • Enlaces de interés:
http://sourcemaking.com/refactoring
http://es.debugmodeon.com/articulo/categorizacion-de-code-smells

Wednesday, 27 May 2009

Canvas para dibujar graficos en Delphi

Después de varios meses vuelvo a abrir el post de jugando con Tcanvas, donde mostraba como configurar y programar el Tcanvas para crear las aplicaciones gráficas que queramos. Pues bien, he encontrado una aplicación "Simple Graph Demo", desarrollada por Kambiz R. Khojasteh, que permite hacer la parte que yo queria sobre la manipulación de objetos a través del canvas. Podemos encontrar todo el proyecto en SourceForge ya que es Open Source. Una vez descargada la última versión estable (v1.542) -> simplegraph.zip, solo tenemos que descomprimirla y crear un package donde importaremos el código fuente SimpleGraph.pas. Debemos hacer algo parecido a lo que hay en la siguiente imagen:

Creamos un package llamado graph.bpl y luego importamos la fuente SimpleGraph.pas, una vez hecho, hacemos un build y un install para instalar el componente en nuestra paleta. Una vez acabado, abrimos el proyecto demo, lo compilamos y lo iniciamos, y podremos jugar con el componente, utilizando diversos objetos, conectándolos, asignándoles propiedades, aplicando zoom, etc. Osea que hay para ir practicando y mejorar la aplicación.


Herramientas para mostrar el password oculto en una textbox

Muchas veces nos encontramos con campos de texto con los típicos asteriscos y no hay manera de acordarse del password que pusimos en su día. Pues bien, hay muchas herramientas on-line que permiten revelar el password, y que nos pueden salvar en caso de algún apuro. Recordad que el robo de passwords es un crimen, y este post solo pretende dar información sobre varias herramientas que podemos utilizar en el caso de que hayamos rellanado un campo en alguna aplicación o web y que no nos acordemos de éste.

  • Asterisk Key
Esta aplicación, para mi una de las mejores (que yo conozca), permite obtener los passwords de aquellas textbox que queramos. La aplicación una vez arrancada escanea el formulario en busca de textbox con los típicos "*******" y te entrega el contenido del password. Asterisk Key es de libre descarga, y podemos encontrar el programa en descarga directa desde el siguiente enlace: Ariskkey.exe.
Aquí os dejo un ejemplo de su utilización. He creado una pequeña aplicación con un TextBox y un password, y luego podemos ver como Asterisk Key te devuelve el texto interno de la TextBox:


  • Show PW
Esta aplicación no tan potente como la anterior pero también nos puede servir en determinados momentos y además es muy portable ya que solo necesitamos su ejecutable. Este funciona de otra manera, simplemente tenemos que pasar el puntero del mouse por la celda con los asteriscos y automáticamente si la aplicación puede (debido a la seguridad de la aplicación) te devolverá el texto debajo de los asteriscos. Desde la web de ShowPassword, podemos decargarnos la aplicación showpw.exe. En la web también encontraremos otra aplicación Protected Storage Viewer, es muy interesante, pero es de pago.


  • Password Reveal Pro
Es utilidad es muy parecido a ShowPassword pero con el inconveniente que es de pago. Puedes descargar una versión Trial, que sirve de igual manera para nuestro cometido. Nos aparece un candado en la parte derecha de la aplicación. Lo seleccionamos y lo arrastramos hasta la textbox, luego nos aparecerá el texto en la aplicación. En la web de Password Reveal, podemos descargar la aplicación. Aquí os dejo un enlace directo para la descarga: psrvls10.zip



Monday, 25 May 2009

QR Code

QR Code (Quick Response) son un nuevo tipo de código de barras que puede contener mucha más información que un simple código de barras, incluyendo caracteres alfanuméricos e incluso otros alfabetos (japonés, cirílico, etc). Estos códigos son facilmente legibles mediante el teléfono móvil, se descarga un software especial de escaneo, y mediante la cámara de fotos del móvil, se marca la imagen, y luego podemos leer el contenido. La imagen que podéis ver en esta página y en la web es un código QR y hay en el la dirección de mi web (ThundaxSoftware). En el siguiente vídeo, podéis ver un anuncio de japón mostrando el funcionamiento del QR Code. Ahora se está explotando mucho, sobretodo en temas comerciales (vallas publicitarias), información de productos, comida, etc. Mediante este código por ejemplo podemos la lista entera de la composición de un producto con sus características. La verdad es que da mucho juego, y como que es un código de formato libre, podemos implementar los programas para que lean este tipo de código.

Podemos descargar la aplicación para nuestro móvil, en el siguiente enlace:
Nos tenemos que conectar al enlace desde nuestro teléfono, y luego descargar la aplicación para nuestro modelo en concreto.

Podemos encontrar generadores QR Online, donde podemos escribir lo que queramos automáticamente nos genera el código QR, aquí os dejo un par de enlaces:
Podemos encontrar toda la información referente a la codificación en la página oficial de qrcode y nos podemos descargar el proyecto que tienen hecho para la decodificación en java:

La web de la librería:

Aquí tengo el programa iniciado, cargando la imagen de mi web:


El log resultado de la captación de la imagen:

Starting QRCode Decoder GUI Example ...
Decoding started
Drawing matrix.
Scanning Finder Pattern.
originPoint is: (37,37)
FinderPattern at
(37,37)(147,37)(37,147)
Angle*4098: Sin 0 Cos 8388608
Version: 3
Adjust AP(1,1) to d(0,-1)
AlignmentPatterns at
(52,52)(132,52)
(52,132)(132,132)
Creating sampling grid.
Reading grid.
gridSize=29
Created QRCode symbol.
Reading symbol.
Version: 3-M
Mask pattern: 011
Correcting data errors.
Reading data blocks.

Decode result:
http://www.thundaxsoftware.org


Por lo tanto, crear aplicaciones donde podamos utilizar esta codificación es relativamente sencillo, y podemos encontrar la implementación hecha en varios lenguajes de programación.

Aquí os dejo también un par de vídeos mostrando la utilidad del QR Code:




Friday, 22 May 2009

Copy-paste de un Excel a un TStringGrid

Hace días que intento encontrar una forma de copiar del clipboard el contenido de un excel hacia un TStringGrid, y ahora que lo tengo, aquí os muestro como hacerlo, es bastante sencillo, y solo hay que parsear el contenido del clipboard e ir ubicando cada celda en su correspondiente sitio. El ejemplo que os pongo es bastante sencillo, y permite dar una visión más grande de lo que se puede llegar a hacer, en mi caso estaba harto de tratar con TMemo, porque requería de uno más por cada columna que necesitaba, y en este caso con el componente TStringGrid, puedo personalizar las columnas a mi antojo.


En este caso lo que haré, es crear una clase personalizada de TStringGrid -> TCustomGrid, donde incluiré un par de propiedades públicas que me ayuden a parsear el contenido del clipboard. Aquí os dejo el código fuente:




uses
Clipbrd;

type
TTest = class(TForm)
sgTest: TStringGrid;
procedure sgTestKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
public
{ Public declarations }
end;

type
TCustomGrid = class(TStringGrid)
const
cRETURN1 = #13;
cRETURN2 = #10;
cTAB = #9;
public
iCol: integer;
iRow: integer;
procedure LoadParam();
procedure ClearCells();
procedure AddValue(Value: string);
end;


procedure TTest.sgTestKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
var
Value: string;
Str: string;
i: Integer;
Custom: TCustomGrid;
begin
if (Shift = [ssCtrl]) and (Key = 67) then //CONTROL+C (Copiar)
begin
Str := '';
for i := 1 to sgtest.ColCount - 1 do
begin
Str := Str + sgtest.Cells[i, sgtest.Row] + chr(9);
end;
Str := Copy(Str, 1, Length(Str) - 1);
Clipboard.Open;
Clipboard.AsText := Str;
Clipboard.Close;
end
else if (Shift = [ssCtrl]) and (Key = 86) then //CONTROL+V (Pegar)
begin
Clipboard.Open;
if not Clipboard.HasFormat(CF_Text) then
Exit;
Value := Clipboard.AsText;
Clipboard.Close;
Custom := TCustomGrid(sgtest);
Custom.LoadParam;
Custom.ClearCells;
for i := 1 to Length(Value) do
begin
if Copy(Value, i, 1) = Custom.cRETURN1 then
Continue;
if Copy(Value, i, 1) = Custom.cRETURN2 then
begin
Custom.iCol := Custom.Col;
Inc(Custom.iRow);
if i < Length(Value) then
Custom.ClearCells;
Continue;
end;
if Copy(Value, i, 1) = Custom.cTAB then
begin
Inc(Custom.iCol);
if i < Length(Value) then
Custom.ClearCells;
Continue;
end;
Custom.AddValue(Copy(Value, i, 1));
end;
if Custom.RowCount - 1 < Custom.iRow then
Custom.RowCount := Custom.iRow;
if Custom.InplaceEditor = nil then
Exit;
Custom.InplaceEditor.Text := Custom.Cells[Custom.Col, Custom.Row];
Custom.InplaceEditor.SelStart := Length(Custom.Cells[Custom.Col, Custom.Row]);
end;
end;

{ TCustomGrid }

procedure TCustomGrid.AddValue(Value: string);
begin
Self.Cells[Self.iCol, Self.iRow] := Self.Cells[Self.iCol, Self.iRow] + Value;
end;

procedure TCustomGrid.ClearCells;
begin
Self.Cells[Self.iCol, Self.iRow] := '';
end;

procedure TCustomGrid.LoadParam;
begin
self.iCol := self.Col;
self.iRow := self.Row;
end;







Wednesday, 20 May 2009

Cifrado RC4

El RC4 es un algoritmo de cifrado en flujo creado en secreto por Ron Rivest en 1987 para su compañía RSA (Rivest-Shamir-Adleman). Es usado en varios productos comerciales como Lotus Notes, Secure Sockets Layer (SSL), RSA SecurPC, WatchGuard, etc.
En Septiembre de 1994 se posteó anónimamente en los Newsgroups una rutina que decía ser el RC4. Se probó contra una copia del RC4 y las pruebas indicaron que ambas rutinas actuaban de la misma forma, por lo que el RC4 ya no es un secreto. El RC4 tiene una clave de 2048 bits, lo que hace que el algoritmo sea rápido y seguro. Crea bytes aleatorios a partir de la clave y hace la operación XOR byte a byte con el archivo a cifrar.
Podemos encontrar la implementación del código en varios lenguajes de programación: Delphi, Java, .net y todos ellos libres. Aquí os dejo varios enlaces para descargaros el código fuente:
Delphi Cryptography o también Rivest Cipher 4 y Java RC4.

El diagrama del algoritmo, lo podemos encontrar en Wikipedia:

Encontrareis una buena explicación de éste en la Wikipedia, explicando la permutación de registros y como monta el cifrado.

Gracias a estos algoritmos de cifrado, podemos crear aplicaciones que lo implementen y crear mensajes bastante seguros combinando varias implementaciones de cifrados. En mi caso, por ejemplo, aquí os presento a Thundax Cript Text, una pequeña herramienta que crea mensajes cifrados mediante el cifrado RC4 y aplicando un hash SHA1 sobre la clave. De esta manera genero mensajes cifrados y muy seguros.

De esta manera, la frase "esto es una prueba" se cifra como "vRU4HmipBUiMU6qrIa6tu5ID" con una clave de 7 caracteres. El hecho de la clave genere un hash, provoca que el querer hacer un brute-force sobre este sea casi imposible.

Podéis descargar la aplicación en el siguiente enlace : Thundax Cript Text

Y aquí teneis un pequeño reto:

Encontrar la semilla del siguiente texto:

dbFsdljNAZxRzN1WHXZ4XbZ/yk82QDJ+F2GfO6xx1OASdP1DhjFObLX1xPfIBOoOF/V+qQ==
IJMOB8y2KlOrJYZJ1kngKzBw4k0ky6atojw=

Notas, la semilla como he comentado antes, pasa por un hash SHA1, y luego se encripta el texto con un cifrado RC4, la semilla no tiene más de 10 carácteres y el charset es "A-Za-z".


  • Enlaces de interés:
http://java.ittoolbox.com/code/d.asp?d=2828&a=s
http://edipermadi.wordpress.com/2009/01/02/fast-rc4-stream-cipher-implementation-on-avr/

Monday, 18 May 2009

Delphi SMTP con Indy 10

El Protocolo Simple de Transferencia de Correo o SMTP es un protocolo de la capa de aplicación. Protocolo de red basado en texto utilizado para el intercambio de mensajes de correo electrónico entre computadoras u otros dispositivos (PDA's, teléfonos móviles, etc.).

Mediante los componentes de Indy 10 (los que vienen con el Delphi 2009), podemos crear aplicaciones que utilizen este protocolo para enviar correos. En la siguiente aplicación que os muestro Thundax Mail Sender, utilizaré estos componentes y enviaré correos mediante gmail. Para mi ejemplo, utilizaré los siguientes componentes:

Un componente SMTP, uno que gestiona el SSL y otro que es el que encapsula el mensaje IdMessage. Con esta aplicación, podré enviar correos en un formato sencillo, sin tener que entrar en mi cuenta de correo (ya lo hará la aplicación), y lo más interesante, puedo crear una lista de mailing para enviar correo masivo a toda una lista de contactos.


La configuración de los componentes es la siguiente:

.pas:


procedure TForm1.Button1Click(Sender: TObject);
begin
    IdSMTP1.Host := server.text;
    IdSMTP1.Password := password.Text;
    IDSMTP1.Port := StrToInt(port.Text);
    IdSMTP1.Username := from.Text;

    IdSSLIOHandlerSocketOpenSSL1.Destination :=  server.text + ':' + port.Text;
    IdSSLIOHandlerSocketOpenSSL1.Host := server.Text;
    IdSSLIOHandlerSocketOpenSSL1.Port := IDSMTP1.Port;

    IdSMTP1.Connect;
    IdMessage1.From.Address := from.text;
    IdMessage1.Recipients.EMailAddresses := EdTo.text;
    IdMessage1.Subject := Subject.text;
    IdMessage1.Body.Text := Message.text;
    IdSMTP1.Send(IdMessage1);
    IdSMTP1.Disconnect;
end;




.dfm:


  object IdSMTP1: TIdSMTP
OnStatus = IdSMTP1Status
IOHandler = IdSSLIOHandlerSocketOpenSSL1
Host = 'smtp.gmail.com'
    Password = 'mypassword'
    Port = 587
    SASLMechanisms = <>
    UseTLS = utUseExplicitTLS
Username = 'my@email.com'
    Left = 112
    Top = 304
  end
  object IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL
Destination = 'smtp.gmail.com:587'
    Host = 'smtp.gmail.com'
    MaxLineAction = maException
Port = 587
    DefaultPort = 0
    SSLOptions.Mode = sslmUnassigned
SSLOptions.VerifyMode = []
    SSLOptions.VerifyDepth = 0
    OnStatusInfo = IdSSLIOHandlerSocketOpenSSL1StatusInfo
Left = 112
    Top = 352
  end
  object IdMessage1: TIdMessage
AttachmentEncoding = 'UUE'
    BccList = <>
    CCList = <>
    Encoding = meDefault
FromList = <
      item
end>
    Recipients = <>
    ReplyTo = <>
    ConvertPreamble = True
Left = 112
    Top = 256
  end




Utilizaré el servidor SMTP de gmail -> smtp.gmail.com y el puerto 587. Si lo queremos enviar con otro servidor, simplemente tenemos que buscar cuál es su servidor smtp y su puerto, por ejemplo para yahoo, me parece que tenemos que utilizar el smtp.mail.yahoo.com y el puerto 465. Otro de las pruebas hechas y con la ayuda de un compañero es sobre el servidor smtp de telefonica -> smtp.telefonica.net y el puerto 25. Pero al parecer no funciona con el nombre, y hay que entrar la IP directamente: 213.4.149.228 y el puerto 25.

Una vez tengamos los parámetros entrados correctamente, la aplicación avisa del estado y muestra la conexión SSL:

SSL status: "before/connect initialization"
SSL status: "before/connect initialization"
SSL status: "SSLv3 write client hello A"
SSL status: "SSLv3 read server hello A"
SSL status: "SSLv3 read server certificate A"
SSL status: "SSLv3 read server done A"
SSL status: "SSLv3 write client key exchange A"
SSL status: "SSLv3 write change cipher spec A"
SSL status: "SSLv3 write finished A"
SSL status: "SSLv3 flush data"
SSL status: "SSLv3 read finished A"
SSL status: "SSL negotiation finished successfully"
SSL status: "SSL negotiation finished successfully"
Cipher: name = RC4-MD5; description = RC4-MD5 SSLv3 Kx=RSA Au=RSA Enc=RC4(128) Mac=MD5;bits = 128;version =TLSv1/SSLv3;
SSL status: "SSL negotiation finished successfully"

Podéis encontrar la aplicación en el siguiente enlace : Thundax Mail Sender.


Friday, 15 May 2009

Persistencia en Java con Hibernate (Parte II)

Ahora nos centraremos en utilizar las herramientas de Hibernate para eclipse, y poder generar los ficheros de configuración mediante asistentes, sin tener que picar todo los xml a mano. Primero necesitamos las herramientas del Hibernate, y las podemos descargar del siguiente enlace : Hibernate Tools. Una vez instalado, si iniciamos el eclipse, en este caso Eclipse JEE 3.4.2 Ganymede, podremos ver que nos aparecen unas opciones Hibernate cuando queremos hacer un nuevo proyecto:


Ahora, creamos un nuevo proyecto, y lo configuramos como indica la siguiente imagen:

Creamos un proyecto vacío, solo con las librerias y el package hecho "mySQL". Ahora lo que haremos es iniciar los asistentes de hibernate y utilizaremos ingenieria inversa para crear las clases necesarias a partir de los datos de la BD. En la primera parte de este ejemplo, creamos todo desde cero, y ahora que ya lo tenemos representado en la BD, utilizaremos lo he tenemos en la BD para generar automáticamente los fichero .java y .hbm.xml y ahorrarnos un montón de horas picando código mediante hbm2java y las tools de Hibernate.

Ahora añadiremos el fichero de configuración del Hibernate, mediante la opción anterior New -> Hibernate configuration file (cfg.xml):


Una vez tenemos el fichero hibernate.cgf.xml, vamos a crear una configuración de consola, por lo tanto vamos a new -> Hibernate Console Configuration.


Seleccionamos nuestro fichero hibernate.cgf.xml y dejamos preparado el fichero de propiedades log2j.properties, y le damos a Finish. Ahora tenemos que generar el Hibernate Reverse Engineering, que nos permitirá crear nuestras clases y ficheros hbm.xml a partir de la estructura de la BD.

Ahora, hacemos new -> Hibernate Reverse Engineering.

Hacemos next, y configuramos la consola de configuración, y luego accedemos a nuestra BD. Una vez vemos la estructura de nuestra BD, seleccionamos la tabla "users", que es la que creamos en la primera parte. Hacemos Finish, y veremos que la estructura de nuestro proyecto aparece de la siguiente manera:


Ahora, ya podemos ejecutar la aplicación para la generación de código con Hibernate. Esto lo haremos desde el menú :

Una vez dentro del Hibernate Code Generation Configurations, creamos uno nuevo, y lo configuramos de la siguiente manera:


Ahora de esta manera vemos que al hacer run, se han añadido los ficheros que queríamos sobre la tabla users y se ha generado la classe users.java como se indica en la siguiente imanen:

Ahora que ya tenemos las clases que necesitamos y los ficheros creados, modificamos los diferentes xml y añadimos las clases que gestionan el proyecto y lo iniciamos como en la parte 1 mediante la vista del "ant", y veremos que nos lista otra vez los datos de la BD:


  • Enlaces de interés:
http://docs.jboss.org/tools/3.0.0.CR1/en/hibernatetools/html_single/index.html#setup

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.