¡Caminando hacia el éxito!

Aprende en Comunidad

Avalados por :

Actualización de informes Crystal Reports Java: Dilema con SDKs y APIs para migración de almacén de datos

  • Creado 01/03/2024
  • Modificado 01/03/2024
  • 14 Vistas
0
Cargando...

He estado luchando por entender algunas inconsistencias que veo con las diferentes APIs de Crystal Reports Java, y la ayuda en línea ha sido bastante vaga o indeterminada para encontrar una solución clara. Así que pensé en preguntar aquí.

Perdona la extensión, pero algo de contexto es necesario...

Tenemos alrededor de 1500 informes de Crystal Reports gestionados por nuestro servidor BOE 4.1 FP11 que apuntan a nuestro almacén de datos. Debido a una migración continua del almacén de datos a la plataforma de otro proveedor, necesitamos actualizar estos informes con nuevos DSNs, y un subconjunto de informes requerirá algunos ajustes SQL en sus objetos CommandTable. Estoy intentando usar uno de sus SDKs de Java para automatizar este proceso. Ten en cuenta que no soy un usuario o experto de BOE o Crystal en absoluto, solo soy un programador con mucha experiencia usando SDKs y APIs para integrar/automatizar cosas.

El proveedor del almacén de datos nos está ayudando con esta migración, y nos pidió un volcado de todo el SQL personalizado (CommandTable.CommandText) de los informes afectados para que pudieran revisarlo y hacer los cambios necesarios. Pude proporcionar esto fácilmente utilizando la API de Crystal Reports for Eclipse (CR4E) y yendo directamente a los archivos .RPT en el sistema de archivos de BOE. A pesar de que estos eran informes gestionados, interrogar directamente los archivos RPT funcionó bien. Pude recorrer el sistema de archivos para recopilar todos los archivos RPT, identificar los afectados y volcar los metadatos del informe, incluido el SQL personalizado de CommandTable, en un archivo de texto proporcionado al proveedor. Hasta aquí todo bien...

El proveedor luego tomó este volcado de texto y ejecutó sus herramientas contra él para validar el SQL y hacer cualquier actualización necesaria. Nos devolvieron un archivo actualizado, y ahora se supone que debo leer esos archivos y luego actualizar los DSNs y el SQL modificado (CommandTable.CommandText) para esos informes.

Así que ahora llegamos a mi dilema actual:

Me doy cuenta de que el SDK de CR4E no está destinado a informes gestionados, pero pensé en tomar un par de estos RPT gestionados y traerlos como copias locales, solo para experimentar con el código de actualización antes de intentar acceder a ellos de forma remota a través de una API gestionada. He escrito un código muy simple para trabajar en la dinámica de actualización, y el código se ejecuta sin errores. Sin embargo, en realidad no se realiza ningún cambio cuando llamo a ReportClientDocument.save() y .close(). La marca de tiempo del archivo cambia, lo que indica que el archivo se actualizó, pero el tamaño del archivo es idéntico, lo que indica que el contenido no cambió. También intenté ReportClientDocument.saveAs() con un nombre de archivo diferente: el nuevo archivo también se escribe, pero nuevamente es idéntico al archivo original.

Al depurar, puedo ver el cambio de SQL en el campo CommandTable.CommandText en memoria, por lo que sé que la llamada CommandTable.setCommandText() está funcionando. Pero, ¿por qué nunca llega al archivo de destino? El archivo no es de solo lectura, ya que compruebo si ReportClientDocument.isReadOnly() devuelve false. Y de nuevo, saveAs() escribe un archivo pero con el contenido original.

¿Es porque el SDK de CR4E sabe que este es en realidad un informe gestionado y se niega a cambiarlo? Si es así, ¿por qué no se lanza una excepción en lugar de fallar silenciosamente e implicar éxito? Esperaba mantener esto lo más rápido y simple posible, así que primero probé la misma API de CR4E con la que comencé. También tengo disponible el SDK de RAS y el SDK de BIP41 para probar a través de la API gestionada, y medio esperaba tener que hacer eso de todos modos para asegurarme de que la base de datos de metadatos se actualice correctamente. Pero antes de volver a dar vueltas, ¿hay algo que me esté perdiendo o haciendo mal?

Aquí tienes un fragmento de código que muestra un ejemplo muy rápido y sucio, utilizando un archivo de informe local codificado de forma rígida que tiene 3 objetos CommandTable, y estoy intentando cambiar el primer CommandTable.CommandText a una cadena SQL ficticia.

