Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

May 2011 - Posts

Generación de Código e Inteligencia Artificial

A finales de los setenta y principios de los ochenta (ya en el siglo pasado ;-)) estuve aprendiendo de todo sobre programación, lenguajes de programación y ciencia de la computación en general. Eran los días de COBOL, BCPL, ensamblador de IBM/360, JCL (Job Control Language), tarjetas perforadas, sistemas operativos Pick, Algol/W, Fortran, PL/I, y C (mi preferido) (en CP/M, DOS, Unix/Xenix). Uno de mis temas preferidos (entonces y ahora) era la Inteligencia Artificial, un término amplio (aún más en estos días). Esos días fueron la época dorada de los sistemas expertos (recuerdo Mycin, EMycin, el configurador automático de DEC, y otros). En ese entonces, estuve escribiendo montones de código C, entonces, como programador perezoso que soy, comencé a escribir mis propios utilitarios en C para generar más programas C ;-). Desde entonces que tengo na idea:

La Generación de Código debe ser una aplicación de Inteligencia Artificial

Hacia finales de los ochenta, principio de los noventas, el monto de código a escribir para tener una aplicación profesional fue disminuyendo (nuevas IDEs, “wizards” y librerías). Pero fue un estado transitorio: la complejidad impuesta por el mercado, por los requerimientos de los clientes, la aparición de aplicaciones en línea, concurrencia en todo, nuevas tecnologías y lenguajes (Windows, GUIs…) todo conspiró para el aumento del esfuerzo que debe ser empleado para cualquier aplicación no trivial. Abracé a Java a mitad de los 90, y luego a .NET en 2001. Grandes librerías de clase, nuevos trucos, estilos arquitectónicos, patrones, JSP, JSF, MVC, ASP.NET, Javascript, Ajax, algo de PHP, computación distribuida, capas físicas, escalabilidad, seguridad, rendimiento, …. De nuevo, el desarrollode software se transformó en ámbito complejo. Y ahora, tenemos mobile, Android, Windows Phone 7, ASP.NET MVC, nuevos JDKs, JVMs, lenguajes dinámicos… Podría continuar enumerando tecnologías sin por siempre… ;-)

Por eso, al comienzo de este siglo, decidí comenzar my proyecto de generación de código, AjGenesis. Primero, éste es el proceso actual que uso en mis proyectos:

(Jeje… Ok, son un desarrollador, no un diseñador ;-))

Los modelos de partida (a la izquierda) son de libre definición: uno puede usar el modelo que quiera y le convenga, no están “fijos”. Esta es una decisión clave: yo quiero que podamos tener la libertad para comenzar con cualquier modelo que elijamos como base para nuestra generación de código. Pero es importante destacar: debe haber un MODEL. No es sólo generación de código: lo que propugno es GENERACION DE CODIGO DESDE UN O VARIOS MODELOS. Usualmente, tengo un modelo abstracto, como:

<Project>
	<Name>AjApp</Name>
	<Description>Building an Application using AjGenesis</Description>
	<Model>
		<Entities>
			<Entity Source="Entities/Customer.xml"/>
			<Entity Source="Entities/Supplier.xml"/>
		</Entities>
	</Model>
</Project>

y uno o más modelos dependientes de la tecnología como:

<Technology>
	<Programming>
		<Dialect>CSharp4NhMvc</Dialect>
	</Programming>
	<Database>
		<Dialect>MsSql</Dialect>
		<Name>AjTestNh</Name>
		<Host>.\SQLEXPRESS</Host>
	</Database>
	<NHibernate>
		<Dialect>NHibernate.Dialect.MsSql2000Dialect</Dialect>
	</NHibernate>
</Technology>

(más información en mis AjGenesis posts (en inglés) y mis posts (en español)). Los modelos se pueden escribir en XML o en texto. O podemos usar nuestros propios modelos (ver Models for Code Generation in AjGenesis, Modelos para Generación de Código en AjGenesis). Los artefactos de texto pueden ser Java, C#, VB.NET, Ruby, cualquiera que necesitemos. Como ejemplo de lo flexible que es este “approach”: cuando comencé con los ejemplos de AjGenesis generé PHP y JSP 1.x. Desde entonces, prácticamente sin cambiar esencialmente el proceso, pude generar para PHP (nuevas versiones), Java, JSP 2.x, Hibernate, NHibernate, ASP.NET 1.x, ASP.NET 2.x, ASP.NET MVC… Pude adaptarme a cualquier tecnología basada en código. Para conseguir tal flexibilidad, no hay IDE asociada en el proyecto. Tiene una interfaz espartana, pero me gusta. Me permite ir explorando nuevos caminos, sin estar anclado a una IDEA, aunque hace menos atractivo su uso.

Desde su concepción, adopté un lenguaje dinámico, AjBasic, para soportar tareas y “templates” (plantillas): yo no quería SOLAMENTE un motor de transformaciones, de un modelo a código mediante plantillas. Quería tener el poder de un lenguaje GENERAL, pero que a la vez permita manejar obj.Propiedad, sobre un objeto no tipado de antemano: los objetos son tan maleables como los modelos iniciales, que son tratados accedidos como objetos. ¿Por qué un lenguaje general, y no uno que sólo soporte algún if y for a nivel de templates? PARA SOPORTAR LA EXTENSIBILIDAD, más allá de la expansión de plantillas. Y para poder acceder a toda la librería .NET (uno no debe desperdiciar una librería de clases cuando la encuentra). Y ¿para qué más? Sorpresa! Para ….. soportar Inteligencia Artificial!

Afirmo:

La Generación de Código desde un Modelo puede ser implementada como un Sistema Experto, y entonces, es una clara aplicación para Inteligencia Artificial

Gráficamente:

Podemos ver a los desarrolladores de software como expertos humanos. Podemos “destilar” su experiencia y colocarla en sistemas expertos, agregdos al proceso de generación de código. Algunos ejemplos:

