Hibernate使用@Convert注解持久化时为何会执行额外更新?
问题:Hibernate保存带@Convert注解实体时出现额外Update操作
在使用Hibernate保存带有@Convert注解字段的简单实体时,执行insert操作后总会紧跟一条参数与insert完全相同的update操作,无法理解为何会出现这条额外查询,是否本该如此?若确实如此,原因是什么?
相关代码
待保存实体
@Entity public class Building { @Id @GeneratedValue private Long id; @Convert(converter = AddressConverter.class) private Address address; // getter、setter等省略 }
需要转换的Address类
public class Address { private String street; private String city; // getter、setter等省略 }
转换器实现
@Converter class AddressConverter implements AttributeConverter<Address, String> { @Override public String convertToDatabaseColumn(Address attribute) { return attribute.getStreet() + " " + attribute.getCity(); } @Override public Address convertToEntityAttribute(String dbData) { String[] data = dbData.split(" "); Address address = new Address(); address.setStreet(data[0]); address.setCity(data[1]); return address; } }
执行日志
18:52:14.959 [main] DEBUG org.hibernate.SQL - insert into building (address, id) values (?, ?) 18:52:14.960 [main] TRACE org.hibernate.orm.jdbc.bind - binding parameter [1] as [VARCHAR] - [test-street test-city] 18:52:14.961 [main] TRACE org.hibernate.orm.jdbc.bind - binding parameter [2] as [BIGINT] - [1] 18:52:14.962 [main] DEBUG org.hibernate.SQL - update building set address=? where id=? 18:52:14.964 [main] TRACE org.hibernate.orm.jdbc.bind - binding parameter [1] as [VARCHAR] - [test-street test-city] 18:52:14.964 [main] TRACE org.hibernate.orm.jdbc.bind - binding parameter [2] as [BIGINT] - [1]
原因分析与解决方法
这种额外的update是Hibernate的脏检查机制导致的,核心问题出在转换器的实现上:
- Hibernate执行insert后,会将数据库中存储的字符串反向转换回
Address对象(通过convertToEntityAttribute方法),然后和内存中原先的Address对象做对比。 - 你的
convertToEntityAttribute方法每次都会新建一个Address实例,而Java中默认的对象对比是引用对比,两个不同的实例会被Hibernate判定为属性发生了变化,触发脏检查,进而执行update操作。
解决方法很简单:
- 给
Address类实现equals和hashCode方法,基于street和city这两个字段的值做比较,这样Hibernate就能正确识别出两个对象的内容是否一致,不会误判为脏数据,也就不会触发多余的update。
内容的提问来源于stack exchange,提问作者Piterowsky
相关产品推荐
相关产品推荐

