Objetos, Tablas Relacionales y Desajuste por Impedancia (Impedance Mismatch)


"La Persistencia de la Memoria" (1931), óleo del pintor catalán Salvador Dalí

 Uno  de los primeros desafíos que tuvo que enfrentar la programación orientada a objetos en el mundo empresarial estuvo relacionado con la persistencia

En efecto, las organizaciones venían de adoptar masivamente Sistemas de Administración de Bases de Datos Relacionales (Relational Database Manager Systems o RDBMS), que eran efectivamente útiles para recuperar y hacer análisis de información. Algo así como un protozoo de lo que después iba a tomar la forma de Inteligencia de Negocios (Business Intelligence o BI). Las bases de datos relacionales están pensadas para cosechar resultados basados en volúmenes de información sembrada a lo largo del tiempo (tomar nota de este detalle, por favor)

En paralelo, desde finales de los ’80 el mundo empresarial comenzó una lenta pero persistente adopción del paradigma de Programación Orientado a Objetos (Object-Oriented Programming u OOP) que implicó un beneficio en términos de modularización, reducción de complejidad, reusabilidad de lógica y otras características, las que a su vez dieron paso a la Programación Orientada a Componentes (no importa: otro día hablamos más de esto último). Lo importante que quiero destacar de los objetos, es que están especialmente pensados para modelar un momento específico de la ejecución: el "Ahora"
Esto es: los objetos tienen estado. No me refiero a que tengan una propiedad llamada Estado. Digo que si a un objeto le cambiamos el valor de cualquiera de sus propiedades, pasa de un estado inicial a un estado final. En otras palabras, en un momento dado del tiempo, el objeto está siempre en un único estado. Se entendió?

Entonces, antes de seguir, tengamos claro que los objetos en memoria representan un instante dado del modelo de datos en el tiempo ("una foto", por así decir), mientras que las bases de datos relacionales están habilitadas para conservar varios estados de ese modelo a lo largo del tiempo (permiten guardar "la película")

Aunque más de un arquitecto crée que los objetos son una evolución de las tablas relacionales (quizás inducidos por el hecho de que se hicieron conocidos después), en absoluto se puede afirmar que alguno de los dos paradigmas pueda remotamente sustituir al otro

Están pensados con fines distintos: los objetos modelan el estado del negocio durante la ejecución de procesos transaccionales (On Line Transaction Processing u OLTP), en tanto que las tablas de una base de datos relacional modelan los distintos estados del negocio a lo largo del tiempo con fines analíticos (On Line Analitical Processing u OLAP)

Entendida esta diferencia, queda claro que el mundo es lo suficientemente grande para contenerlos a los dos paradigmas, y así pareciera ser que la convivencia entre ambos va a ser más sencilla. Pero no

El problema es que, okay, tengo que ejecutar una transacción de negocio sobre un objeto Cliente, entonces recupero su último estado de la base de datos, le ejecuto su transacción en memoria, y vuelvo a persistirlo (asegurandome de poder, a futuro, recuperar el estado correspondiente según su período de vigencia)

Lo complicado de esto que parece tan trivial y obvio es la forma en que ambos paradigmas modelan los datos

Para ilustrarlo mejor, voy a tomar como ejemplo un caso real, correspondiente a una compañía de telefonía móvil en la que trabajé en la segunda mitad de los ’90. Esta compañía tenía

  • Clientes, que eran los destinatarios de las facturas de Servicios, aunque un Cliente podía a su vez tener varias
  • Cuentas. Éstas normalmente estaban asociadas con un número de teléfono móvil, reflejando el hecho de que un Cliente, por ejemplo una organización, podía tener varias Cuentas: una para cada ejecutivo, etc. A su vez, las Cuentas podían tener contratados varios
  • Servicios. Ejemplo, una de las dos Cuentas de un Cliente podía poder efectuar llamadas de larga distancia en tanto que la otra no; aunque a su vez esta última podría tener bloqueadas las llamadas entrantes. En ese ejemplo, Larga Distancia y Bloqueo de Llamadas Entrantes serían dos instancias posibles de Servicio. Eventualmente una Cuenta podría comprar
  • Bienes entendiendo que, al igual que los Servicios, ambos tienen un costo asociado. Pero a diferencia de estos últimos, los Bienes se facturan una única vez

Un posible, seguramente no el único, diagrama de objetos que modele las entidades de negocio que conté hasta recién podría ser el siguiente

