Unofficial Content
  • This documentation is valid for:

English version 
 


Introducción

Es notorio el proceso de conversión de aplicaciones de AS nativo (RPG o Cobol) a ambientes como .NET o Java.

En este sentido en GeneXus existen funcionalidades para permitir la convivencia de programas Java/NET y RPG/Cobol dentro de la misma KB e incluso el mismo modelo más info.

De este modo se puede tener, en un mismo modelo, algunos objetos generados para una plataforma (RPG/cobol) y otros para otra (Java/NET), incluso algunos objetos generados para ambas plataformas.

(NOTA: En adelante mencionaremos el caso de Java y RPG, dado que fue el caso de estudio que motivó esta solución, pero aplica lo mismo a Cobol y NET)

La base de datos: el punto en común

Si bien existe interoperabilidad y un objeto Java puede llamar a un objeto RPG, la integración más fuerte o el punto de confluencia es la base de datos, la cual es única para ambas plataformas. Así si se tiene un modelo con los generadores RPG y Java, la base de datos será en el iSeries (DB2 Iseries) y tanto los programas RPG como Java accederán a la misma.

La particularidad que se da en este caso es que RPG define la base de datos como DDS compiladas y Java como SQL Collection. Si bien Java puede acceder a la base de datos creada con RPG no se cumple la inversa.
Como consecuencia, en el caso de tener un modelo con más de un ambiente y ser uno de ellos RPG, este debe ser el generador utilizado para crear/reorganizar la base de datos.

La limitación del esquema

Eso nos lleva a que pueden existir funcionalidades en Java que requieran alguna particularidad de la base de datos que RPG no soporta. Dichas funcionalidades deberán ser resueltas por otro camino.

El caso particular a analizar en este documento es el uso de atributos blob. Este tipo de atributos no es soportado en RPG y al intentar crear/reorganizar una base de datos incluyendo este tipo de atributos aparecerá el mensaje: "Attribute data type (BLOB) is not supported on the selected environment (DBMS/Generator)" impidiendo la creación/reorganización de la base de datos.

Dado que no hay planes de implementar el soporte del tipo de datos BLOBs en el generador RPG, se detalla a continuación un posible camino para resolver la necesidad.

La metáfora

Si bien el caso puede ser genérico (el blob puede ser usado para muchos fines y en variados escenarios), se describe un ejemplo mínimo para la mejor compresión de la solución. A partir de ese ejemplo o metáfora, se puede generalizar a otros casos similares.

En este caso el enunciado es:

"Se tiene un sistema de RRHH que maneja información de los empleados de una compañía. El mismo se encuentra en producción con RPG y se desean desarrollar algunos módulos en Java. Esos módulos requieren manejar la foto del empleado como parte de su legajo/ficha. "

El análisis del problema a resolver

Si se pudiera migrar toda la aplicación a Java entonces el problema no existiría puesto que no se requiere "convivencia" de ambos generadores.
El problema es que se requieren algunos objetos (liquidación de haberes, etc.) en RPG y otros (manejo de la ficha del funcionario, etc.) en Java.

La situación implica:
  • para manejar la "foto del empleado" es necesario el tipo de datos blob.
  • el generador RPG no soporta el tipo de datos Blob, no solo como generador en si, sino que tampoco puede crear/reorganizar tablas que los contengan.
  • no es posible tener un modelo con RPG y Java siendo Java el generador que maneje (cree/reorganice) la base de datos. Debe ser RPG el que lo haga.

Parece un problema sin solución: se precisa reorganizar la base de datos con RPG porque se usan programas RPG, se precisa un BLOB pero RPG no los puede crear... entonces...confused

Ideas sobre posibles soluciones

Hay varias soluciones posibles, una primaria puede ser manejar los blobs fuera de la base de datos, como se hacia antes de que se soportara el tipo de datos blob.
Es decir, en un file server se mantiene esa información y en la base de datos se mantiene una referencia al archivo (básicamente el path) en cada registro y cuando se precisa se utiliza la función LoadBitmap().
Esta solución puede servir, de todos modos por un tema administrativo, de seguridad, de integridad transaccional, etc puede ser mejor manejar todo en una base de datos y no requerir un file server aparte.

Ahora, analizando mejor el problema el mismo se puede reducir a "preciso agregar el atributo blob a la tabla y que tanto programas RPG como Java accedan a la misma".

No es posible frown o al menos aparecen algunos efectos secundarios como que los programas RPG no compilarían o si lo hacen no sabrían como manejar ese atributo. Incluso ¿como se haría? ¿Modificando la DDS a mano? bueno, no solo hay que saber hacerlo hay que recordar hacerlo cuando se ejecuten reorganizaciones que puedan afectar esa tabla.
En cualquier caso parece riesgoso y costoso.

Sin embargo, si es posible crear una "paralela" a la tabla de "Empleados" que incluya ese atributo y de algún modo que los programas RPG solo usen la de "Empleados" y los programas Java usen la que requieran usar (la de Empleados si no requieren el BLOB y la "paralela" si se requiere el BLOB).

En definitiva es "extender" la tabla de Empleados en otra tabla en lugar de agregarle atributos.

Resumen de la solución

Entonces, una solución es definir otra base de conocimientos ("secondary kb") que sea solo en Java y que sea la encargada de crear/reorganizar la tabla que incluya el atributo blob.
Luego en la KB original ("main kb") se "apunta" a dicha tabla con un Data View y se modifica la tabla de empleados para agregar una "foreing key" a esa tabla (EmpImgId).
 

