Avalados por :

Atualização de relatórios Crystal Reports Java: Dilema com SDKs e APIs para migração de data warehouse

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

Tenho lutado para entender algumas inconsistências que vejo com as diferentes APIs do Crystal Reports Java, e a ajuda online tem sido bastante vaga ou indeterminada para encontrar uma solução clara. Então pensei em perguntar aqui.

Desculpe a extensão, mas algum contexto é necessário...

Temos cerca de 1500 relatórios do Crystal Reports gerenciados pelo nosso servidor BOE 4.1 FP11 que apontam para nosso data warehouse. Devido a uma migração contínua do data warehouse para a plataforma de outro fornecedor, precisamos atualizar esses relatórios com novos DSNs, e um subconjunto de relatórios exigirá alguns ajustes SQL em seus objetos CommandTable. Estou tentando usar um dos SDKs de Java para automatizar esse processo. Tenha em mente que não sou um usuário ou especialista em BOE ou Crystal de forma alguma, apenas sou um programador com muita experiência usando SDKs e APIs para integrar/automatizar coisas.

O fornecedor do data warehouse está nos ajudando com essa migração e nos pediu um dump de todo o SQL personalizado (CommandTable.CommandText) dos relatórios afetados para que pudessem revisá-lo e fazer as alterações necessárias. Consegui fornecer isso facilmente usando a API do Crystal Reports for Eclipse (CR4E) e acessando diretamente os arquivos .RPT no sistema de arquivos do BOE. Mesmo que esses fossem relatórios gerenciados, consultar diretamente os arquivos RPT funcionou bem. Consegui percorrer o sistema de arquivos para coletar todos os arquivos RPT, identificar os afetados e fazer o dump dos metadados do relatório, incluindo o SQL personalizado do CommandTable, em um arquivo de texto fornecido ao fornecedor. Até aqui tudo bem...

O fornecedor então pegou esse dump de texto e executou suas ferramentas contra ele para validar o SQL e fazer qualquer atualização necessária. Eles nos devolveram um arquivo atualizado e agora supostamente devo ler esses arquivos e depois atualizar os DSNs e o SQL modificado (CommandTable.CommandText) para esses relatórios.

Então agora chegamos ao meu dilema atual:

Percebo que o SDK do CR4E não é destinado a relatórios gerenciados, mas pensei em pegar um par desses RPT gerenciados e trazê-los como cópias locais, apenas para experimentar com o código de atualização antes de tentar acessá-los remotamente através de uma API gerenciada. Escrevi um código muito simples para trabalhar na dinâmica de atualização, e o código é executado sem erros. No entanto, na realidade, nenhuma alteração é feita quando chamo ReportClientDocument.save() e .close(). O timestamp do arquivo muda, indicando que o arquivo foi atualizado, mas o tamanho do arquivo é idêntico, indicando que o conteúdo não mudou. Também tentei ReportClientDocument.saveAs() com um nome de arquivo diferente: o novo arquivo também é escrito, mas novamente é idêntico ao arquivo original.

Ao depurar, consigo ver a alteração de SQL no campo CommandTable.CommandText na memória, então sei que a chamada CommandTable.setCommandText() está funcionando. Mas, por que nunca chega ao arquivo de destino? O arquivo não é somente leitura, pois verifico se ReportClientDocument.isReadOnly() retorna false. E novamente, saveAs() escreve um arquivo, mas com o conteúdo original.

Será que o SDK do CR4E sabe que este é na verdade um relatório gerenciado e se recusa a alterá-lo? Se for esse o caso, por que não lança uma exceção em vez de falhar silenciosamente e implicar sucesso? Esperava manter isso o mais rápido e simples possível, então primeiro testei a mesma API do CR4E com a qual comecei. Também tenho disponível o SDK do RAS e o SDK do BIP41 para testar através da API gerenciada e meio que esperava ter que fazer isso de qualquer maneira para garantir que o banco de dados de metadados seja atualizado corretamente. Mas antes de ficar dando voltas, há algo que estou perdendo ou fazendo errado?

Aqui está um trecho de código que mostra um exemplo muito rápido e sujo, usando um arquivo de relatório local codificado de forma rígida que tem 3 objetos CommandTable, e estou tentando alterar o primeiro CommandTable.CommandText para uma string SQL fictícia.

File rptFile = new File("test/update_test1.rpt");
ReportClientDocument clientDoc = ReportClientDocument.openReport(rptFile);
if ( clientDoc.isReadOnly() )
  throw new Exception("O relatório é somente leitura!");
IDatabase database = clientDoc.getDatabaseController().getDatabase
                
                
Pedro Pascal
Se unió el 07/03/2018
Pinterest
Telegram
Linkedin
Whatsapp

4 Respuestas

0
Cargando...