- Un sistema experto peude usar un motor de reglas para tomar decisiones, pequeñas, medianas o grandes. Una pequeña: “El reporte web puede tener muchas filas, necesitamos paginación, filtro, y ordenar por tales columnas, que son las más importantes para este dominio… “. Y entonces, alguna parte del sistema parte recomendaría: “Sí! Podemos usar JQuery y JQuery.UI, hacer filtro del lado del servidor, servir los datos desde este acceso Ajax… etc…”… y otra parte (sí, el sistema experto podría ser “multi-mente” ;-)) diría “No! Necesitamos filtro del lado del servidor, y necesitamos estas consultas y servicios para cumplirlos, y estos puntos de acceso Ajax y … “ ¿Se capta la idea?

Una micro-decisión que empleé el año pasado en un proyecto real: algunas entidades en el modelo abstracto fueron marcadas con el atributo “massive” (masiva) indicando: “tenemos gran cantidad de instancias de la entidad X en el sistema”. Entonces, cuando en la interfaz de usario necesitamos seleccionar una de esas entidades X, en vez de presentar una lista desplegable, el motor de generación de código (con lenguaje general que decide con if, funciones y rutinas) colocó una caja de texto de búsqueda, con auto-complete, y un servicio Ajax en el servidor.

Podría usar para el modelo abstracto una ontología de base sobre la cual poder hacer inferencias y tomar decisiones. Ejemplo:

- Cada entidad puede ser: una persona, una compañía, un lugar, un bien físico, un evento, un documento. Entonces, el sistema experto podría inferir: un lugar físico tiene una dirección; un bien físico debe ser guardado en un lugar físico; un documento X debe ser necesario para representar el movimiento de un bien físico de un lugar a otro; una persona puede ser un empleado; cada empleado tiene seguridad social… etc…. etc.. Entonces, cuando necesitamos un reporte de los eventos X, podemos inferir: los necesitamos ordenados por fecha, agrupados por lugar físico, etc..… etc… Tal documento tiene un total de moneda, sería interesante tener reportes agrupados por distintos criterios (por ejemplo, por parte involucrada en ese documento, un cliente, una compañía, una persona). Y por mes. Ah! Y necesitamos un gráfico de torta, y … y así, así… ;-)

Podría agregar un motor de reglas para tomar algunas de estas decisiones, con reglas tipo “si la entidad X es masiva y la interfaz de usuario es web y podemos usar JQuery entonces… “. O reglas como: “si la entidad X hereda de la entidad Y y necesitamos persistencia, podemos adoptar la estrategia tabla-por-jerarquía. O tal vez alguna otra parte del sistema experto recomiendo tabla-por-clase-concreta. Y de ahí, generar los mapeos, la base de datos”. O: “esta entidad X es un documento que tiene una fecha; necesitamos agregar un índice sobre esta columna fecha/hora”. Es una forma de poner explícitamente las razones por las que tomamos decisiones como ésa cada día. Actualmente, en el proceso de AjGenesis, muchas de esas decisiones están en las tareas y en pistas que quedan en los modelos abstractos y de tecnologías. Pero bien podrían colocarse a nivel del sistema experto.

Como gran panorama, la generación de código desde un modelo involucra:

- Tenemos este punto de partida: modelos abstractos, modelos técnicos (con restriccionos como “este sistema lo necesitamos en ASP.NET MVC 3”)

- Esta es el entregable esperado (algún sistema ya armado parcialmente, que sirvió como prueba de concepto de las tecnologías que vamos a usar)

- Y tenemos todo este conocimiento experto destilado de la experiencia de nuestros desarralloradores, buenas prácticas, patrones...  Y con todo esto podemos ir desde el comienzo hasta el final esperado, aprovechando el conocimiento acumulado, configuraciones de ORM, librerías de inyección de dependencias, patrones de UI, patrones de implementación, conocimiento en la creación de bases de datos normalizadas, etc..

El desarrollo de software y su arquitectura está relacionados con:

- Conocer lo que necesitamos obtener
- Conocer cuales son los problemas a resolver (persistencia, concurrencia, seguridad, rendimiento, traza, interfaz usable por el usuario, etc…)
- Conocer cuales son las soluciones disponibles (base de datos relacionales, bases NoSQL, lenguajes de programación, librerías de clases, frameworks, tecnologías de interfaz de usuario, patrones de todo tipo…)

La generación de código desde un modelo tiene (para mí un gran) beneficio: pone en claro la existencia, SEPARACION y RELACIONES entre todos los puntos de arriba. En general, aprendemos todo eso pero centrados en una o dos tecnologías. Y cada problema viene mezclado con su solución. Y cada decisión nos viene “acoplada” a detalles técnicos, nos entretenemos en cada árbol, sin poder ver el bosque.

Otro camino a explorar (que inicialmente lo quise explorar para AjGo, mi código de soporte para investigar formas de jugar al Go): el uso de agentes expertos. ¿Qué significa “agente experto” en ésta, mi jerga? Mi visión: tener módulos, agentes que pueden ser agregados y removidos sin mayor esfuerzo, que puedan resolver problemas. Alguien podría desarrollar un agente experto en JavaServer Faces. Podría ser que alguien tenga un agente especialista en el nuevo ASP.NET MVC 6.6, o en el JSF 17.2, o en el nuevo “SuperDuperOpenSourceUIThatIsTheNextBigThingHere”. No impoirta, siempre podemos adoptar NUEVAS SOLUCIONES a PROBLEMAS SIMILARES (ingresar la factura en una interfaz, consultar el estado de tal tema, realizar operaciones), habiendo destilado el nuevo conocimiento y “envasádolo” en un nuevo agente experto.

Ok, todas estas son razones para un (¿próximo?) AjRules ;-). Y la justificación de mi trabajo con AjLisp (see here) y algo del código de un viejo AjProlog a ser portado a .NET (ver algo en AjCodeKatas).

Otros posts relacionados con el tema:

Modelos, decisiones, Inteligencia Artificial en AjGenesis
Sobre la Generación de Código