La aplicación será generada en la KB main.
Los programas Java accederán tanto a tablas de la base de datos "main" (manejada con RPG) como a tablas de la base de datos "secondary" (manejada con Java), solo en el caso de requerir un atributo Blob.
Los programas RPG solo accederán a tablas de la base de datos "main".

Detalle de la solución

Hay un proyecto en el gxopen que tiene dos KBs. Una es la KB con RPG y Java que sería la "aplicación" en si y la otra es solo una KB Java que se encarga de crear las tablas con blobs.
Se detallan a continuación las partes más relevante de las mismas.

Secondary KB

Se define una KB que lo único que tiene son estructuras (TRNS) para definir y manejar (crear/reorganizar) las tablas con atributos blob.

Esta KB utiliza el generador Java como único generador.

En la figura anterior existe una transacción de "EmployeesImg" con la siguiente estructura:
EmployeeImgId*
EmployeeImgPhoto - atributo blob

Main KB

Esta KB tiene configurados como generador para la reorganización/default el RPG y como secundario el generador Java.
Es la que tendría todas las estructuras y generaría todos los objetos de la aplicación de RRHH.

Específicamente para resolver el ejemplo los objetos involucrados son:

DataView a la tabla definida en la KB Secundaria
EmpImgId*
EmppImgPhoto - atributo blob

Transacción de empleados (employees)
EmpId*
EmpName
EmpImgId 
EmpImgPhoto

Los atributos EmpImgId y EmpImgPhoto fueron agregados a la tabla de empleados.

Un "ejemplo de uso" podría ser un reporte de empleados incluyendo la foto del mismo
En este caso el código sería:
for each                            // for each employee (table managed in main KB)
    &Photo=loadbitmap(EmpImgPhoto)   // loading the employee's photo (blob). It's accessing using the DV 
    print employee                  // printing (name, id, photo, etc)
endfor


O sea, ese reporte navegará la tabla "Employees" y hará un join con la tabla "EmployeesImg" para obtener la imagen correspondiente.

En definitiva como la solución de "agregar un atributo a la tabla" no parece viable, lo que se hace es "agregar una tabla con ese atributo".

NOTA: Es importante destacar que a nivel de base de datos se establece una relación N -> 1 entre Employees y EmployeesImg cuando en realidad la relación es 1 - 1.
Esto debe controlarse por programa, es decir, no debería existir más de un empleado que hiciera referencia a la misma imagen.

¿Cómo se resuelve el caso del alta de funcionarios?
Dependerá de como sea la interfaz que se requiera.
Hay varias posibilidades:
  • utilizar un "allownulls(EmpImgId)" en la transacción de Employees y llamar a otra transacción después del insert en esta.
  • utilizar transacciones paralelas que no tengan la foreing key para que, incluso un programa RPG, pueda dar el alta.
  • en la versión yi se cuenta con la regla update que podría solucionar el problema más info sobre update rule

No parece algo complejo de resolver, es buscar la opción que mejor se adapte. Incluso más adelante en este documento se mencionan algunas variaciones a la solución lo cual puede ayudar en este sentido.

¿Cómo se resuelve la eliminación de un funcionario?
Similar al alta, al eliminar un funcionario debería llamarse a un procedure que eliminara el registro correspondiente en la tabla de EmployeesImg.

Variaciones

La solución, a nivel estructural, permite muchas variaciones.

Por ejemplo, manteniendo la tabla de funcionarios como está pero agregando una nueva tabla que establezca la relación.

Quedarían:
 
EMPLOYEES
EmployeeId*
EmployeeName

EMPLOYEEIMG
EmpImgId*
EmpImgPhoto


y se agrega
 
EMPLOYEE2
EmpId*
EmpImgId*


Incluso en este caso la funcionalidad Using Filters as Hints incluida en la versión Yi puede resultar útil.

Otra posible variación es que no haya relación entre las tablas, es decir las estructuras serían:
 
EMPLOYEES
EmployeeId*
EmployeeName

EMPLOYEEIMG
EmpImgId*
EmpImgPhoto



y no hay relación entre ellas. Si se requiere relacionarlas se hará por programa.
En el ejemplo del reporte el código sería similar a:
 
for each                            // for each employee (table managed in main KB)
    &EmployeeId=EmployeeId          // saving the employeeId
    &Photo=nullvalue(&Photo)        // initializing with null the employeePhoto
    for each                        // for each image (accessing the table defined in the secondary KB)
        where EmpImgId=&EmployeeId  // filtering using the employeeId saved previously
        &Photo=loadbitmap(EmpImgPhoto)// loading the employee's photo (blob)
    endfor
    print employee                  // printing (name, id, photo, etc)
endfor


Existen muchas variaciones más, de todos modos, todas pasan por la misma idea "extender una tabla definiendo una tabla 1-1 y no agregando atributos a la misma".

La decisión de que variación utilizar dependerá del marco en el cual se quiera utilizar.
Talvez las primeras soluciones al establecer una relación 1->N o N->1 definen una estructura más aproximada a la realidad y la última opción, al no definir relación alguna entre las tablas, puede traer aparejada más complejidad a la hora de programar objetos que requieran la relación.

Last update: February 2024 | © GeneXus. All rights reserved. GeneXus Powered by Globant