Spring Data JPA操作Oracle报ORA-01400无法插入NULL到ID字段如何解决
错误原因
- 自定义原生INSERT未触发JPA自动字段填充:你在实体类上配置的
@GeneratedValue主键生成策略、@CreatedDate审计字段注解,仅在调用JPA提供的标准save(实体对象)方法时才会生效。你自行编写的原生插入SQL不会被JPA解析处理,不会自动填充主键ID、创建时间两个必填字段。 - Oracle与MySQL主键自增逻辑差异:MySQL的ID字段只要设置了
AUTO_INCREMENT属性,INSERT语句不指定ID时会自动生成有效值;但Oracle的序列、IDENTITY自增(仅12c及以上版本支持)默认不会隐式调用,除非建表时为ID字段指定默认值为序列的nextval,否则不指定ID插入就会触发非空约束报错。 - 未处理
CREATED_DATE字段:你当前的INSERT语句也没有提供创建时间字段的值,即使解决了ID报错,后续也会触发CREATED_DATE字段的非空约束报错。
解决方案
方案1:改用JPA标准save方法(推荐)
无需修改原有实体配置,调整业务代码逻辑即可,是兼容性最好的方案:
- 先删除你在Repository中自定义的原生save方法,直接使用JpaRepository自带的
save(T entity)方法。 - 修改业务代码,构造实体对象后调用标准save方法:
// 原有读取Excel的逻辑不变 // 替换原有的this.userDetailRepository.save(phoneNumber, textMessage); UserDetail userDetail = new UserDetail(); userDetail.setPhoneNumber(phoneNumber); userDetail.setTextMessage(textMessage); // 如果你需要@CreatedDate自动生效,需要在项目启动类添加@EnableJpaAuditing注解,同时在UserDetail类上添加@EntityListeners(AuditingEntityListener.class) // 若未开启JPA审计,手动设置创建时间即可 userDetail.setCreatedDate(LocalDateTime.now()); userDetailRepository.save(userDetail);
方案2:修改原生INSERT适配Oracle规则
如果你必须使用原生SQL实现插入,可以二选一调整:
方式A:SQL中显式补全必填字段
修改Repository中的INSERT语句,显式调用序列生成ID、添加创建时间:
@Modifying @Query(value = "INSERT INTO test (INFO_ID, PHONE_NUMBER, TEXT_MESSAGE, CREATED_DATE) VALUES (your_sequence_name.nextval, :phoneNumber, :textMessage, SYSTIMESTAMP)", nativeQuery = true) @Transactional void save(@Param("phoneNumber") String phoneNumber, @Param("textMessage") String textMessage);
注意把语句中的your_sequence_name替换为你Oracle数据库中实际创建的序列名。
方式B:给表字段添加默认值
如果你不想修改SQL,可以直接调整Oracle表结构,给两个必填字段设置默认值(仅Oracle 12c及以上版本支持):
ALTER TABLE test MODIFY INFO_ID DEFAULT your_sequence_name.nextval; ALTER TABLE test MODIFY CREATED_DATE DEFAULT SYSTIMESTAMP;
配置完成后原有原生INSERT语句无需修改即可正常运行。
额外注意事项
你当前的Excel读取逻辑存在逻辑问题:遍历所有行、所有单元格时,phoneNumber和textMessage会被后续单元格覆盖,最终只会存入Excel最后一行的有效数据。如果你需要批量存入所有行的内容,需要将存库逻辑放到行遍历循环内,每遍历完一行就执行一次存库操作。
内容的提问来源于stack exchange,提问作者iwritecomeinmymind
相关产品推荐
相关产品推荐