Imagen tomada de Artificial Intelligence, Introduction & Application (Part I)

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, May 31 2011 11:28 by lopez | 1 comment(s)

NHibernate 3 (Parte 7) One-To-Many con Inverse

Anterior Post
Siguiente Post

En el anterior ejemplo tenía un problema a resolver: por cada capítulo de un libro nuevo se ejecutaba un INSERT Y un UPDATE, cuando bien podría bastar uno. En este post, uso una manera alternativa de mapear una lista uno-a-varios, usando el atribute inverse. Como ya es usual, el código del ejemplo está en mi AjCodeKata Code Project, en el directorio trunk/NHibernate/BookChapters. Pueden bajarse una versión “congelada a hoy” desde NHibernate3BookChaptersInverse.zip.

Modifiqué la anterior solución, agregando un nuevo proyecto de consola Books.Inverse, copiando el proyecto Books.Console original. Entonces, agregué un nuevo atributo en el mapeo de Book:

<list name="Chapters" cascade="all-delete-orphan" 
  inverse="true">
  <key column="BookId"/>
  <index column="ChapterIndex"/>
  <one-to-many class="Chapter"/>
</list>

El nuevo atributo es inverse=”true”. Con esta pista, NHibernate conoce que el manejo de la lista y sus elementos está bajo el control del programador. ¿Qué implica esto? Ejecuté el nuevo programa de consola: cada capítulo es grabado con un solo INSERT, pero el resultado en la base de datos fue:

No hay BookId, no hay ChapterIndex con valores, son todos null! El problema está en el código original:

cookbook.Chapters.Add(new Chapter() { Title = "Models and Mappings" });
cookbook.Chapters.Add(new Chapter() { Title = "Configuration and Schema" });
cookbook.Chapters.Add(new Chapter() { Title = "Sessions and Transactions" });

Cuando creo un nuevo Chapter, no pongo valores en Book o en su Chapter Index. Esos datos los ponía el NHiberante. Pero ahora con inverse=”true”, esos valores caen bajo mi responsabilidad: NHibernate no interviene. Entonces, para solucionar el problema, cambié la clase Chapter, agregando una nueva propiedad:

// Used in Inverse example
public virtual int ChapterIndex { get; set; }

Y cambié el código a:

cookbook.Chapters.Add(new Chapter() { Title = "Models and Mappings", Book = cookbook, ChapterIndex = 0 });
cookbook.Chapters.Add(new Chapter() { Title = "Configuration and Schema", Book = cookbook, ChapterIndex = 1 });
cookbook.Chapters.Add(new Chapter() { Title = "Sessions and Transactions", Book = cookbook, ChapterIndex = 2 });

Ahora, la salida muestra los insert correctos:

NHibernate: INSERT INTO Books (Title, Author, Id) VALUES (@p0, @p1, @p2);@p0 = 
'NHibernate Cookbook' [Type: String (4000)], @p1 = 'Jason Dentler' 
[Type: String (4000)], @p2 = b643aa0e-1917-4066-96a4-64a09562a58a [Type: Guid (0)]

NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
 (@p0, @p1, @p2, @p3, @p4);@p0 = 'Models and Mappings' [Type: String (4000)], @p1 
 = NULL [Type: String (4000)], @p2 = b643aa0e-1917-4066-96a4-64a09562a58a
 [Type : Guid (0)], @p3 = 0 [Type: Int32 (0)], @p4 = 
 df9114af-7183-4b1b-9826-d12982a898a5 [Type: Guid (0)]
 
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
 (@p0, @p1, @p2, @p3, @p4);@p0 = 'Configuration and Schema' 
 [Type: String (4000)], @p1 = NULL [Type: String (4000)], @p2 =
 b643aa0e-1917-4066-96a4-64a09562a58a [Type: Guid (0)], @p3 = 1 [Type: Int32 (0)],
 @p4 = 3fc1df36-c7de-4ff8-add2-8f843b627115 [Type: Guid (0)]

NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
 (@p0, @p1, @p2, @p3, @p4);@p0 = 'Sessions and Transactions' 
 [Type: String (4000)], @p1 = NULL [Type: String (4000)], @p2 = 
 b643aa0e-1917-4066-96a4-64a09562a58a  [Type: Guid (0)], @p3 = 2 [Type: Int32 (0)],
 @p4 = 9c8891e4-6419-4469-aa3a-154f2c908985 [Type: Guid (0)]

Los datos grabados están bien:

Todo bien! Noten que no tuve que agregar los capítulos a la sesión de NHibernate: son grabados como parte del libro nuevo, igual que en el anterior ejemplo. Pero con inverse=true, tuve que poner los valores de la relación inversa (de Chapter a Book) y la propiedad de orden que había elegido.

Próximos pasos: explorar borrado y actualización, mapeos más complejos, otras opciones de logging, otras formas de hacer mapeos (como Fluen NHibernate, ConfORM, y el nuevo mapeo por código de NHibernate 3.2).

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Mon, May 30 2011 10:44 by lopez | with no comments

Filed under: , ,

NHibernate 3 (Parte 6) One-To-Many con Many-To-One

Anterior post
Próximo post

Esta vez, la base de datos es la misma del anterior ejemplo (mismo nombre y scripts de creación):

Pero quiero tener una referencia, en el dominio, de Chapter (capítulo) a Book (libro):

public class Chapter
{
    public virtual Guid Id { get; set; }
    public virtual string Title { get; set; }
    public virtual string Notes { get; set; }
	
	// New property
    public virtual Book Book { get; set; } 
}

Ass que cambié el mapeo de Chapter a:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Books"
namespace="Books">
  <class name="Chapter" table="Chapters">
    <id name="Id">
      <generator class="guid" />
    </id>
    <property name="Title" not-null="true" />
    <property name="Notes" />
    <many-to-one name="Book" column="BookId" />
  </class>
</hibernate-mapping>

