背景:最近在升级hibernate的版本,由hibernate5.x升级至hibernate6.x,记录下在查询数据时遇到的问题。
1.执行如下代码:
2.报错现象:
org.hibernate.query.sqm.InterpretationException: Error interpreting query [from IntentModel a where id=1 order by a.create_time ]; this may indicate a semantic (user query) problem or a bug in the parser [from IntentModel a where id=1 order by a.create_time ]
3.问题分析:
(1)根据错误信息可以确定表【IntentModel】是真实存在的,查看表结构,确实存在id和create_time列,所以可以明确不是表结构的问题。
(2)回看代码,就是一个根据条件查询数据的SQL,查询条件有两个,拆开一个一个执行,发现问题问题出现order by a.create_time语句上。
(3)由(2)中的结论得知create_time也是存在的,所以怀疑是不是实体类上的@Column(name = "create_time")是不是没生效,但很快又被我推翻了,如果注解没生效,那么表结构中应该没有create_time列,陷入了沉思~~~~
(4)拆开查询条件执行时,发现id列作为查询条件时都可以执行成功,create_time列作为查询条件确抛异常,所以很明确就是create_time的问题,我又试了试其他的列作为查询条件是否能成功,惊喜马上就要出现了,发现SQL中的列名和实体类属性名称一模一样的就可以成功,反之不一样的就会抛异常,大胆怀疑就是SQL中写的列名和数据库中的列名映射出了问题,可明明都是create_time,为啥会这样呀,于是我将SQL中的列名改成了属性名createTime【不是@Column(name = "create_time")中的name】,查询成功。
(5)问题解决后,我查询了相关资料后,发现hibernate5.x升级到hibernate6.x版本之后,SQL语句也发生了变化。
4.总结如下:
Hibernate 5.x版本中,使用@Column
注解时,Hibernate会使用@Column注解的name
属性作为列名来创建SQL查询。而在Hibernate 6.x版本中,Hibernate更倾向于使用实体类的属性名称作为SQL查询的列名。
这种变化可能是为了提高Hibernate与实体类之间的映射一致性,以及提高代码的可读性。Hibernate 6.x版本更加强调与JPA规范的一致性,因此在处理列名时,它更倾向于使用实体类的属性名称。
扩展
Hibernate 6.x对命名策略的调整:在Hibernate 6.x中,命名策略(naming strategy)的实现和应用有所调整。尽管你可能在实体类的属性上使用@Column(name = "column_name")来指定数据库列名,但Hibernate在构建HQL或原生SQL查询时,会根据配置的命名策略来转换实体属性名。默认情况下,Hibernate会将实体类中的Java命名风格(驼峰命名法)转换为数据库风格(下划线分隔的小写命名)。
HQL与SQL查询的差异:在使用HQL(Hibernate Query Language)查询时,你通常会直接引用实体类属性名,而Hibernate会自动处理属性名与数据库列名之间的映射关系。但在使用原生SQL查询时,你直接在查询语句中引用数据库列名,因此需要使用数据库中的实际列名,这可能与实体类属性名不一致。
在HQL(Hibernate Query Language)查询中,直接使用实体类的属性名是标准做法,因为Hibernate会根据配置的命名策略自动处理属性名到数据库列名的映射。这意味着在HQL中使用
firstName
而不是first_name
是推荐的,因为Hibernate会自动将驼峰命名的Java属性名转换为数据库中相应的下划线分隔的小写列名。如果Hibernate的命名策略被配置为将firstName
转换为first_name
,直接使用first_name
可能与命名策略的自动转换逻辑冲突,导致Hibernate无法正确解析查询。使用SQL查询:如果你的查询需求无法通过HQL表达,或者你确实需要直接操作数据库列名,可以考虑使用原生SQL查询。在Hibernate中,你可以通过createSQLQuery方法来执行原生SQL查询,这样就可以直接使用first_name数据库列名。