无Model类时如何通过EntityManager生成并下载CSV文件?
无需Model类实现SQL查询结果生成CSV并下载
问题根源
原代码中em.createNativeQuery()执行select *时,返回的是**List<Object[]>**(每行数据对应一个Object数组,数组元素是各列的值),强行转成List<String>必然抛出ClassCastException。调试看到的[column1, column2, column3]其实是Object数组的toString()结果,并非字符串类型的元素。
解决方案
1. 修正服务类代码
把返回类型改为List<Object[]>,直接获取原生查询的正确结果:
public List<Object[]> searchBersaniFile() { Query q = em.createNativeQuery(SQL_SELECT_REPNORM_BERSANI); // 无需强转,getResultList()返回的就是List<Object[]> List<Object[]> resultList = q.getResultList(); // 空判断避免后续空指针异常 return resultList == null ? new ArrayList<>() : resultList; }
2. 修正CSV生成逻辑
遍历List<Object[]>,将每行的Object数组元素逐个转成字符串,用分号分隔,同时处理空值与CSV规范兼容问题:
try (ServletOutputStream outServlet = response.getOutputStream()) { List<Object[]> listResult = null; switch (flowType) { case STATO_BERSANI: listResult = awpSapNewRepository.searchBersaniFile(); break; } StringBuffer buffer = new StringBuffer(); // 遍历每行数据 for (Object[] row : listResult) { List<String> columnValues = new ArrayList<>(); for (Object value : row) { // 处理空值,统一转为空字符串 String strValue = value == null ? "" : value.toString(); // 兼容CSV规范:若字段含分隔符、引号或换行,进行转义 if (strValue.contains(";") || strValue.contains("\"") || strValue.contains("\n") || strValue.contains("\r")) { strValue = "\"" + strValue.replace("\"", "\"\"") + "\""; } columnValues.add(strValue); } // 用分号拼接成一行 buffer.append(String.join(";", columnValues)); buffer.append("\r\n"); } String fileName = buildFileName(flowType); // 处理文件名编码,避免中文/特殊字符乱码 String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()); response.setContentType("text/csv; charset=utf-8"); response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); outServlet.write(buffer.toString().getBytes(StandardCharsets.UTF_8)); outServlet.flush(); } catch (Exception e) { e.printStackTrace(); // 此处可添加自定义异常处理逻辑,如返回错误提示 }
关键优化点
- 类型修正:正确识别JPA原生查询返回的
Object[]数组,彻底解决类型转换异常 - 空值处理:防止空指针异常,统一将null转为空字符串
- CSV规范兼容:处理字段包含分隔符、引号、换行的场景,避免CSV解析错误
- 文件名编码:通过URLEncoder处理文件名,解决中文/特殊字符导致的下载文件名乱码问题
扩展:动态添加表头(可选)
如果需要在CSV开头添加表头,可以通过查询元数据获取列名:
// 在服务类中新增获取列名的方法 public List<String> getBersaniColumnNames() { Query q = em.createNativeQuery(SQL_SELECT_REPNORM_BERSANI); ResultSetMetaData metaData = q.unwrap(org.hibernate.query.NativeQuery.class).getResultSetMetadata(); List<String> columnNames = new ArrayList<>(); for (int i = 1; i <= metaData.getColumnCount(); i++) { columnNames.add(metaData.getColumnName(i)); } return columnNames; }
在生成CSV时先写入表头:
// 先写入表头 List<String> columnNames = awpSapNewRepository.getBersaniColumnNames(); buffer.append(String.join(";", columnNames)); buffer.append("\r\n"); // 再写入数据行...
内容的提问来源于stack exchange,提问作者Robs
相关产品推荐
相关产品推荐