El nuevo elemento es many-to-one. El mapeo de Book no tiene cambios:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Books"
namespace="Books">
  <class name="Book" table="Books">
    <id name="Id">
      <generator class="guid" />
    </id>
    <property name="Title" not-null="true" />
    <property name="Author" not-null="true"/>
    <list name="Chapters" cascade="all-delete-orphan">
      <key column="BookId"/>
      <index column="ChapterIndex"/>
      <one-to-many class="Chapter"/>
    </list>
  </class>
</hibernate-mapping>

Agregué el “show_sql” con valor “true” en el archivo de configuración, en la sección de NHibernate:

  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
      <property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.connection_string">Data Source=.\SQLEXPRESS;Initial Catalog=NHibernate3BooksOneToMany;Integrated Security=True</property>
      <property name="proxyfactory.factory_class">
        NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle
      </property>
     <property name="show_sql">true</property>
      <mapping assembly="Books.Console" />
    </session-factory>
  </hibernate-configuration>

Esta propiedad es muy útil para aprender qué hace el NHibernate por debajo: muestra en consola los comandos SQL que NHibernate ejecuta durante su operación. Este es programa de consola:

ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
using (ISession session = sessionFactory.OpenSession())
{
    using (ITransaction tx = session.BeginTransaction())
    {
        Book cookbook = new Book()
        {
            Title = "NHibernate Cookbook",
            Author = "Jason Dentler",
            Chapters = new List<Chapter>()
        };
        cookbook.Chapters.Add(new Chapter() { Title = "Models and Mappings" });
        cookbook.Chapters.Add(new Chapter() { Title = "Configuration and Schema" });
        cookbook.Chapters.Add(new Chapter() { Title = "Sessions and Transactions" });
        session.Save(cookbook);
        tx.Commit();
        session.Close();
    }
}
using (ISession session = sessionFactory.OpenSession())
{
    foreach (Book book in session.Query<Book>().Fetch(b => b.Chapters))
        {
            System.Console.WriteLine(string.Format("Book {0}", book.Title));
            int nchapter = 0;
            foreach (Chapter chapter in book.Chapters)
            {
                System.Console.WriteLine(string.Format("Chapter {0}:{1}", ++nchapter, chapter.Title));
                System.Console.WriteLine(string.Format("From Book {0}", chapter.Book.Title));
            }
        }
}
System.Console.ReadKey();

Tengo un nuevo método: .Fetch. Lo explicaré más abajo. Ahora, ejecuto el programa, y examino la salida. Al principio aparece:

NHibernate: INSERT INTO Books (Title, Author,
 Id) VALUES (@p0, @p1, @p2);@p0 = 'NHibernate Cookbook' [Type: String (4000)], @p1 = 
'Jason Dentler' [Type: String (4000)], 
@p2 = 20d05308-aea1-49f2-9b4a-7441e73dab4e [Type: Guid (0)]

Es el comando de inserción de los datos de libro. Luego aparece:

NHibernate: INSERT INTO Chapters (Title, Notes, BookId, Id) VALUES (@p0, @p1, @p 
2, @p3);@p0 = 'Models and Mappings' [Type: String (4000)], @p1 = NULL [Type: 
String (4000)], @p2 = NULL [Type: Guid (0)], @p3 = 
15ce4169-08ef-4f75-8493-8fe68da7e918 [Type: Guid (0)] 
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, Id) VALUES (@p0, @p1, @p2, @p3);
@p0 = 'Configuration and Schema' [Type: String (4000)], @p1 = NULL [Type: String (4000)],
 @p2 = NULL [Type: Guid (0)], @p3 = 3edb510d-4cbd-44fd-b4a1-094a258ec07f [Type: Guid (0)] 
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, Id) VALUES (@p0, @p1, @p2,
 @p3);@p0 = 'Sessions and Transactions' [Type: String (4000)], @p1 = NULL [Type: String (4000)], @p2 = NULL [Type: Guid (0)], @p3 = af025d1b-cdc6-4c8f-9014-71c5dee135e1 [Type: Guid (0)]

Estos comandos insertan los tres capítulos. Pero, epa! NO HAY nada que guarde el BookId de nuestro libro (noten que @p2 = NULL) y ni aparece la columna ChapterIndex. Curiosamente, NHibernate pone los valores de esas columnas EN UNA SEGUNDA PASADA:

NHibernate: UPDATE Chapters SET BookId = @p0, ChapterIndex = @p1 WHERE Id = @p2; 
@p0 = 20d05308-aea1-49f2-9b4a-7441e73dab4e [Type: Guid (0)], @p1 = 0 [Type: 
Int32 (0)], @p2 = 15ce4169-08ef-4f75-8493-8fe68da7e918 [Type: Guid (0)] 
NHibernate: UPDATE Chapters SET BookId = @p0, ChapterIndex = @p1 WHERE Id = @p2; 
@p0 = 20d05308-aea1-49f2-9b4a-7441e73dab4e [Type: Guid (0)], @p1 = 1 [Type: 
Int32 (0)], @p2 = 3edb510d-4cbd-44fd-b4a1-094a258ec07f [Type: Guid (0)] 
NHibernate: UPDATE Chapters SET BookId = @p0, ChapterIndex = @p1 WHERE Id = @p2; 
@p0 = 20d05308-aea1-49f2-9b4a-7441e73dab4e [Type: Guid (0)], @p1 = 2 [Type: 
Int32 (0)], @p2 = af025d1b-cdc6-4c8f-9014-71c5dee135e1 [Type: Guid (0)]

Esto es raro (y es la razón por la que en el anterior ejemplo tuve que definir a ChapterIndex y a BookId como columnas que acepten null en la base de datos). Yo hubiera esperado que NHibernate las grabe en una sola pasada, ya en el INSERT de cada capítulo. Pero parece que tiene su propia “opinión y conducta”, y para él lo “natural” es hacerlo en dos pasadas. Esta es una de las MUCHAS conductas que hay que aprender para realmente domina NHibernate. Y no es una conducta evidente. Noten la utilidad de usar show_sql: hay otras opciones de logueo en NHibernate (por ejemplo, log4net), pero pueden comenzar con esta simple propiedad para sus programas iniciales. Debería mejorara esto de DOS pasadas a UNA, en un próximo post.

