java 在Hibernate中启用LazyInitialization增强后出现Assert错误

qyuhtwio  于 2023-01-29  发布在  Java
关注(0)|答案(1)|浏览(127)

发布这个,因为它可能会帮助其他人在类似的情况。
我们正在将一个项目从Hibernate 3.6.11升级到5.4.25,其中一些实体Map为延迟加载属性。在Hibernate 3.6.11中,这需要使用Ant org.hibernate.tool.instrument.javassist.InstrumentTask。在Hibernate 5中,这已经被一个完全不同的EnhancementTask所取代(使用enableLazyInitialization=true)。
在这种新方法中,我们发现了一些错误

java.lang.AssertionError
    at org.hibernate.engine.internal.AbstractEntityEntry.overwriteLoadedStateCollectionValue(AbstractEntityEntry.java:334)
    at org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty(AbstractEntityPersister.java:1144)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.lambda$loadAttribute$0(LazyAttributeLoadingInterceptor.java:104)
    at org.hibernate.bytecode.enhance.spi.interceptor.EnhancementHelper.performWork(EnhancementHelper.java:130)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.loadAttribute(LazyAttributeLoadingInterceptor.java:76)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.fetchAttribute(LazyAttributeLoadingInterceptor.java:72)
    at org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor.handleRead(LazyAttributeLoadingInterceptor.java:53)
    at org.hibernate.bytecode.enhance.spi.interceptor.AbstractInterceptor.readObject(AbstractInterceptor.java:153)
    at com.sample.entity.OrderModel.$$_hibernate_read_orderpositions(OrderModel.java)
    at com.sample.entity.OrderModel.getOrderPositions(OrderModel.java:1537)

这发生在序列中
1.加载实体OrderModel
1.在OrderModel示例上运行预加载模式,但关键是OrderModel.getOrderPositions()未预加载
1.在事务外部,更改OrderModel示例
1.在新事务中,保存OrderModel示例
1.仍然在事务中,迭代OrderModel.getOrderPositions()
当访问集合时,将发生上述堆栈跟踪。

ffx8fchx

ffx8fchx1#

原因是最终运行的DefaultSaveOrUpdateEventListener. performUpdate(

persistenceContext.addEntity(
        entity,
        ( persister.isMutable() ? Status.MANAGED : Status.READ_ONLY ),
        null, // <-- this is the 'loadedState'
        key,
        persister.getVersion( entity ),
        LockMode.NONE,
        true,
        persister,
        false
);

因此将创建一个loadedState为null的EntityEntry。稍后,当LazyAttributeLoadingInterceptor加载该集合并尝试替换该属性的已加载状态时,

assert loadedState != null;

火灾。
我不确定这是否是Hibernate的bug,或者我们的代码只是以Hibernate不支持的方式加载/管理实体。

public class LazyAttributeSaveOrUpdateEventListener implements SaveOrUpdateEventListener {

    public static final LazyAttributeSaveOrUpdateEventListener INSTANCE = new LazyAttributeSaveOrUpdateEventListener();
    private static final long serialVersionUID = 1L;
    private static final Log LOGGER = LogFactory.getLog(LazyAttributeSaveOrUpdateEventListener.class);

    private final transient Field loadedStateField;

    LazyAttributeSaveOrUpdateEventListener() {
        loadedStateField = ReflectionUtils.findField(AbstractEntityEntry.class, "loadedState");
    }

    @Override
    public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
        if (event.getEntity() instanceof PersistentAttributeInterceptable) {
            initializeLoadedState(event);
        }
    }

    private void initializeLoadedState(SaveOrUpdateEvent event) {
        final EntityEntry entry = getEntityEntry(event);
        final Object[] loadedState = entry.getLoadedState();
        if (loadedState == null && entry.getStatus() != Status.READ_ONLY) {
            // if accessing a lazy-loaded collection on this entity, org.hibernate.persister.entity.AbstractEntityPersister.initializeLazyProperty()
            // will try to re-set the loaded state, resulting in an AssertionError if loadedState is null
            LOGGER.debug("Got empty loaded state for " + event.getEntity());
            EntityPersister persister = entry.getPersister();
            Object[] stubLoadedValue = ArrayHelper.filledArray(
                null,
                Object.class,
                persister.getPropertyTypes().length);
            ReflectionUtils.makeAccessible(loadedStateField);
            ReflectionUtils.setField(loadedStateField, entry, stubLoadedValue);
        }
    }

    private EntityEntry getEntityEntry(SaveOrUpdateEvent event) {
        return Optional.ofNullable(event.getEntry())
            .orElseGet(() -> {
                final PersistenceContext persistenceContext = event.getSession().getPersistenceContextInternal();
                return persistenceContext.getEntry(event.getEntity());
            });
    }

}

并将其注册为

eventListenerRegistry.appendListeners(EventType.SAVE_UPDATE, LazyAttributeSaveOrUpdateEventListener.INSTANCE);

我曾担心创建一个空初始化的加载状态会带来负面影响,但到目前为止一切似乎都正常。

相关问题