Figura 1 – Modelo de Entidades de Negocio en su versión Orientada a Objetos

El modelo de negocio que describí está extremadamente simplificado: no considera diferentes mecanismos de facturación. Por ejemplo, que un bien se pueda facturar en más de un pago, o que un servicio acepte indistintamente pagos mensuales, bimestrales, etc. Lo cierto es que no quiero modelar estrictamente a la compañía en la que me inspiré, sino que quiero hacer un ejemplo acotado, sencillo y rapidamente entendible. Veamos

  • Un Cliente tiene una propiedad de tipo IList<Cuenta> -estoy usando sintaxis de tipos de datos genéricos (Generics) de .NET. Sólo para el curioso, hay un artículo de Juval Löwy introductorio haciendo click aquí-. La idea de esta propiedad es que cada instancia de Cliente exponga las Cuentas que, en ese momento de la ejecución, el Cliente tenga activas
  • Una Cuenta pertenece, en un dado momento, sólo a un Cliente. Esto lo refleja la propiedad Cuenta.Cliente
  • Cada Cuenta tiene disponibles una ICollection<Facturable>, de elementos que indistintamente son Bienes o Servicios y los modelo como piezas separadas porque la conducta y propiedades de ambos es diferente, aunque ya no incluí esas diferencias en el modelo
  • Aún así, los Bienes y Servicios, como ítems Facturables que son, heredan de éste la propiedad Monto

Hasta acá venimos bien. Ahora veamos el esquema de persistencia de este modelo de datos

Figura 2 – Modelo de Entidades de Negocio en su versión Relacional

Desde el primer vistazo ya se ven gruesas diferencias con el modelo anterior. Repasémoslas

  • Navegación. El objeto Cliente tenía una propiedad que era una lista de Cuentas. Sin embargo la tabla Cliente no tiene una columna equivalente, por lo que para poder recuperar esa propiedad, habrá que pedirsela al RDBMS con una sentencia del estilo

    SELECT …
    FROM   Cuenta
    WHERE  Cuenta.IdCliente =
    <el cliente en cuestión>
     

  • Cardinalidad. También, en el plano de objetos, la relación Cuenta-Facturable era m:n (o familiarmente hablando, "muchos a muchos"). Pero como uno de los requerimientos, a nivel analítico, es poder almacenar los distintos estados del modelo, existe la necesidad de persistir la vigencia de los vínculos entre Cuentas e ítems Facturables. Por esta razón, surge en el modelo de tablas lo que se conoce como entidad asociativa, que tiene como finalidad caracterizar, en sus columnas, un vínculo generalmente de cardinalidad m:n aunque es factible verla también en vínculos 1:n o incluso 1:1
    En este caso, las columnas que caracterizan la relación son FechaDesde y FechaHasta
    Se me puede hacer la observación, definitivamente válida, de que la relación Cliente-Cuenta también tiene una duración definida en el tiempo (por ejemplo, un Cliente puede empezar con una Cuenta, al año adquirir tres nuevas y finalmente quedar con sólo dos). Si lo incluía en el diagrama se hacía más complejo de explicar pero sí es válida la observación (y así efectivamente ocurría en el negocio)
  • Vigencia de la Propiedad. Un caso curioso se da con la propiedad, en el mundo de los objetos, Facturable.Monto: los precios de los Bienes y Servicios pueden variar a lo largo del tiempo. Sin embargo, puede ser necesario recuperar el precio vigente de un ítem Facturable a una fecha histórica (por ejemplo para rehacer una factura). Por esta razón tenemos que agregar nuevamente una entidad asociativa, MontoVigencia, aunque esta vez no caracteriza la relación de dos tablas sino de una tabla con una de sus propiedades
    Otros ejemplos típicos de esta situación es la tabla histórica de Clientes, que sirve para persistir, por ejemplo, domicilios viejos de facturación, u otras propiedades que puedan variar y se necesite conservar su vigencia. Lo escribo y me viene a la memoria esas situaciones en las que había que recuperar un Cliente a una fecha histórica, con sus Cuentas vigentes en aquel entonces, y las mismas con los ítems Facturables también presentes en esa fecha histórica. Todo para regenerar una factura histórica que, sin embargo, se le debía enviar a… su domicilio actual. Prefiero ni acordarme… :’-o
  • Herencia. La última disrupción que voy a destacar -aunque quizás haya otras- es la imposibilidad de reflejar, en tablas relacionales, relaciones de herencia. En el modelo de objetos se dice que la relación entre Facturable y Bien, por ejemplo, es de herencia: un Bien hereda los miembros de Facturable
    Por otro lado, a relación entre Cliente y Cuenta es de asociación. Asociación del tipo composición: un Cliente se compone de Cuentas en el sentido de que las Cuentas son parte del Cliente. Si el Cliente queda inactivo, las Cuentas también
    En cambio, la relación entre Cuentas e ítems Facturables si bien es de asociación, es del tipo agregación: una Cuenta agrega ítems Facturables (esto es: no se compone de ellos) en el sentido que si la Cuenta se da de baja, los ítems Facturables siguen vivos en las otras Cuenta que los agregan
    Tenemos entonces, en el mundo de objetos, tres tipos de relación: herencia, composición y agregación
    En cambio, en el mundo relacional… todo da lo mismo!! Es cierto que con gatillos (triggers) se puede simular la conducta de la composición (la baja del Cliente gatilla la baja de las Cuentas). Pero la herencia no se puede marcar de ningún modo. Un registro de la tabla Bien no tiene Monto. Para poder recuperar un objeto Bien ("bien" completo) hace falta relacionar tablas con un comando SQL de la pinta

    SELECT *
    FROM Bien, Facturable, MontoFacturable
    WHERE
          Bien.IdFacturable = Facturable.Id AND
          MontoFacturable.IdFacturable = Facturable.Id AND
          MontoFacturable.FechaDesde <=
    <fecha> AND
          MontoFacturable.FechaHasta >= <fecha>

    El no poder modelar herencia tiene otro efecto secundario: en mi modelo de objetos debería marcar a Facturable como clase abstracta de manera tal que sólo puedan generarse instancias de sus herederos Bien y Servicio, mas no de Facturable. Con esto logramos que cualquier instancia de tipo Facturable sea en realidad, o bien un Bien, o bien un Servicio (por herencia, al ser instancias de esas dos clases, son a la vez instancias de Facturable; lo que no pueden es ser instancia de Facturable sin ser a la vez instancia de alguno de sus herederos). En cambio, en el mundo de las tablas relacionales, puedo perfectamente tener una registro de Facturable sin que exista ni Bien ni Servicio con el mismo identificador. O también puedo tener, para un mismo registro Facturable, sendos registros Bien y Servicio con el mismo identificador. En el modelo de la Fig. 2, lo que hicimos fue poner el flag Facturable.EsServicio para poder determinar lógicamente en cuál de las dos tablas asociadas está el resto de los datos. Pero como decía, de ahí a que efectivamente esté… las tablas relacionales no garantizan nada
    En definitiva, no le busquemos la vuelta porque no tiene: las tablas relacionales no modelan herencia y se acabó la discusión