Este es el comando de Select:

NHibernate: select book0_.Id as Id0_0_, chapters1_.Id as Id1_1_, book0_.Title as 
Title0_0_, book0_.Author as Author0_0_, chapters1_.Title as Title1_1_, chapters 
1_.Notes as Notes1_1_, chapters1_.BookId as BookId1_1_, chapters1_.BookId as Boo 
kId0__, chapters1_.Id as Id0__, chapters1_.ChapterIndex as ChapterI5_0__ from Bo 
oks book0_ left outer join Chapters chapters1_ on book0_.Id=chapters1_.BookId

Sorpresa! El select recupera Books Y Chapters, usando un outer join. De esta manera, NHibernate puede llenar los datos de los capítulos correspondientes a cada libro en un solo comando. Esta no es la conducta asumida: los capítulos no son recuperados cada vez que recuperamos un libro. NHibernate asume que cargar los datos de Chapters SOLO cuando comenzamos a iterar sobre la lista. El comando de arriba, con el join agregado es consecuencia de haber usado el Fetch:

foreach (Book book in session.Query<Book>().Fetch(b => b.Chapters))

Ese método adicional, dice: “Hey, NHibernate! Ve y trae los libros, pero los capítulos también, que ya te aviso que los voy a necesitar!”. Pueden experimentar qué pasa si sacan el Fetch. El programa sigue funcionando, y mostrando los capítulos de cada libro. Pero ahora la salida (parcial) sería (con otros datos):

NHibernate: select book0_.Id as Id0_, book0_.Title as Title0_, book0_.Author as 
Author0_ from Books book0_ 
Book NHibernate Cookbook 
NHibernate: SELECT chapters0_.BookId as BookId1_, chapters0_.Id as Id1_, chapter 
s0_.ChapterIndex as ChapterI5_1_, chapters0_.Id as Id1_0_, chapters0_.Title as Title1_0_,
 chapters0_.Notes as Notes1_0_, chapters0_.BookId as BookId1_0_ FROM Chapters
 chapters0_ WHERE chapters0_.BookId=@p0;@p0 = 
cfb043eb-6ffc-4727-a5ad-05afe867ab57 [Type: Guid (0)] 
Chapter 1:Models and Mappings 
From Book NHibernate Cookbook 
Chapter 2:Configuration and Schema 
From Book NHibernate Cookbook 
Chapter 3:Sessions and Transactions 
From Book NHibernate Cookbook

Hay un SELECT para el libro, y luego, PARA CADA LIBRO hay un select para recuperar sus capítulos. Arriba se muestra un solo libro, pero si tuviéramos 10, tendríamos 10 comandos select de capítulos. De nuevo: otra “conducta” a tener en cuenta para tener un programa que no abuse de enviar comandos a la base de datos. Notemos que la salidad:

Book NHibernate Cookbook

 

APARECE ANTES que el select de los capítulos. Sólo hasta que el programa comienza a iterar por la lista de capítulos de un libro, envía el SELECT correspondiente.

Otro punto: el chapter.Book no está en nulo. Gracias a NHibernate, la referencia de Chapter a Book ha sido puesta de forma automática.

Entonces, agregué el método .Fetch porque se que voy a listar los datos de los capítulos y los necesito. Igualmente, todavía tenemos que explorar alternativas para evitar el par INSERT-UPDATE cuando se da de alta un libro con capítulos, en próximo post.

Como otras veces, el código está en mi AjCodeKata Code Project, en el directorio trunk/NHibernate/BookChapters. Pueden bajara ahora mismoa una versión “congelada” desde my Skydrive: NHibernate3BookChapters.zip.

Próximos pasos: explorar el inverse=”true”, otras opciones de logging, many-to-many, cache de session, etc.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Mon, May 23 2011 11:04 by lopez | 1 comment(s)

Filed under: , ,

AjTalk: Implementando un intérprete tipo Smalltalk (Parte 1) Estructura de objetos

Comienzo a escribir sobre las decisiones de diseño que tuve que tomar para armar AjTalk, mi intérprete tipo Smalltalk de código abierto escrito en C#. Hay una largar tradición en el armado de máquinas virtuales Smalltalk. Veamos qué pude armar y por qué.

En est primer post quiero exporar cómo implementar el concepto central de todo compilador o intérprete que soporta objetos tipo Smalltalk. Un objeto en Smalltalk clásico tiene un estado interno, representado por variables de instancia (dejemos por un tiempo las variables indexadas). Una primera vista:

En muchas implementaciones de Smalltalk (incluyendo las primeras, como el clásico Smalltalk-80), cada objeto contiene valores de un tipo: cada objeto contiene ya sea, bytes, o words o punteros a otros objetos:



Es usal que algunos valores, antes It is also usual that some values, enlugar de ser punteros, son valores “primitivos” como los valores enteros. Para diferenciar un puntero de otro objeto, como un valor entero, algunos bits de la palabra que almacena el puntero/entero son usados para distinguir los dos casos. Un puntero puede referenciar a otro objeto:

Otras implementaciones usan un tabla de objetos. Los punteros referencian celdas de esa tabla, en vez de referenciar directamente al objeto:

Una tabla de objetos puede tener, en cada celda, información sobre el objeto, como su tipo, su clase, y el lugar en memoria de su estado interno. La estrategia de tabla de objetos simplifica operaciones como el “become:” y el “garbage collection” (la “recolección de basura”, detección y liberación de la memoria ya no usada). Puede ser usada para remover un mensaje de memoria y recuperarlo cuando un mensaje le sea enviado. Entonces, la celda tendría la información de persistencia.

