命名查询多表内连接同名字段值重复问题排查
问题分析与解决方案
这个问题的核心原因很明确:你的SQL查询里返回了两个同名的列s.name(学生姓名)和b.name(书籍名称),当Hibernate通过setProperties(Student.class)做映射时,它会根据列名而非表别名来匹配实体属性。因为Student类里只有一个name属性,Hibernate会把最后读取到的同名列值覆盖之前的,最终导致书籍名称被学生姓名替换。
下面是具体的解决步骤:
1. 给重复列名添加别名
首先修改你的SQL语句,把重复的name列用别名区分开,比如把书籍名称的列名改为bookName:
StringBuilder sb = new StringBuilder(); sb.append("select s.studentId, s.name, s.lastName, b.name as bookName "); sb.append("from student s "); sb.append("inner join borrowedbook c on s.studentId = c.studentId "); sb.append("inner join books b on c.bookId = b.bookId ");
2. 处理结果映射
因为你的查询结果包含了Student实体没有的bookName字段,直接用setProperties(Student.class)无法完整映射,这里有两种常用的处理方式:
方式一:手动遍历结果数组赋值
如果只是需要把书籍名称和Student关联,可以手动遍历返回的Object[]数组,把书籍名称赋值给Student的某个扩展属性(比如给Student类新增一个bookName字段):
Query query = getSession().createSQLQuery(sb.toString()); List<Object[]> searchResults = query.list(); for (Object[] result : searchResults) { Student student = new Student(); student.setStudentId((Integer) result[0]); student.setName((String) result[1]); student.setLastName((String) result[2]); student.setBookName((String) result[3]); // 假设Student类新增了这个字段 stuList.add(student); }
方式二:使用ResultTransformer映射到自定义DTO
如果不想修改Student实体,可以创建一个包含所有查询字段的DTO类,比如StudentBookDTO,然后用ResultTransformer自动映射:
// 先定义DTO类 public class StudentBookDTO { private Integer studentId; private String name; private String lastName; private String bookName; // 构造方法、getter和setter省略 } // 查询时使用ResultTransformer Query query = getSession().createSQLQuery(sb.toString()) .setResultTransformer(Transformers.aliasToBean(StudentBookDTO.class)); List<StudentBookDTO> dtoList = query.list(); // 再根据需要转换为Student列表(如果需要的话) for (StudentBookDTO dto : dtoList) { Student student = new Student(); student.setStudentId(dto.getStudentId()); student.setName(dto.getName()); student.setLastName(dto.getLastName()); // 可以把bookName存到Student的某个集合或者单独属性里 stuList.add(student); }
额外提示
如果你坚持要使用setProperties方法,那必须保证查询返回的列名和实体属性名完全一一对应,且没有重复列名。但在多表关联查询出现重复列名的场景下,给列加别名是最直接的解决办法。
内容的提问来源于stack exchange,提问作者Shun
相关产品推荐
相关产品推荐