File rptFile = new File("test/update_test1.rpt");
ReportClientDocument clientDoc = ReportClientDocument.openReport(rptFile);
if ( clientDoc.isReadOnly() )
  throw new Exception("El informe es de solo lectura!");
IDatabase database = clientDoc.getDatabaseController().getDatabase
                
                
Pedro Pascal
Se unió el 07/03/2018
Pinterest
Telegram
Linkedin
Whatsapp

4 Respuestas

0
Cargando...

Aha, creo que te estabas refiriendo a la llamada ReportClientDocument.verifyDatabase().

Sin embargo, tengo la misma preocupación que con la otra llamada, ya que esta también intenta conectarse a la base de datos y validar todas las columnas. Intenté hacerlo y recibí una excepción que indicaba que el nombre JNDI (el DSN) no se encontraba (lo cual es cierto, ya que no existe en mi máquina local donde se encuentra este archivo de prueba). Podría funcionar desde una conexión RAS administrada, pero nuevamente estaría preocupado por la sobrecarga de verificar cada conexión.

Respondido el 15/04/2024
LUCIANO RIOJA GHIOTTO
Se unió el 13/07/2019
0
Cargando...

Hola David,

Lo primero que debes hacer es realizar todo esto en un informe primero. Abre el Diseñador, abre uno de tus informes, haz clic en la opción de menú Database y luego en Set Location.....

Selecciona el Cliente, completa la información de inicio de sesión, una vez que se establezca la conexión, haz clic en el nuevo origen de datos y luego en el antiguo origen de datos y luego en el botón Map. Si se detectan problemas de asignación de tipos de campo, se abrirá una interfaz de asignación y deberás asignar los campos individualmente.

Establece la ubicación para cada origen de datos listado, si tienes un subinforme haz lo mismo.

Ahora haz clic en Database... y luego en Verify, esto actualizará la información de la tabla en el informe con la nueva información de la tabla seleccionada.

Ahora utiliza el mismo flujo de trabajo en el SDK. En este caso, necesitarás usar ReplaceConnection(OldConn, NewConn, DoNotVerify) para que la información de la base de datos se actualice correctamente, incluso al usar un Command.

Ambas llamadas son una configuración global, por lo que valida cada tabla en la colección, por lo que no es necesario llamarlo en el bucle para establecer la ubicación, una vez que se complete ese bucle, realiza las llamadas solo una vez. Las conexiones de subinformes también deben establecerse, luego realiza esas llamadas.

Y debes estar realmente conectado a la base de datos, no puedes simplemente llamarlas y esperar que la conexión funcione o verifique la información de la tabla.

El flujo de trabajo sería el siguiente:

Abre el informe

Obtén el SQL del objeto Command

Cambia el SQL y configúralo para los informes principales y los subinformes

Ahora establece las propiedades de conexión para iniciar sesión.

Si el informe está utilizando un Procedimiento Almacenado, configura primero los parámetros del SP antes de iniciar sesión.

Si no, establece los valores de cualquier parámetro de CR que necesite ser cambiado o que no tenga valores predeterminados guardados

Ahora inicia sesión en la BD

Verifica la conectividad aquí, esto no verifica la información de los datos del informe, solo prueba si la información de conexión funciona.

Establece los valores predeterminados de los parámetros, si es necesario.

Ahora SetLocation()

Ahora verifica la base de datos.

Y ahora puedes guardar el informe, si usas RAS para actualizar alguna parte, entonces necesitas usar el objeto ReportClientDocument al guardar. Tiene la información actualizada. El ClientDocument puede no tenerla, algunas propiedades básicas se pueden guardar, pero para cambiar la fuente de DB necesitas usar el objeto RCD.

Solo para ser claro, ¿estás usando Java o .NET?

No tengo ejemplos, pero tal vez puedas encontrar algunos aquí:

https://wiki.scn.sap.com/wiki/display/BOBJ/Java+%28Crystal+Reports+for+Eclipse%29+SDK

Y más aquí:

https://wiki.scn.sap.com/wiki/display/BOBJ/Java+SDK

Para ejemplos de .NET ve aquí, escribí 2 aplicaciones de prueba, una para Parámetros/Inicio de sesión y otra para Impresión:

https://wiki.scn.sap.com/wiki/display/BOBJ/Crystal+Reports%2C+Developer+for+Visual+Studio+Downloads

Don

Respondido el 15/04/2024
LUCIANO RIOJA GHIOTTO
Se unió el 13/07/2019
0
Cargando...

Don,

¡Primero, gracias por la respuesta detallada!

Pero vaya, eso es mucho trabajo que esperaba poder evitar. Si fueran uno o un puñado de informes, probablemente no sería gran cosa. Más de 1000 supongo que llevará un tiempo procesar.