¿Cómo implementar estas características en AjTalk? Desde el comienzo quise que AjTalk pueda acceder y manejar objetos .NET nativos. Así que permito dos tipos de objetos: los objetos .NET nativos (enteros, strings, arreglos, objetos de la librería de clases, por ejemplo, ADO.NET, etc..), y los objetos AjTalk. Así que en vez de manejar objetos que sólo contengan bytes, words o punteros, decidí que los objetos AjTalk tengan variables instancias que referencian a cualquier objeto, sea .NET o AjTalk.


Internamente, un objeto AjTalk mantiene un arreglo de objetos:

[Serializable]
public class BaseObject : IObject, ISerializable
{
    private IBehavior behavior;
    private object[] variables; // pointers to objects
    // ...
}


Un elemento de ese arreglo puede referenciar a un entero, un caracter, es decir, objetos primitivos, u objetos más complejos. .NET permite apuntar a valores elementales usando “boxing”, colocando esos valores dentro de un objeto en la memoria dinámica. Es el precio a pagar para tener la flexibilidad de manejar todo tipo de objetos: usar boxing/unboxing. Afortunadamente, .NET maneja ese mecanismo de forma transparente. Desde el punto de vista de código C#, todo ese almacenamiento via “boxing” no necesita ser explícito. Esta forma de armar objetos AjTalk también delega el manejo de “garbage collection” a la propia implementación de .NET.

Próximos pasos: explorar cómo implementar mensajes y su invocación, clases y herencia, y más.

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Fri, May 20 2011 11:04 by lopez | 2 comment(s)

Filed under: , ,

Aprendiendo Java

Ya varias veces me preguntaron por Twitter sobre recursos de Java para estudiar. Sea este post un intento de respuesta a esta pregunta, ampliada: no sólo recursos, sino, qué estudiar de Java (lo que dependerá del trabajo a encarar), y estudiar algo más amplio: programar.

El mundo de Java se ha ido expandiendo desde la aparición pública en 1995 del lenguaje, Java Virtual Machine, y librería de clases, de la mano de Sun. Siguieron versiones del lenguaje y librería. El mayor cambio ha sido en las versiones 1.5 y 1.6, donde aparecieron nuevas “features” del lenguaje, y se esperan más para el 1.7. Sin embargo, para gran parte de lo que se necesita para COMENZAR a aprender Java, es suficiente con saber manejarse con lo común a cualquier versión. Como saben, hace un tiempo Oracle compró a Sun, y veremos qué pasa con el desarrollo de las nuevas versiones, y el futuro de Java en general.

Para estudiar Java, hay que recordar:

- Se implementa sobre una JVM (Java Virtual Machine) multiplataforma
- Tiene una extensa librería de clases
- Como lenguaje, es orientado a objetos
- No tiene un entorno “prefijado” o preferido de desarrollo (IDE, Integrated Development Environment)

Paso a enumerar algunos recursos. Primero, el burro por delante ;-)… Tienen un curso incompleto pero espero que interesante, en mis recursos en línea:

Curso: Introducción a Java

Ahí también tienen otros cursos en línea. Luego, pueden ver ejemplos en mi Skydrive, de los cursos de Java que fui dando: Ejemplos de Java.

Luego, lo más recomendable en línea debe ser lo que había armado Sun, ahora bajo la égida de Oracle:

The Java Tutorials

Comenzaría por Learning the Java Language, Essential Java Classes, Collections. Tienen en esa página temas más especializados.

Visitaría a la gente de Java Hispano: http://www.javahispano.org. En especial su documentación http://www.javahispano.org/documentacion/ que es variada (en nivel, calidad, algunos elementos también tienen sus años).

Pero ahora viene parte de mi respuesta a la pregunta extendida: ¿Qué estudiar de Java? Yo recomiendo:

- Estudiar el lenguaje, que es sencillo, concentrándose en los conceptos de objetos
- Comenzar a estudiar la librería, con algunas colecciones.
- Seguir con algo que todos manejamos en otros lenguajes: acceso a archivos, descubriendo que en Java tenemos streams en general. Ver algo de sockets para ir viendo que “leer/escribir” es algo que se puede abstraer desde java.io. Ver ejemplos en mi Skydrive ClaseIo.zip, ClaseClienteServidor.zip.
- Ver acceso a base de datos usando JDBC.

Luego, lo que venga después, depende de lo que quieran hacer y el trabajo que tengan que encarar. Si tienen que hacer aplicaciones de escritorio, seguramente tendrán que elegir estudiar: Swing (ver los tutoriales de Java, por ejemplo Swing, Creating a GUI with Swing) o SWT The Standard Widget Toolkit. Si tiene que hacer aplicaciones web (algo casi inescapable en estos días), recomiendo:

- Bajar, instalar y jugar con el Tomcat de la fundación Apache
- Sólo si es necesario, ver JBoss (en mi opinión: “too much”, un Application Server que ha ido creciendo a partir de las especificaciones de Sun. Tiene una arquitectura microkernel, y eso es interesante. Pero para desarrollo web, aprendan con Tomcat.

Y comiencen a estudiar JavaServer Pages: sé que no es la “última tecnología web” pero si no entienden JSP no van a poder entender todo los modelos web de Java. Estudien en ese momento el modelo de Servlets, que es la base de todos los modelos. Luego, estudien Javascript en el cliente, y Ajax. Les debo enlaces sobre ese tema. Ver por ahora http://delicious.com/ajlopez/javascript+tutorial y http://delicious.com/ajlopez/ajax+tutorial.

Otro ámbito sería la programación para dispositivos móviles. Ahí les debo recomendaciones y enlaces, porque nunca me ha tocado aún ese tipo de programación.

Algo más, antes que me olvide: NO ESTUDIEN Enterprise JavaBeans, a no ser que su vida esté en peligro. Esa especificación de Sun,  a pesar de las mejores de 3.x, debería ser borrada de la historia humana. Tengo una teoría: los extraterrestres existen, nos vienen observando desde hace décadas. Vieron que manejamos armas nucleares, tuvieron gran debate, pero decidieron: “Démosle una oportunidad a los terráqueos”. Vieron que seguimos con guerras. Debatieron de nuevo: “Veamos de seguir dándoles una oportunidad”. Sus científicos de computación vieron que desarrollamos EJB (Enterprise JavaBeans) y Web Services sobre WCF (Windows Communication Foundation) y decretaron inmediatamente: “No tienen cura, están completamente perdidos en la irracionalidad, declaremos cuarentena” ;-).

