迁移至Spring Boot 2.0.0.RELEASE后遇"Got different size of tuples and aliases"异常求助
这个问题大概率是因为Spring Boot 2.0.0.RELEASE对应的Hibernate版本(5.2.x系列)对结果集到非实体POJO的映射做了更严格的校验,而你之前依赖的隐式映射逻辑在正式版中被收紧了。下面是具体的排查和解决步骤:
1. 核对结果集列数与POJO属性/构造参数数量
首先要确认存储过程返回的列的数量,和你用来接收结果的POJO的属性数量(如果用setter注入)或者构造函数参数数量(如果用构造函数注入)完全一致。新版本的Hibernate会严格校验这个匹配度,哪怕多一列或少一列都会抛出这个异常。
2. 显式定义结果集映射(推荐方案)
在Spring Boot 2.0之前的版本,可能允许Hibernate隐式地将结果集映射到POJO,但正式版中更推荐显式使用@SqlResultSetMapping和@ConstructorResult来绑定结果:
步骤1:给POJO定义结果集映射
在你的实体类或者POJO类上添加@SqlResultSetMapping(注意:如果POJO不是实体类,这个注解需要放在任意一个实体类上,因为JPA要求映射定义在实体类或包级注解中):
@Entity @SqlResultSetMapping( name = "UserStatsMapping", classes = @ConstructorResult( targetClass = UserStatsPojo.class, columns = { @ColumnResult(name = "user_id", type = Long.class), @ColumnResult(name = "total_orders", type = Integer.class), @ColumnResult(name = "last_login", type = LocalDateTime.class) } ) ) public class User { // 实体类内容 }
这里的columns数组要和存储过程返回的列名完全对应,targetClass是你的非实体POJO,同时要确保POJO有对应参数顺序和类型的构造函数:
public class UserStatsPojo { private Long userId; private Integer totalOrders; private LocalDateTime lastLogin; // 构造函数参数顺序要和@ColumnResult的顺序完全一致 public UserStatsPojo(Long userId, Integer totalOrders, LocalDateTime lastLogin) { this.userId = userId; this.totalOrders = totalOrders; this.lastLogin = lastLogin; } // getter方法 }
步骤2:在NamedNativeQuery中引用这个映射
修改你的@NamedNativeQuery,指定resultSetMapping为上面定义的映射名称:
@NamedNativeQuery( name = "User.getUserStats", query = "CALL get_user_stats(:userId)", resultSetMapping = "UserStatsMapping" )
3. 确保列别名与POJO属性名完全匹配(如果用setter注入)
如果你之前是依赖无参构造函数+setter方法来注入结果,那要确保存储过程返回的列别名和POJO的属性名完全一致(包括大小写,部分数据库对大小写敏感)。比如POJO属性是userId,存储过程返回的列别名必须是user_id(对应下划线转驼峰的命名策略)或者直接是userId,具体取决于你的Hibernate命名配置。
如果存储过程返回的列名和POJO属性名不匹配,可以在查询中手动指定别名(不同数据库语法可能有差异,需按需调整):
CALL get_user_stats(:userId) AS user_id, total_orders, last_login
4. 检查Hibernate命名策略配置
Spring Boot 2.0中默认的Hibernate命名策略从SpringNamingStrategy改成了SpringPhysicalNamingStrategy,这个策略会将数据库列名的下划线自动转成驼峰式的属性名。如果你之前依赖旧的命名策略,可能需要在application.properties中显式配置:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
不过更推荐的还是用显式的结果集映射,避免依赖命名策略的隐式转换。
内容的提问来源于stack exchange,提问作者Erce Tilav

