Hibernate使用Oracle序列作为实体ID调用save方法抛出PropertyAccessException异常
问题根因
该异常是典型的Oracle NUMBER类型与Java Long类型映射不匹配导致的伪错误提示。Oracle序列返回的默认值类型是BigDecimal,如果你的MY_ID字段在数据库中没有指定明确的精度,Hibernate默认会将无精度的NUMBER类型映射为BigDecimal,反射赋值给Long类型的myid字段时类型转换失败,异常被包装后就会出现「无法给Long字段赋值null」的错误提示,实际和null值无关。
可行解决方案
方案1:明确指定ID字段的映射精度(推荐)
修改实体类的@Column注解,明确指定字段的数据库类型精度,让Hibernate直接将映射类型匹配为Long:
@Id @SequenceGenerator(name="seqName", sequenceName="myschema.mysequence",allocationSize=1) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seqName") @Column(name = "MY_ID", columnDefinition = "NUMBER(19,0)") // 新增columnDefinition配置,19为Long类型最大支持精度 private Long myid;
配置后Hibernate会自动完成类型转换,反射赋值不会再报错,直接调用session.save()即可正常返回生成的主键。
方案2:原生SQL插入返回主键(适配现有逻辑)
如果你需要继续用原生SQL插入,可以利用Oracle的RETURNING子句直接返回生成的主键,无需提前查询序列:
Query query = session.createNativeQuery("INSERT INTO myschema.MYTABLE (MY_ID, someotherfields)" + " VALUES (myschema.mysequence.nextval, :someotherfieldvalues) RETURNING MY_ID"); // 绑定业务字段参数 query.setParameter("someotherfieldvalues", yourValue); // 执行插入并获取返回的主键 Long generatedId = ((BigDecimal) query.getSingleResult()).longValue();
方案3:批量插入性能优化
如果需要批量插入数千条数据,在Hibernate配置文件中添加如下参数即可大幅提升save()方法的批量插入性能:
# 每100条SQL做一次批量提交,可根据实际场景调整大小 hibernate.jdbc.batch_size=100 hibernate.order_inserts=true hibernate.jdbc.batch_versioned_data=true
配置开启后批量插入性能相比单次查询序列再插入可提升10倍以上。
内容的提问来源于stack exchange,提问作者Gadou
相关产品推荐
相关产品推荐

