SQLQuery
+ Transformers.ALIAS_TO_ENTITY_MAP
, 在较新版本的 Hibernate(5.2+)中已被标记为 过时(deprecated),推荐使用 ResultSetMapping
或 JdbcTemplate
来确保 Map
的 key 保持驼峰命名(与 SQL 别名一致)。
优化方案
1. 使用 JdbcTemplate
(推荐)
如果项目允许,直接使用 Spring 的 JdbcTemplate
查询,可以 100% 保证 Map
的 key 与 SQL 别名一致(驼峰命名)。
优化后的代码
javaimport org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.util.List;import java.util.Map;@Repositorypublic class InventoryRepository {@Autowiredprivate JdbcTemplate jdbcTemplate;public List<Map<String, Object>> queryFullInventoryData() {StringBuilder sql = new StringBuilder();sql.append(" SELECT DISTINCT ");sql.append(" '' as warehouseName, ");sql.append(" '' as warehouseCode, ");sql.append(" b.CLIENT_NAME as customerName, ");sql.append(" b.TAX_ACCOUNT as socialCreditCode, ");sql.append(" '0' as isFutures, ");sql.append(" '普货' as isFuturesLabel, ");sql.append(" '1' as businessType, ");sql.append(" '进口' as businessTypeLabel, ");sql.append(" CASE WHEN NVL(tray.IS_BONDED, 0) = 1 THEN 'BS' ELSE 'FBS' END as tradeType, ");sql.append(" CASE WHEN NVL(tray.IS_BONDED, 0) = 1 THEN '保税' ELSE '非保税' END as tradeTypeLabel, ");sql.append(" CASE WHEN NVL(tray.IS_BONDED, 0) = 1 THEN '4' ELSE '1' END as tradeMode, ");sql.append(" CASE WHEN NVL(tray.IS_BONDED, 0) = 1 THEN '保税仓库货物' ELSE '一般贸易' END as tradeModeLabel, ");sql.append(" tray.BILL_NUM as masterBillNo, ");sql.append(" '' as futuresReceiptNo, ");sql.append(" '' as futuresReceiptStatus, ");sql.append(" tray.CTN_NUM as containerNo, ");sql.append(" tray.TRAY_ID as trayNo, ");sql.append(" '' as lotNo, ");sql.append(" 'QT' as productType, ");sql.append(" '其他' as productTypeLabel, ");sql.append(" tray.CARGO_NAME as productName, ");sql.append(" tray.CARGO_TYPE as productBrand, ");sql.append(" tray.NOW_PIECE as countL1, ");sql.append(" tray.NOW_PIECE as countL2, ");sql.append(" tray.GROSS_WEIGHT as grossWeight, ");sql.append(" tray.NET_WEIGHT as netWeight, ");sql.append(" 0.000 as volume, ");sql.append(" 0.000 as length, ");sql.append(" '11' as countOneUnit, ");sql.append(" '件' as countOneUnitLabel, ");sql.append(" '11' as countTwoUnit, ");sql.append(" '件' as countTwoUnitLabel, ");sql.append(" '35' as weightUnit, ");sql.append(" '千克' as weightUnitLabel, ");sql.append(" '113' as volumeUnit, ");sql.append(" '立方米' as volumeUnitLabel, ");sql.append(" '30' as lengthUnit, ");sql.append(" '米' as lengthUnitLabel, ");sql.append(" '' as productionDate, ");sql.append(" tray.ENTER_TALLY_TIME as inboundTime, ");sql.append(" TO_NUMBER(tray.FLOOR_NUM) as storageLocationKq, ");sql.append(" TO_NUMBER(tray.ROOM_NUM) as storageLocationCk, ");sql.append(" TO_NUMBER(tray.AREA_NUM) as storageLocationQy, ");sql.append(" tray.STOREROOM_NUM as storageLocationKw, ");sql.append(" tray.ENTER_TALLY_TIME as operateTime ");sql.append(" FROM bis_tray_info tray ");sql.append(" LEFT JOIN BIS_ENTER_STOCK a ON tray.CONTACT_NUM = a.LINK_ID ");sql.append(" LEFT JOIN BASE_CLIENT_INFO b ON a.STOCK_ID = b.IDS ");sql.append(" WHERE tray.NOW_PIECE > 0 AND tray.CARGO_STATE = '01' ");return jdbcTemplate.queryForList(sql.toString());}}
优点
- 100% 保证
Map
的 key 与 SQL 别名一致(驼峰命名)。 - 性能更好(直接走 JDBC,避免 Hibernate 的额外开销)。
- 代码更简洁,无需
ResultTransformer
或ResultSetMapping
。
2. 使用 Hibernate 6+ 的 ResultSetMapping
(纯 Hibernate 方案)
如果必须使用 Hibernate,可以改用 ResultSetMapping
(Hibernate 5.2+ 推荐方式)。
优化后的代码
javaimport org.hibernate.query.NativeQuery;import org.hibernate.transform.ResultTransformer;import org.springframework.stereotype.Repository;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import javax.persistence.SqlResultSetMapping;import javax.persistence.ColumnResult;import java.util.List;import java.util.Map;@Repository@SqlResultSetMapping(name = "InventoryDataMapping",columns = {@ColumnResult(name = "warehouseName"),@ColumnResult(name = "warehouseCode"),@ColumnResult(name = "customerName"),// ... 其他列(省略,保持与 SQL 别名一致)})public class InventoryRepository {@PersistenceContextprivate EntityManager entityManager;public List<Map<String, Object>> queryFullInventoryData() {StringBuilder sql = new StringBuilder();sql.append(" SELECT DISTINCT ");sql.append(" '山港陆海智慧冷链物流(青岛)有限公司' as warehouseName, ");sql.append(" '91370282MA944K6YX0' as warehouseCode, ");sql.append(" b.CLIENT_NAME as customerName, ");// ... 其他 SQL(保持不变)NativeQuery<Map<String, Object>> query = entityManager.createNativeQuery(sql.toString(), "InventoryDataMapping");return query.getResultList();}}
优点
- Hibernate 官方推荐方式(适用于 Hibernate 5.2+)。
Map
的 key 与 SQL 别名一致(驼峰命名)。
缺点
- 需要定义
@SqlResultSetMapping
,代码稍显繁琐。
3. 使用 ResultTransformer
(Hibernate 5 兼容方案)
如果仍在使用 Hibernate 5,可以继续用 ResultTransformer
,但需要手动转换:
javaimport org.hibernate.query.NativeQuery;import org.hibernate.transform.AliasToEntityMapResultTransformer;import org.springframework.stereotype.Repository;import javax.persistence.EntityManager;import javax.persistence.PersistenceContext;import java.util.List;import java.util.Map;@Repositorypublic class InventoryRepository {@PersistenceContextprivate EntityManager entityManager;public List<Map<String, Object>> queryFullInventoryData() {StringBuilder sql = new StringBuilder();sql.append(" SELECT DISTINCT ");sql.append(" '山港陆海智慧冷链物流(青岛)有限公司' as warehouseName, ");sql.append(" '91370282MA944K6YX0' as warehouseCode, ");sql.append(" b.CLIENT_NAME as customerName, ");// ... 其他 SQL(保持不变)NativeQuery<Map<String, Object>> query = entityManager.createNativeQuery(sql.toString());query.unwrap(NativeQuery.class).setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);return query.getResultList();}}
注意
AliasToEntityMapResultTransformer
是Transformers.ALIAS_TO_ENTITY_MAP
的替代方案(Hibernate 5.2+)。- 仍然可能受 JDBC 驱动影响(某些驱动可能返回大写 key)。
总结
方案 | 适用场景 | 推荐指数 | 备注 |
| Spring 项目 | ⭐⭐⭐⭐⭐ | 最佳方案,100% 保证驼峰命名 |
| Hibernate 6+ | ⭐⭐⭐⭐ | 官方推荐,但需要定义映射 |
| Hibernate 5 | ⭐⭐⭐ | 兼容旧版,但可能受驱动影响 |
最终推荐
✅ 优先使用 JdbcTemplate
(简单、高效、100% 保证驼峰命名)。
✅ 如果必须用 Hibernate,则使用 ResultSetMapping
(Hibernate 6+)。
❌ 避免直接使用 Transformers.ALIAS_TO_ENTITY_MAP
(已废弃)。