为何使用@Formula查询关联实体布尔值时始终返回true?
这是个很典型的Hibernate类型映射问题,我来帮你理清背后的原因:
核心问题根源
你的问题出在Hibernate对@Formula返回值的类型转换逻辑上,具体和数据库BIT类型的处理有关:
直接查询BIT字段的问题
当你在@Formula中直接写select si.published from ...时,数据库返回的是BIT类型的原始值(比如MySQL中BIT(1)存储的是二进制0/1)。而Hibernate在处理@Formula的原生SQL结果时,并没有沿用实体属性(比如ServiceItem.published)的类型转换规则——后者是通过@Column注解明确了和BIT类型的映射关系,Hibernate知道要把二进制值转换成Java boolean。但@Formula的结果是原生查询返回的,Hibernate可能会把非空的BIT值统一判定为true,忽略其实际的二进制值。case语句修复的原因
当你改用case when si.published = true then true else false end时,相当于在SQL层面显式返回了数据库的布尔字面量(true/false),而非原始的BIT二进制值。这时候Hibernate能正确识别这些字面量,并映射为对应的Java boolean类型,所以结果符合预期。
是Bug还是预期行为?
这更偏向于特定数据库方言下的预期行为,而非Hibernate的bug:
- @Formula是Hibernate的扩展特性,并非JPA标准,它执行的是原生SQL查询,Hibernate不会自动为其应用实体属性的类型转换器。
- 不同数据库对BIT类型的存储和返回格式有差异(比如SQL Server的BIT和MySQL的BIT处理逻辑不同),Hibernate无法在所有场景下自动适配原生SQL返回的BIT值到Java boolean。
额外优化建议
如果你不想写case语句,也可以尝试在@Formula中显式转换类型,比如针对MySQL可以用:
@Formula("(select cast(si.published as boolean) from service_item si join offer o on si.id = o.service_item_id where o.id = id)")
不过这种写法依赖数据库方言,通用性不如case语句。
内容的提问来源于stack exchange,提问作者Michail Michailidis