Aha, acredito que você estava se referindo à chamada ReportClientDocument.verifyDatabase().

No entanto, tenho a mesma preocupação com essa chamada, pois também tenta se conectar ao banco de dados e validar todas as colunas. Tentei fazer isso e recebi uma exceção indicando que o nome JNDI (o DSN) não foi encontrado (o que é verdade, pois não existe em minha máquina local onde esse arquivo de teste está localizado). Pode funcionar a partir de uma conexão RAS gerenciada, mas novamente estaria preocupado com a sobrecarga de verificar cada conexão.

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

Olá David,

Primeiro, você deve fazer tudo isso em um relatório primeiro. Abra o Designer, abra um de seus relatórios, clique na opção Database e depois em Set Location.....

Selecione o Cliente, preencha as informações de login, uma vez que a conexão seja estabelecida, clique na nova fonte de dados e depois na antiga fonte de dados e então no botão Mapear. Se houver problemas de mapeamento de tipos de campo, uma interface de mapeamento será aberta e você terá que mapear os campos individualmente.

Defina a localização para cada fonte de dados listada, se você tiver um sub-relatório, faça o mesmo.

Agora clique em Database... e depois em Verify, isso atualizará as informações da tabela no relatório com as novas informações da tabela selecionada.

Agora use o mesmo fluxo de trabalho no SDK. Neste caso, você precisará usar ReplaceConnection(OldConn, NewConn, DoNotVerify) para que as informações do banco de dados sejam atualizadas corretamente, mesmo ao usar um Command.

Ambas as chamadas são uma configuração global, então valide cada tabela na coleção, não é necessário chamá-las em loop para definir a localização, uma vez que esse loop seja concluído, faça as chamadas apenas uma vez. As conexões de sub-relatórios também devem ser configuradas, então faça essas chamadas.

E você realmente precisa estar conectado ao banco de dados, não pode simplesmente chamá-las e esperar que a conexão funcione ou verifique as informações da tabela.

O fluxo de trabalho seria o seguinte:

Abra o relatório

Obtenha o SQL do objeto Command

Altere o SQL e configure-o para os relatórios principais e os sub-relatórios

Agora configure as propriedades de conexão para fazer login.

Se o relatório estiver utilizando um Procedimento Armazenado, configure primeiro os parâmetros do SP antes de fazer login.

Caso contrário, defina os valores de qualquer parâmetro do CR que precise ser alterado ou que não tenha valores padrão salvos

Agora faça login no BD

Verifique a conectividade aqui, isso não verifica as informações dos dados do relatório, apenas testa se a informação de conexão funciona.

Defina os valores padrão dos parâmetros, se necessário.

Agora SetLocation()

Agora verifique o banco de dados.

E agora você pode salvar o relatório, se estiver usando RAS para atualizar alguma parte, então precisa usar o objeto ReportClientDocument ao salvar. Ele tem as informações atualizadas. O ClientDocument pode não tê-las, algumas propriedades básicas podem ser salvas, mas para alterar a fonte de BD, você precisa usar o objeto RCD.

Apenas para esclarecer, você está usando Java ou .NET?

Não tenho exemplos, mas talvez você possa encontrar alguns aqui:

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

E mais aqui:

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

Para exemplos do .NET, veja aqui, escrevi 2 aplicativos de teste, um para Parâmetros/Login e outro para Impressão:

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,

Primeiramente, obrigado pela resposta detalhada!

Mas isso é muito trabalho, eu esperava poder evitar. Se fosse um ou um punhado de relatórios, provavelmente não seria grande coisa. Mais de 1000, suponho que levará um tempo para processar.

Vou tentar e, se conseguir fazer funcionar com um único relatório, farei um teste moderado para ver como funciona e determinar a viabilidade. Talvez tenhamos que dividir para conquistar, se não houver outra opção.

Estou utilizando o SDK de Java, como indicado no título do tópico. Tenho todas as referências que você vinculou, bem como exemplos em .NET e Java JSP, nos quais baseei minha abordagem, mas nenhum dos exemplos ou documentos abordava diretamente a atualização do Command SQL, então tenho estado principalmente às cegas. E não me lembro de ter visto EM LUGAR ALGUM que o fluxo de trabalho que você descreve acima seja necessário para fazer isso. Seria ótimo se isso levasse a uma solução funcional, mas eu sugeriria uma documentação mais clara a respeito disso, nesse caso.

Vou informar sobre o que descobrir.

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

Não tenho Java, mas aqui está um exemplo usando .NET para obter o comando SQL:


        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 = "O relatório está usando o objeto Command: \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 = "ERRO: " + ex.Message;
                btnDBDriver.Text += "O relatório principal não possui driver de dados";
            }
        }
    

Para atualizar o SQL, aqui está um exemplo:


        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;
        }
    

Espero que isso ajude...

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?