Había planteado que en Java no hay una IDE prederminada. Pueden hacer todo desde la línea de comando, si quieren, y configurar su editor preferido para compilar y lanzar las aplicaciones que construyan. Pero recomiendo bajarse y estudiar el Eclipse. Van a encontrar diferentes versiones, ya preparadas, algunas orientadas a desarrollo web. Lo bueno de Eclipse: es sencillo, multiplataforma, maneja sus propios widgets gráficos basados en SWT (con lo que se adapta al sistema gráfico nativo), tiene una comunidad activa, es muy usado en los ambientes de desarrollo, está al nivel de cualquier IDE comercial. Es fácil de instalar y desinstalar (prácticamente copiar archivos). Sólo tendrán que tener paciencia en la elección de los plugins que quieran usar: Eclipse es totalmente extensible por plugins, y de ahí que uno extrañe un poco lo que se tiene en .NET: una IDE (el Visual Studio) que tiene prácticamente todo lo necesario para programar sistemas “out-of-the-box”. Pero Eclipse “out-of-the-box” es totalmente adecuado para estudiar Java. El tema es configurarlo para el trabajo que encaren: he ido a consultoras donde cada máquina y proyecto tienen un Eclipse distinto ;-)

Pero no es sólo visitar la librería de clases, y ver el Eclipse. Hay que aprender a programar, a pensar en objetos, y sobre técnicas de programación que hoy tenemos disponibles. Para el tema de pensar en Java con objetos, no sólo programar, pueden bajarse el libro Thinking in Java de Bruce Eckel, la tercera edición:

http://www.mindviewinc.com/downloads/TIJ-3rd-edition4.0.zip (hay que hacer un curso para encontrar este enlace desde http://www.mindview.net/Books/TIJ/ ;-) Tiene otros libros para bajar de Bruce Eckel desde http://www.mindviewinc.com/downloads/

Otra cosa, una opinión sobre la certificación: pienso que no hace falta, sólo para el currículum y búsqueda de trabajo. La mejor certificación es la experiencia en el desarrollo. Vean que Java es más que un lenguaje, es una tecnología con amplia librería de clases, utilitarios, librerías adicionales, servidores, etc. Es un mundo muy amplio. Vayan de a poco, aprendiendo las partes que recomendé y luego viendo las partes que necesitan para cada trabajo en particular. También van a encontrar que muchas veces tiene sus “bordes ásperos”, y hay que armarse de paciencia para conseguir algún resultado.

Un ejemplo de algo que se necesita en Java, pero no he visto que sea necesario en .NET, es el utilitario Maven. Van a tener que aprenderlo. Pero es un ejemplo de algo que a muchos en el ambiente Java les fascina, pero yo lo veo de otra manera: es una gran herramienta para solucionar un problema, que no DEBERIA ser un problema tan grande. A mucha gente le gusta, porque soluciona la complejidad de tantas librerías con tants versiones: pero es un remedio que oculta el problema inicial, la complejidad de esa tarea. Si tienen que estudiar Maven, recomiendo antes estudiar algo más simple, ubicuo, entendible y poderoso: el Apache Ant.

Cuando dominen JDBC para acceder a base de datos, pueden comenzar a estudiar y usar Hibernate. Parece que no terminan nunca los temas ;-). Bueno, uno más: para tener un panorama de los problemas y soluciones al desarrollo de Java, recomiendo estudiar (lo simple primero, luego sólo los temas que necesitan) el Spring Framework, donde encontrarán también nuevos utilitarios que se montaron para facilitar el desarrollo en Java. Al estudiar Spring, verán distintas implementaciones de interfaz web, como usar MVC (Model-View-Controller), sus distintas implementaciones, y otras tecnologías como JavaServer Faces.

El mejor libro para ir entendiendo cómo todo eso se usa en conjunto, lo comento en mi post:

Agile Java Development With Spring, Hibernate And Eclipse

Quiero aprovechar este post para agregar algunos puntos que van más allá de la programación en Java. Primero, Uds. no tienen que ser “programadores Java”. Sólo al principio. Uds. tienen que, diría, luchar, esforzarse por ser “desarrolladores de software”, es más, “desarrolladores de software profesionales”: gente que se toma en serio el trabajo que realiza, y trata cada mes, semana, día, de aprender algo nuevo y mejorar en las prácticas que realiza. Tiene que pensar más allá de “programar en Java”. Tienen que pensar en “desarrollar software”. Ya no hay programadores de un lenguaje. Para ver algunos temas casi imprescindibles en nuestra profesión, les recomiendo el libro de @karlseguin: Foundations of Programming. Para los que estudian Java, encontrarán que tiene más orientación a .NET, pero no se engañen: todo lo de ese libro se puede aplicar a otras tecnologías. Traten también de ser polyglot programmers, cosa que si aprenden Java, conseguirán estudiando los lenguajes y tecnologías montadas sobre la JVM, como Scala, Groovy, Clojure, JRuby, y más.

Me recomiendo a mí mismo ;-) leer:

Aprendiendo de Jason Bourne
Programmer Competency Matrix
What is your strategy for become a better developer

Si son seguidores de esto blog (es decir, yo y mi Tía Carlota ;-) ya saben lo que viene ahora:

- Estudien y apliquen Test-Driven Development (TDD, leer mis posts sobre el tema)

- Trabajen en équipos ágiles (leer mis posts sobre Scrum)