Lo intentaré y si logro que funcione con un solo informe, tomaré una muestra moderada y veré cómo funciona para determinar la viabilidad. Tal vez solo tengamos que dividir y conquistar, si no hay otra opción.

Estoy utilizando el SDK de Java, como se indica en el título del hilo. Tengo todas las referencias que enlazaste, así como ejemplos de .NET y Java JSP, en los que basaba mi enfoque, pero ninguno de los ejemplos o documentos trataba directamente sobre la actualización de Command SQL, por lo que he estado mayormente a ciegas. Y no recuerdo haber visto EN NINGÚN LUGAR que se requiera el flujo de trabajo que describes arriba para hacer esto. Será genial si conduce a una solución funcional, pero sugeriría una documentación más clara al respecto en ese caso.

Informaré sobre lo que descubra.

Respondido el 15/04/2024
LUCIANO RIOJA GHIOTTO
Se unió el 13/07/2019
0
Cargando...

I don't have Java but here's a sample using .NET to get the SQL Command:


        for (int x = 0; x < dbConCount; x++)
        {
            try
            {
                DBDriver = rptClientDoc.DatabaseController.GetConnectionInfos()[x].Attributes.get_StringValue("Database DLL").ToString();
                btnDBDriver.Text += DBDriver + " :";
                if (((dynamic)rptClientDoc.Database.Tables[0].Name) == "Command")
                {
                    CrystalDecisions.ReportAppServer.Controllers.DatabaseController databaseController = rpt.ReportClientDocument.DatabaseController;
                    ISCRTable oldTable = (ISCRTable)databaseController.Database.Tables[0];

                    btnSQLStatement.Text = "Report is using Command Object: \n" + ((dynamic)oldTable).CommandText.ToString();
                    btnSQLStatement.Text += "\n";

                    IsLoggedOn = false;
                    IsCMD = true;
                }
                if (DBDriver.ToString() == "crdb_bwmdx.dll")
                    IsBEX = true;
            }
            catch (Exception ex)
            {
                //btnDBDriver.Text = "ERROR: " + ex.Message;
                btnDBDriver.Text += "Main Report has no Data Driver";
            }
        }
    

To update the SQL here's an example:


        static void testSetSQLCommandTable_CRPE(CrystalDecisions.CrystalReports.Engine.ReportDocument rpt)
        {
            //CrystalDecisions.CrystalReports.Engine.ReportDocument rpt = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
            //ISCDReportClientDocument rcd;

            //rcd = rpt.ReportClientDocument;

            CrystalDecisions.Shared.ConnectionInfo crConnInfo = new CrystalDecisions.Shared.ConnectionInfo();

            CrystalDecisions.ReportAppServer.Controllers.DatabaseController databaseController = rpt.ReportClientDocument.DatabaseController;
            CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfos connectionInfos = databaseController.GetConnectionInfos(null);
            CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo oldConnectionInfo = connectionInfos[0];
            CrystalDecisions.ReportAppServer.DataDefModel.ConnectionInfo newConnectionInfo = CreateConnectionInfo();
            databaseController.ReplaceConnection(oldConnectionInfo, newConnectionInfo, null, CrDBOptionsEnum.crDBOptionMapFieldByRowsetPosition);

            ISCRTable oldTable = (ISCRTable)databaseController.Database.Tables[0];
            CommandTable newTable = new CommandTable();
            newTable.Alias = "Command";
            newTable.Name = "Command";
            newTable.QualifiedName = "Command";
            newTable.ConnectionInfo = newConnectionInfo;
            newTable.CommandText = @"SELECT 'Customer'.'Contact Last Name' FROM 'xtreme'.'dbo'.'Customer' 'Customer'";
            //newTable.CommandText = @"SELECT 'Checks'.'CheckID', 'Checks'.'LoopType', 'Checks'.'RunType' FROM 'astellastest'.'dbo'.'Checks' 'Checks' WHERE 'Checks'.'CheckID'<100";

            databaseController.SetTableLocation(oldTable, newTable);
            //IsRpt = false;
        }
    

Hope that helps...

Don

Respondido el 15/04/2024
LUCIANO RIOJA GHIOTTO
Se unió el 13/07/2019

contacto@primeinstitute.com

(+51) 1641 9379
(+57) 1489 6964

© 2024 Copyright. Todos los derechos reservados.

Desarrollado por Prime Institute

¡Hola! Soy Diana, asesora académica de Prime Institute, indícame en que curso estas interesado, saludos!
Hola ¿Puedo ayudarte?