SpringBoot JPA调用deleteById报Unknown Source空指针异常
SpringBoot集成JPA操作PostgreSQL测试场景空指针异常问题解答
1. 堆栈中"Unknown Source"的含义
这个标记是JVM生成堆栈轨迹时的标准提示,不属于错误信息,出现原因分两种:
- 对应类是JDK动态代理在运行时动态生成的字节码,比如堆栈里的
$Proxy170、$Proxy155都是Spring AOP为Repository生成的代理对象,这类类没有对应的.java源文件,编译时也不会保留行号调试信息,自然无法显示具体代码行 - 普通类出现该标记,一般是编译时未加
-g参数生成调试信息,class文件中缺失源文件、行号映射
你看到的at jdk.proxy2/jdk.proxy2.$Proxy170.deleteById(Unknown Source)是JDK动态代理的正常表现,不是异常根源,不需要在这行上排查问题。
2. 空指针异常的触发根因
顺着堆栈往上找,真正的报错点在最顶部:
java.lang.NullPointerException at java.base/java.lang.Class.isAssignableFrom(Native Method) at com.vladmihalcea.hibernate.type.json.internal.JsonTypeDescriptor.fromString(JsonTypeDescriptor.java:104)
异常和Spring Data JPA的Repository代理无关,是你引入的hibernate-types(Vlad Mihalcea开发的Hibernate JSON类型扩展库)在反序列化JSON字段时抛出的,具体逻辑和现象对应关系如下:
deleteById的底层执行逻辑是先通过ID查询出完整实体,再执行删除操作,查询阶段需要把数据库中存储的JSON字段值反序列化为实体类中对应的Java对象,这一步会调用到报错的JsonTypeDescriptor逻辑- 插入操作能正常执行,是因为插入走的是Java对象到JSON字符串的序列化路径,目标类型全程明确,不会触发类型缺失问题
- 单条插入后立刻删除报错、批量插入10条以上删除正常的特殊现象,是因为
@BeforeAll/@AfterAll搭配TestInstance(Lifecycle.PER_CLASS)时,setUp保存实体和tearDown删除实体不在同一个持久化上下文中:保存时的实体类型元数据没有被跨上下文缓存,反序列化JSON字段时拿不到目标Java类型,传入Class.isAssignableFrom的参数为null就触发了NPE;批量操作多次后持久化上下文会加载并缓存对应实体的类型映射,后续删除操作就可以正常执行。
常见触发场景和修复方案
- JSON字段类型配置不明确:检查实体类JSON字段的
@Type注解,不要只声明类型名,要明确绑定对应的Java类型,示例:// 容易出问题的写法 @Type(type = "jsonb") @Column(columnDefinition = "jsonb") private UserExtraInfo extraInfo; // 修复后的写法 @Type( type = "jsonb", parameters = @Parameter(name = "classType", value = "com.example.entity.UserExtraInfo") ) @Column(columnDefinition = "jsonb") private UserExtraInfo extraInfo; - 依赖版本不兼容:检查
hibernate-types版本和当前Hibernate版本对齐,Hibernate 5.6+使用hibernate-types-55依赖,Hibernate 6+使用hibernate-types-60依赖,不要跨大版本混用,避免老版本存在的跨会话类型元数据丢失bug - 测试事务未对齐:给测试类添加
@Transactional注解,让setUp、测试方法、tearDown运行在同一个事务和持久化上下文中,避免跨会话读取的元数据丢失问题。
内容的提问来源于stack exchange,提问作者Ville Myrskyneva
相关产品推荐
相关产品推荐

