com.caucho:resin:4.0.66与jakarta.persistence-api:2.2.3兼容问题及NPE求助
问题根源
使用com.caucho:resin:4.0.66搭配jakarta.persistence:jakarta.persistence-api:2.2.3时,com.caucho.amber.manager.AmberPersistenceProvider的getProviderUtil()方法未完成实现,直接返回null。当Apache BVal的JPATraversableResolver调用javax.persistence.Persistence$PersistenceUtilImpl.isLoaded()时,因provider.getProviderUtil()为null触发NullPointerException(NPE),栈跟踪信息如下:
Caused by: java.lang.NullPointerException at javax.persistence.Persistence$PersistenceUtilImpl.isLoaded(Persistence.java:145) at org.apache.bval.jsr303.resolver.JPATraversableResolver.isReachable(JPATraversableResolver.java:34) at org.apache.bval.jsr303.resolver.DefaultTraversableResolver.isReachable(DefaultTraversableResolver.java:60) at org.apache.bval.jsr303.resolver.CachingTraversableResolver.isReachable(CachingTraversableResolver.java:82) at org.apache.bval.jsr303.ConstraintValidation.isReachable(ConstraintValidation.java:241) ... 37 more
触发NPE的核心代码(PersistenceUtilImpl.isLoaded):
for (PersistenceProvider provider : providers) { LoadState loadstate = provider.getProviderUtil().isLoadedWithoutReference(entity, attributeName); <<== LINE 145 if(loadstate == LoadState.LOADED) { return true; } else if (loadstate == LoadState.NOT_LOADED) { return false; } // else continue }
AmberPersistenceProvider的未实现方法:
@Override public ProviderUtil getProviderUtil() { // TODO Auto-generated method stub return null; }
可行解决方案
1. 自定义PersistenceProvider包装类
实现代理类包装AmberPersistenceProvider,重写getProviderUtil()返回合法的ProviderUtil实例:
public class FixedAmberPersistenceProvider implements PersistenceProvider { private final PersistenceProvider delegate; public FixedAmberPersistenceProvider(PersistenceProvider delegate) { this.delegate = delegate; } // 代理其他所有PersistenceProvider方法,直接调用delegate对应实现 @Override public EntityManagerFactory createEntityManagerFactory(String emName, Map map) { return delegate.createEntityManagerFactory(emName, map); } // 其余PersistenceProvider方法同理代理... @Override public ProviderUtil getProviderUtil() { return new ProviderUtil() { @Override public LoadState isLoadedWithoutReference(Object entity, String attributeName) { // 默认返回UNKNOWN,让遍历逻辑继续;也可根据Amber的实体加载逻辑自定义实现 return LoadState.UNKNOWN; } @Override public LoadState isLoadedWithReference(Object entity, String attributeName) { return LoadState.UNKNOWN; } @Override public LoadState isLoaded(Object entity) { return LoadState.UNKNOWN; } }; } }
通过SPI机制替换原实现:在项目的META-INF/services/javax.persistence.spi.PersistenceProvider文件中,写入自定义类的全限定名,替换原com.caucho.amber.manager.AmberPersistenceProvider。
2. 替换Apache BVal的TraversableResolver
修改BVal配置,禁用依赖JPA的JPATraversableResolver,改用默认解析器:
- 在
bval.properties配置文件中添加:
org.apache.bval.jsr303.traversableResolver=org.apache.bval.jsr303.resolver.DefaultTraversableResolver
也可以自定义一个不依赖JPA的TraversableResolver实现,彻底避免调用PersistenceUtil.isLoaded()方法。
3. 升级Resin或更换JPA实现
- 检查Resin 4.0.x后续版本(如4.0.67及以上)是否修复了Amber的
getProviderUtil()未实现问题; - 若无法升级Resin,可替换JPA实现为Hibernate、EclipseLink等成熟框架,这些实现均正确完成了
getProviderUtil()方法的实现。
内容的提问来源于stack exchange,提问作者jluehe