En los puntos que acabo de destacar, he mostrado que ambos paradigmas tienen características propias que no se encuentran en el otro, o que son muy difíciles de imitar. En Física existe un término para denotar la existencia de limitaciones al transmitir una onda de un medio a otro (por ejemplo, porque en el nuevo medio no es posible conservar la misma frecuencia). El término es Impedancia, que en latín significa "imposibilidad de caminar (de avanzar)". Pues bien, la literatura informática ha englobado esas limitaciones para convertir objetos en registros y viceversa como Desajuste por Impedancia (o Impedance Mismatch)

A este desajuste no queda alternativa que asumirlo. No ha faltado el arquitecto o desarrollador arrogante que ha dicho "las bases de datos relacionales van a morir: se vienen las bases de datos orientadas a objetos"
Si realmente existiera algo así quizás sería grandioso. Pero lo cierto es dos cosas

  1. Las bases de datos han estado por años, llevan más de dos décadas de inversión, investigación, innovación… Hoy hay toda una industria alrededor de conceptos como Business Intelligence, Minado de Datos (Data Mining), Centros Comerciales de Datos (Data Marts), … Créanme: no se viene ninguna base de datos orientada a objetos. Si no me créen pregúntenle a Oracle, a IBM, a Microsoft… incluso a MySql, cuándo tienen pensado sacar a la calle su base de datos orientada a objetos
  2. Pero sobre todo, lo que es principal, es que las tablas relacionales… son otra cosa!!! Sirven para otra cosa!!! No es que los objetos las superen: son paradigmas complementarios!! Ambos hacen falta!! Se necesitan mutuamente!!