Van a aprender que la complejidad de Java (todos lo que hay que tener en cuenta para armar una aplicación profesional) fue en gran parte madre de mi iniciativa sobre generación de código.

Si están en Buenos Aires, o en Argentina, hay varias actividades ágiles, ver actividades en Latinoamérica en:

http://www.agiles.org/

TDD es prácticamente un “must be done”. Pueden estudiar TDD en curso presencial, acá en Buenos Aires, con el bueno de @hernanwilkinson:

http://www.10pines.com/content/construcci%C3%B3n-de-software-robusto-con-tdd

Finalmente, les recuerdo que voy coleccionando enlaces sobre Java en:

http://delicious.com/ajlopez/java+tutorial
http://delicious.com/ajlopez/java+example

Vean que de cada tema del universo, tengo enlaces en delicious ;-) ;-)

Bueno, ya es tiempo de cerrar este post, e irme a luchar contra el Efecto Coto. Cualquier duda, recomendación, les pido colaboren con comentarios. Pero más allá de todo esto, de Java y demás, siempre recuerden, parafraseando a Rambo II: “La mejor herramienta es una mente despierta” ;-)

Bueno, eso es todo por hoy, salud, mis queridos chichipíos, vermú con papa fritas, y gud shou!!

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Wed, May 11 2011 12:24 by lopez | 3 comment(s)

NHibernate 3 (Parte 5) Primer mapeo Uno-a-Varios

Anterior post
Próximo post

Esta vez, tengo este modelo de datos para mapear a clases:

Cada Book (libro) tiene 0, 1  o más Chapter (Capítulos). Este es el diagrama de clases de mi dominio:

 

El código para Book.cs:

public class Book { public virtual Guid Id { get; set; } public virtual string Title { get; set; } public virtual string Author { get; set; } public virtual IList<Chapter> Chapters { get; set; } }



Como antes, las propiedades son virtuales (tengo que escribir por qué son virtuales, en un post de esta serie). Y noten, Chapter es un  IList<Chapter>. El IList será armado por NHibernate. El Chapter.cs:

public class Chapter { public virtual Guid Id { get; set; } public virtual string Title { get; set; } public virtual string Notes { get; set; } }



El mapeo de Book tiene un nuevo elemento:

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Books" namespace="Books"> <class name="Book" table="Books"> <id name="Id"> <generator class="guid.comb" /> </id> <property name="Title" not-null="true" /> <property name="Author" not-null="true"/> <list name="Chapters" cascade="all-delete-orphan"> <key column="BookId"/> <index column="ChapterIndex"/> <one-to-many class="Chapter"/> </list> </class> </hibernate-mapping>



El elemento <list> apunta a la propiedad de Book llamada Chapters (la IList). Los elementos serán ordenados basado en la columna de table ChapterIndex. BookId y ChapterIndex no son propiedades de la clase Chapter. Son columnas en la tabla Chapter. El mapeo de la clase Chapter no tiene ninguna referencia a Book:

<?xml version="1.0" encoding="utf-8" ?> <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Books" namespace="Books"> <class name="Chapter" table="Chapters"> <id name="Id"> <generator class="guid.comb" /> </id> <property name="Title" not-null="true" /> <property name="Notes" /> </class> </hibernate-mapping>



 

¿Qué es eso de “delete-all-orphan”? Vean

http://www.nhforge.org/doc/nh/en/index.html#collections-onetomany

El programador puede agregar y remover capítulos de un objeto libro, y entonces, NHibernate se encargará de su persistencia. Si un objeto se remueve de la lista por código, NHibernate eliminará el capítulo removido SI no ha sido agregado mientras tanto a OTRO libro (es decir, quedó como objeto, pero huérfano).

Este es el código para agregar y recuperar un libro con capítulos:

ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory(); using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tx = session.BeginTransaction()) { Book cookbook = new Book() { Title = "NHibernate Cookbook", Author = "Jason Dentler", Chapters = new List<Chapter>() { new Chapter() { Title = "Models and Mappings" }, new Chapter() { Title = "Configuration and Schema" }, new Chapter() { Title = "Sessions and Transactions" } } }; session.Save(cookbook); foreach (Book book in session.Query<Book>()) { System.Console.WriteLine(string.Format("Book {0}", book.Title)); int nchapter = 0; foreach (Chapter chapter in book.Chapters) System.Console.WriteLine(string.Format("Chapter {0}:{1}", ++nchapter, chapter.Title)); } tx.Commit(); session.Close(); } }



Tuve un problema al escribir el ejemplo: inicialmente había definido ChapterIndex y BookId como columnas que no admitían null, en la tabla Chapters. Pero, notablemente, NHibernate requiere que esos campos puedan aceptar valores null. Así que en mis primeras pruebas, me daba un error al grabar un nuevo libro con capítulos. Ahí me di cuenta de esta nota (en la documentación de NHForge, en el enlace que puse más arriba):

Very Important Note: If the <key> column of a <one-to-many> association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". See the discussion of bidirectional associations later in this chapter.

Cambié las columnas a nullable, y todo funcionó. Me asombra este requirimiento: yo esperaba que NHibernate llene las columnas BookId y ChapterIndex DURANTE la grabación de cada capítulo. Pero nones. Graba primero el capítulo con los campos en null, y luego los actualiza.

El código de este post, como es usual, está en mi AjCodeKatas Google Project, en trunk/NHibernate/BooksOneToMany. Pueden bajar una versión “actual congelada” desde NHibernate3BooksOneToMany.zip.

Próximos pasos: explorar otras opciones uno-a-varios, mapeos bidireccionales, y más.

Fuentes consultadas: Jason Dentler’s NHibernate 3.0 Cookbook
http://ayende.com/Blog/archive/2006/12/02/nhibernatecascadesthedifferentbetweenallalldeleteorphansandsaveupdate.aspx
http://www.nhforge.org/doc/nh/en/index.html#collections-onetomany

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Posted Tue, May 10 2011 12:36 by lopez | 1 comment(s)

Filed under: , ,