Spring Boot集成Crystal Reports连接PostgreSQL的问题排查
问题:Crystal Reports for Eclipse与Spring Boot集成时ODBC数据源连接失败
环境与前置问题
- 使用64位Crystal Reports 2025,已安装PostgreSQL ODBC驱动
- 创建.rpt文件时,JDBC连接能看到数据库表但无法展开字段(点击表名旁的‘+’无反应),ODBC连接则可正常列出字段
核心需求
将Crystal Reports for Eclipse SP31与Spring Boot(Gradle、Java 21)集成,实现从远程.rpt文件导出PDF的功能
当前实现代码
public byte[] generateReport() throws IOException, ReportSDKException { // 1) Download .rpt byte[] rptBytes = myStorageService.getFile( "https://url/to/the/file.rpt" ); // 2) Write to a temp .rpt file Path tempRpt = Files.createTempFile("temp-", ".rpt"); Files.write(tempRpt, rptBytes); tempRpt.toFile().deleteOnExit(); // 3) Open in Crystal/JRC (read-only) ReportClientDocument report = new ReportClientDocument(); try { report.open( tempRpt.toString(), com.crystaldecisions.sdk.occa.report.application.OpenReportOptions._openAsReadOnly ); // 4) Grab DatabaseController & IDatabase DatabaseController dbController = report.getDatabaseController(); IDatabase database = dbController.getDatabase(); Connections connections = database.getConnections(); // 5) Rebind each table for (int i = 0; i < connections.size(); i++) { IConnection conn = connections.get(i); IConnectionInfo connInfo = conn.getConnectionInfo(); // a) Clear any old, designer-saved properties PropertyBag bag = connInfo.getAttributes(); bag.clear(); bag.put("DSN", "PostgreSQL30"); bag.put("User ID", "my_user"); bag.put("Password", "my_password"); bag.put("UseDSNProperties", "false"); connInfo.setAttributes(bag); connInfo.setKind(ConnectionInfoKind.SQL); conn.setConnectionInfo(connInfo); } dbController.logonEx("PostgreSQL30", "", "my_user", "my_password"); // 7) Export to PDF ByteArrayOutputStream baos = new ByteArrayOutputStream(); ExportOptions opts = new ExportOptions(); PDFExportFormatOptions pdfOpts = new PDFExportFormatOptions(); opts.setExportFormatType(ReportExportFormat.PDF); opts.setFormatOptions(pdfOpts); report.getPrintOutputController().export(opts, baos); baos.flush(); return baos.toByteArray(); } finally { try { report.close(); } catch (Exception ignored) {} Files.deleteIfExists(tempRpt); } }
报错信息
2025-06-02T20:09:12.331-05:00 ERROR 40348 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: com.crystaldecisions.sdk.occa.report.lib.ReportSDKException: Error finding JNDI name (PostgreSQL30)---- Error code:-2147467259 Error code name:failed] with root cause com.businessobjects.reports.jdbinterface.common.DBException: Error finding JNDI name (PostgreSQL30) at com.crystaldecisions.reports.queryengine.driverImpl.jdbc.JDBCConnection.Open(Unknown Source) ~[DatabaseConnectors.jar:12.2.232.5499] at com.crystaldecisions.reports.queryengine.JDBConnectionWrapper.Open(SourceFile:123) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.queryengine.Connection.bs(SourceFile:1788) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.queryengine.Connection.bv(SourceFile:507) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.queryengine.Connection.uk(SourceFile:3035) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.dataengine.dfadapter.DFAdapter.a(SourceFile:697) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.dataengine.dfadapter.DFAdapter.for(SourceFile:707) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.reportdefinition.ReportHelper.a(SourceFile:198) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.requesthandler.ReportViewingRequestHandler.long(SourceFile:962) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.requesthandler.ReportViewingRequestHandler.a(SourceFile:640) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.requesthandler.ReportViewingRequestHandler.int(SourceFile:666) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.do(SourceFile:1944) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.if(SourceFile:661) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.a(SourceFile:167) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter$2.a(SourceFile:529) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter$2.call(SourceFile:526) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.reports.common.ThreadGuard.syncExecute(SourceFile:102) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.for(SourceFile:525) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.int(SourceFile:424) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.reports.sdk.JRCCommunicationAdapter.request(SourceFile:352) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.sdk.erom.jrc.a.a(SourceFile:54) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.businessobjects.sdk.erom.jrc.a.execute(SourceFile:67) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.proxy.remoteagent.RemoteAgent$a.execute(SourceFile:716) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.proxy.remoteagent.CommunicationChannel.a(SourceFile:125) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.proxy.remoteagent.RemoteAgent.a(SourceFile:537) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.dt.a(SourceFile:186) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.ReportSource.a(SourceFile:1558) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.ReportSource.a(SourceFile:337) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.PrintOutputController.if(SourceFile:224) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.PrintOutputController.export(SourceFile:206) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.crystaldecisions.sdk.occa.report.application.PrintOutputController.export(SourceFile:184) ~[CrystalReportsRuntime.jar:12.2.232.5499] at com.myproject.services.AppReportService.generateReport(AppReportService.java:283) ~[main/:na]
已排查情况
- 已在64位和32位ODBC数据源管理器中配置PostgreSQL30,Crystal Reports客户端可正常使用该数据源
- 问题仅出现在Spring Boot集成后的报表导出环节
解决建议
- 修正连接类型配置:当前代码中
connInfo.setKind(ConnectionInfoKind.SQL)会让JRC尝试用JDBC/JNDI连接,改为ConnectionInfoKind.ODBC明确指定使用ODBC驱动 - 调整ODBC属性参数:修改PropertyBag配置,添加驱动名称等关键参数,确保JRC能识别ODBC驱动:
bag.put("DSN", "PostgreSQL30"); bag.put("User ID", "my_user"); bag.put("Password", "my_password"); bag.put("UseDSNProperties", "true"); // 依赖DSN配置时设为true bag.put("Driver", "{PostgreSQL Unicode(x64)}"); // 对应安装的64位驱动名称 - 替换logonEx方法:
dbController.logonEx("PostgreSQL30", "", "my_user", "my_password")中的第一个参数被解析为JNDI名称,改用dbController.logon("my_user", "my_password"),让JRC使用已配置的ODBC连接信息 - 确认进程位数匹配:Spring Boot是64位Java进程,必须确保系统中64位PostgreSQL ODBC驱动已正确安装,且ODBC数据源是64位版本(32位数据源无法被64位进程识别)
- 改用直连配置(推荐):在Spring Boot环境中,DSN可能因权限或环境变量问题无法被JRC读取,直接配置数据库地址、端口、库名等参数替代DSN:
bag.clear(); bag.put("Driver", "{PostgreSQL Unicode(x64)}"); bag.put("Server", "your_db_host"); bag.put("Port", "5432"); bag.put("Database", "your_db_name"); bag.put("User ID", "my_user"); bag.put("Password", "my_password"); bag.put("UseDSNProperties", "false"); - 验证依赖兼容性:确保Crystal Reports for Eclipse的JAR包支持Java 21,若版本不兼容需升级到对应支持版本
- 检查数据库权限:确认
my_user拥有报表涉及表的SELECT权限,且服务器端口开放、网络连接正常
内容的提问来源于stack exchange,提问作者José
相关产品推荐
相关产品推荐