El asunto entonces es cómo atenuar los efectos del Desajuste por Impedancia. Para esto hay una serie de técnicas, y a la vez de prácticas. Repasemos las técnicas primero

  • Objetos de Acceso a Datos (Data Access Objects o DAOs). En la arquitectura de aplicación, todo el modelo de entidades de negocio queda en objetos que se instancian y se ejecutan en una capa lógica (y quizás física) conocida como Capa de Negocio, ya que las reglas del negocio quedan plasmadas en la conducta de los objetos de esta capa
    Ahora bien, en lugar de incluir acá mismo la lógica CRUD (por Create, Retrieve, Update and Delete, o Crear, Recuperar, Actualizar y Suprimir registros) ponemos una Capa de Acceso a Datos cuyos "habitantes" van a ser objetos que implementen CRUD. A estos objetos se los conoce como Objetos de Acceso a Datos, o más comunmente DAOs
    Es una buena práctica aplicar la técnica de DAOs porque desacoplamos la lógica de lidiar con el Desajuste por Impedancia de la lógica funcional de la aplicación. Evitamos así el llamado código spaghetti, y en el fondo no estamos haciendo otra cosa que aplicar correctamente el paradigma de objetos
    Las técnicas que siguen son suplementarias a ésta, quiero decir, no son alternativas sino que son formas rápidas de implementar DAOs sin escribir o escribiendo muy poquitas líneas de código
  • Frameworks de Persistencia (Persistence Frameworks). Esta alternativa se basa en la filosofía del denominador común: si con tablas se pueden hacer menos cosas que con objetos, entonces que el modelo de entidades de negocio se realice primero en el esquema de tablas, y luego creamos objetos-entidades como copias exactas de las mismas. Lógico, si esto lo lée un smalltalkero de la primera hora, yo creo que le salta a la yugular al que diga que le gusta esta alternativa (en ese caso, mejor que no se entere que esta alternativa es la que más me gusta a mí: más incluso que la de O/R-M que voy a comentar a continuación). Sin embargo no es tan descabellado: la lógica de negocio se deja mayormente en clases que modelan procesos, no en las entidades
    A ver si con un ejemplo lo explico mejor: en lugar de que el objeto Cuenta tenga un método Cuenta.Facturar() que tome sus Servicios vigentes y sume sus Montos; en cambio tenemos un objeto Facturador con un método Facturador.Facturar(Cuenta). Este método recibe una Cuenta a la que le pide sus Servicios y suma los Montos de todos ellos
    Nada se crea, nada se pierde, todo se transforma. La clase Cuenta pasa a ser menos activa, más pasiva. Las clases activas son las que modelan procesos de negocio. Las pasivas, las que modelan entidades de negocio
    Los frameworks de persistencia normalmente nos evitan crear estas clases pasivas (las entidades): las generan ellos mismos mediante introspección en los esquemas de las tablas. Nosotros como desarrolladores sólo usamos esas clases creadas automáticamente desde las clases-procesos (que sí tenemos que construir). La lógica CRUD, también la aporta el framework de persistencia
    Un ejemplo típico de framework de persistencia es ADO.NET. La clase DataSet contiene una representación en objetos de los registros de aquellas tablas que, visualmente con el IDE de Microsoft, le indicamos. No sólo las tablas sino también sus relaciones
    Los frameworks de persistencia más avanzados, ADO.NET entre ellos, detectan cuáles objetos de los que recuperaron en la lectura, han sufrido modificaciones (sea, propiedades cambiadas, u objetos suprimidos, creados, etc). A estos objetos modificados, Fowler los llama "objetos sucios" ("dirty objects"). Los frameworks son capaces de sincronizar la base de datos con el nuevo estado del modelo, invocando los métodos CRUD necesarios
  • Mapeadores Objeto/Relacionales (Object/Relational Mappers u O/R-M). Esta alternativa es la más seductora. Mediante mecanismos, ya sea un archivo XML, atributos en el código de las clases y propiedades, o de alguna otra forma, se mapean clases en tablas, propiedades en columnas, asociaciones en relaciones, etc
    Como resultado, a lo largo de toda nuestra aplicación, los objetos que modelan entidades de negocio tienen lógica dentro, etc. Son objetos absolutamente planos (POJOs, por Plain Old Java Objects, o POCOs, por Plain Old CLR Objects). Los mapeadores O/R suelen tener una API con alguna clase principal a la que se le dice qué tipo de objetos se necesitan. La consulta no se le expresa en SQL sino en un dialecto basado en los POJOs/POCOs. Esta clase principal se encarga de generar las sentencias SQL necesarias para recuperar lo que pedimos, y luego mover la información de las columnas a las propiedades (proceso que se conoce como hidratación). Analogamente, una vez que procesamos esos objetos (llámese, modificarlos, borrar algunos, crear otros, etc) se los damos al mapeador O/R y él se encarga de generar los comandos que reflejen el nuevo estado en la base de datos
    Suena muy sexy pero es peligrosa:
    • Instancia objetos por cada registro recuperado, y eso no sólo puede degradar la ejecución en colecciones muy grandes, sino que a veces es absolutamente innecesario. Por ejemplo, supongamos que queremos poblar una lista pop-up con nombres de ciudades (y el código de cada una, para que seleccionen por la ciudad y nos quedemos con su código)
      La lista pop-up tiene una estructura que es tabular (matricial). Igual que la tabla de ciudades
      Sin embargo, el mapeador hidrata una serie de objetos Ciudad, poblándole sus propiedades Ciudad.Codigo y Ciudad.Descripcion, pasa luego esta serie al control de interfaz de usuario de la lista pop-up… para que acceda a esas propiedades y llene su contenido
      En suma, de una matriz bidimensional -la tabla relacional- pasamos a objetos -la clase Ciudad-… para terminar pasándolos de nuevo a una matriz bidimensional -el control de interfaz de usuario-. Como almacenamiento intermedio, la colección de objetos es un poco cara en términos de ejecución. Lo que ahorramos de tiempo codificando lo pagaremos a perpetuidad en ejecución
      Algunos de estos mapeadores, no obstante, trabajan con mecanismos de caching de objetos, pero yo recomiendo hacer una buena prueba de stress para sacarse la duda de su efectividad
    • Al definir mapeos de asociaciones en relaciones, el mapeador luego es capaz de traernos un Cliente ya con todas sus Cuentas hidratadas… automáticamente!!! El comando lo arma él. Esto es tan fabuloso como peligroso: si una Cuenta a su vez está asociada con otros objetos y mapeamos esa asociación en relaciones, qué va a pasar cada vez que necesitemos un Cliente? Se va a instanciar todo el grafo. Esto puede ser carísimamente peligroso
      Concientes de esto, los mapeadores O/R incorporan el concepto de carga perezoza (lazy loading). Si una asociación se marca como "carga perezoza", el mapeador no va a cargar esos registros sino hasta tanto necesitarlos
      De vuelta una virtud y un defecto a la vez: decidir qué relaciones se van a cargar activamente (eager loading) y cuales perezozamente, en modelos de datos grandes, lleva tanto tiempo que desarrollar la capa de DAOs a mano puede resultar no tan malo después de todo. Lo que quiero decir con todo esto es que, si bien los mapeadores O/R provéen una solución al Desajuste por Impedancia, introducen a la vez una gama de problemas nuevos a resolver nada triviales (al menos, el Desajuste por Impedancia era un problema conocido)
      La plataforma J2EE tiene una oferta muy rica y variada de mapeadores. Uno de ellos, Hibernate, goza de muy buena reputación y hay varios libros escritos sobre él
      La plataforma .NET tiene menos frameworks O/R-M disponibles, y esto es visto por algunos como una debilidad. Mi opinión al respecto es que J2EE tenía como alternativa los Enterprise JavaBeans (EJB) que eran y son una tecnología bastante compleja. Si no, una API llamada Java Data Objects (JDO) que se esperaba que fuera el Hibernate oficial, pero la audiencia le dio la espalda. Todo esto ayudó a que los desarrolladores consideren seriamente la necesidad de contar con un mapeador O/R
      En el caso .NET, si bien existe NHibernate, una migración de Hibernate a .NET, yo personalmente desaconsejo su uso teniendo disponible el framework de persistencia ADO.NET
      Podría seguir extendiendome, pero como no voy en contra de O/R-M sino que sólo quiero conscientizar de que es más compleja de usar y ejecutar de lo que aparenta
      No obstante prometo postear más sobre O/R-M porque tengo experiencia práctica que compartir

 Amén de las técnicas que destaqué, hay prácticas que también permiten mejorar el Desajuste por Impedancia

  • Entender claramente dónde las bases de datos son más hábiles que los objetos y viceversa. Y sacar el máximo provecho de eso. Por ejemplo, si tenemos una lógica de datos muy intensiva, la primera alternativa a considerar es codificar esa lógica en la forma de procedimientos almacenados (stored procedures) directamente en la base de datos
    A este respecto hay un debate, porque si esa lógica de datos está relacionada con actividades de negocio, lo que todos esperan que pase es que se ejecute en la capa de negocio, cambiándole el estado a los objetos en memoria, y luego que el nuevo estado del modelo en memoria se "baje" a la base de datos
    Personalmente, hace unos años atrás yo abonaba a esta última alternativa. Hoy soy un convencido de que la primera me gustá más (me estoy volviendo viejo, parece)
    Fuera de broma, lo que quiero decir es que hoy flexibilicé mis posturas. No soy tan observante de su la lógica es de Negocio o no, como lo soy de si es lógica de cómputo intensivo o de acceso intensivo a datos
    • Si es de cómputo intensivo, yo lo pongo en la Capa de Negocio
    • Si es de acceso intensivo a los datos, lo dejo en un Procedimiento Almacenado. Sí, aunque sea una transacción de negocio: acaso en la capa de presentación no solemos poner, por ejemplo en JavaScript, que "si la edad es menor a 18 años no se puede realizar la operación"? Acaso esa validación no la impone el negocio? Sin embargo en esa capa solemos hacerlo. Por qué? Para evitar un round trip al back-end, donde seguramente esa lógica también está plasmada. Pero quién tiene ganas de enfrentar al gerente de Operaciones cuando se queje de que la aplicación es tan lenta que se arman unas colas impresionantes, los clientes se cansan y se van, los empleados están estresados de trabajar tantas horas extras, etc
      Bueno, yo aplico esa salvaguarda, siempre que en términos de rendimiento se justifique, metiendo lógica de negocio de acceso intensivo a datos dentro de procedimientos almacenados
  • La segunda práctica que recomiendo, esto es muy personal y cada uno sabrá, es favorecer el modelado data-driven (dirigido por los datos, las tablas relacionales) por sobre el object-driven (dirigido por los objetos)
    Es decir, mientras se esté analizando el negocio, comenzar por modelar primero las tablas en las cuales la información de la aplicación deba estar disponible, para luego extraer de ellas, por ejemplo mediante un framework de persistencia, las clases-entidades y sus métodos CRUD
    Esto es materia opinable. Al menos a mí esta alternativa es la que más me gusta. En cualquier caso, probá las dos y después optá por Frameworks de Persistencia o por Mapeadores Objeto/Relacionales según los resultados que te dió cada uno

