Hibernate使用addScalar映射NativeQuery到DTO时报列不存在错误
问题根因
报错java.sql.SQLException: Column 'absenceId' not found的核心原因是SQL返回的列名、addScalar声明的列名、DTO属性名三者不匹配,同时代码存在多处映射疏漏和不规范写法:
- SQL查询的数据库字段以下划线命名,且没有配置对应别名:比如查询的
a.absence_id返回列名是absence_id,但addScalar中声明的列名是驼峰格式的absenceId,数据库返回结果里根本不存在这个名称的列,直接触发列找不到的错误。同理create_date、date_off、enable_date等字段都存在这个问题。 - 字段映射错配:DTO中定义的是
startDate属性,但SQL查询的是create_date、addScalar声明的是createDate,三者完全不对应;SQL查询了date_off字段,但DTO里根本没有定义dateOff属性,后续就算列名匹配也会映射失败。 - 存在SQL注入风险:直接拼接用户传入的id参数到SQL字符串,没有使用参数占位符。
- DTO类缺失必要结构:没有无参构造、没有属性对应的getter/setter方法,
fullNameOfDepartment字段末尾缺失分号,aliasToBean转换器无法通过反射完成属性赋值。
修正方案
1. 调整查询语句,统一列别名,改用参数传值
SQL中所有查询字段必须起和addScalar、DTO属性名完全一致的别名,参数改用命名占位符传递,避免SQL注入:
public List<AbsenceResponse> findAllByUserId1(Long id) { StringBuilder sql = new StringBuilder(); sql.append( "SELECT a.absence_id as absenceId, " + "a.create_date as startDate, " + "a.end_date as endDate," + "a.date_off as dateOff," + "a.title as title," + "a.description as description," + "a.status as status," + "a.type as type," + "a.enable_date as enableDate, " + "u.full_name as fullNameOfDepartment"); sql.append(" FROM absence a "); sql.append(" INNER JOIN user u ON u.user_id = a.absence_by "); sql.append(" WHERE a.user_id = :userId"); List<AbsenceResponse> res = entityManager.unwrap(org.hibernate.Session.class) .createNativeQuery(sql.toString()) .addScalar("absenceId", LongType.INSTANCE) .addScalar("startDate", DateType.INSTANCE) .addScalar("endDate", DateType.INSTANCE) .addScalar("dateOff", IntegerType.INSTANCE) .addScalar("title", StringType.INSTANCE) .addScalar("description", StringType.INSTANCE) .addScalar("status", IntegerType.INSTANCE) .addScalar("type", IntegerType.INSTANCE) .addScalar("enableDate", DateType.INSTANCE) .addScalar("fullNameOfDepartment", StringType.INSTANCE) .setParameter("userId", id) .setResultTransformer(Transformers.aliasToBean(AbsenceResponse.class)) .list(); return res; }
2. 补全DTO类结构
新增缺失的dateOff属性,补全无参构造和所有属性的getter/setter方法,修正语法笔误:
import java.util.Date; public class AbsenceResponse { private Long absenceId; private Date startDate; private Date endDate; private Integer dateOff; private String title; private String description; private int status; private int type; private Date enableDate; private String fullNameOfDepartment; // 别名转换器必须依赖无参构造实例化对象 public AbsenceResponse() {} // 补全所有属性的getter、setter public Long getAbsenceId() {return absenceId;} public void setAbsenceId(Long absenceId) {this.absenceId = absenceId;} public Date getStartDate() {return startDate;} public void setStartDate(Date startDate) {this.startDate = startDate;} public Date getEndDate() {return endDate;} public void setEndDate(Date endDate) {this.endDate = endDate;} public Integer getDateOff() {return dateOff;} public void setDateOff(Integer dateOff) {this.dateOff = dateOff;} public String getTitle() {return title;} public void setTitle(String title) {this.title = title;} public String getDescription() {return description;} public void setDescription(String description) {this.description = description;} public int getStatus() {return status;} public void setStatus(int status) {this.status = status;} public int getType() {return type;} public void setType(int type) {this.type = type;} public Date getEnableDate() {return enableDate;} public void setEnableDate(Date enableDate) {this.enableDate = enableDate;} public String getFullNameOfDepartment() {return fullNameOfDepartment;} public void setFullNameOfDepartment(String fullNameOfDepartment) {this.fullNameOfDepartment = fullNameOfDepartment;} }
注意事项
addScalar传入的列名大小写敏感,必须和SQL中定义的字段别名完全一致,NativeQuery不会自动把数据库下划线命名转成驼峰命名。Transformers.aliasToBean的映射规则是通过反射调用DTO属性对应的setter方法赋值,所以DTO必须有无参构造,所有待映射字段必须有public修饰的setter方法。- 如果使用Hibernate 5.2+版本,
setResultTransformer已经被标记为废弃,可以改用TupleTransformer或者JPA自带的SqlResultSetMapping实现DTO映射,逻辑是一致的。
内容的提问来源于stack exchange,提问作者Phạm Cường
相关产品推荐
相关产品推荐