Como le dí un poquito duro a O/R-M, me despido prometiendo pronto un artículo enteramente dedicado a estos mapeadores, sus buenas prácticas y qué considerar al elegir entre la variada oferta de O/R-M que hay (Agregado el 2 de Abril de 2007: excepto lo de "qué considerar al elegir entre la variada oferta de O/R-M que hay", del resto me ocupé a fines de Marzo 2007 en el post "Mapeo de Objetos y Tablas Relacionales (O/R-M). Lo Que a Mí me Sirvió")

En el durante dejo algunas referencias interesantes para el que quiera leer más

Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

6 respuestas a Objetos, Tablas Relacionales y Desajuste por Impedancia (Impedance Mismatch)

  1. Robert dijo:

    Hola diegum,
    en nuestro proyecto Java EE estamos usando la Java Persistence API (JPA) como framework orm, y estamos esperando con ansias el articulo sobre O/R-M que prometiste….

    Saludos

    Robert David

  2. Diego dijo:

    Prometido que llegará, Robert
     
    Me debo a mi público

  3. rd.acardenas dijo:

    Entonces seguiremos esperando con mucha expectativa. En estos momentos nos encontramos en la fase de análisis y de paso estamos aprovechando para capacitarnos en JSF, EJB3 y JPA…

    Mi equipo está muy eufórico y la verdad a mi me tiene un poco preocupado tanto optimismo… =S, no conozco gente que desarrolle con Java EE 5 así que nos podemos ir olvidando de los refuerzos… y en mayo se termina el contrato, creo que pediremos ampliación… =(

    Saludos Diegum

  4. Diego dijo:

    Voilá: "Mapeo de Objetos y Tablas Relacionales (O/R-M). Lo Que a Mí me Sirvió"

  5. Silvana dijo:

    Diegun la verdad que quiero felicitarte por el post… la claridad conque planteaste los conceptos es unica … espero poder leer tu articulo de O/R-M😉.

  6. Diego dijo:

    Muy amable, Silvana!!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s