【问题标题】:Hibernate: initialize vs unproxy休眠:初始化与取消代理
【发布时间】:2019-09-02 07:31:09
【问题描述】:

我遇到了多对多问题

@ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
@JoinTable(
    name = "PORTFOLIO_USER_PERMS",
    joinColumns = { @JoinColumn(name = "USERNAME") },
    inverseJoinColumns = { @JoinColumn(name = "PORTFOLIO_ID"), }
)
private List<Portfolio> sharedPortfolios = new ArrayList<>();

我有一种情况,其中一个 sharedPortfolios 一个元素是代理,另一个元素具有一个代理属性(由于一对一的关系,actulay 是同一个对象)。此列表通常返回到 Controller 方法,然后将其转换为相应的 DTO。您可能会怀疑,带有代理对象的单个属性会导致 LazyInitializationException。

这意味着在服务中,我需要遍历列表中的每个元素,以查找任何可能是代理和非代理的属性,使用 Hibernate 实用程序:Hibernate.unproxy(...) 之前它被传递给控制器​​。

我的问题:

1)Hibernate.initialize和Hibernate.unproxy有什么区别?

初始化代理对象并不能解决问题。

2) 为什么其中一个元素是代理,而其他元素不是?

3) 还有什么比手动遍历这个列表和所有属性并搜索代理对象更好的方法?

非常感谢。

最好的问候。

【问题讨论】:

  • fetch = FetchType.EAGER
  • 非常感谢。即使它解决了问题,它也有性能问题。除非确实有必要,否则我不需要从数据库中检索该列表。
  • 您如何检索您的实体?有不同的方法可以告诉 Hibernate 急切地获取关系。例如,使用 HQL:from Foo foo join fetch foo.sharedPortfolios where blablabla
  • 实际上我没有使用查询,只是 sharedPortfolios 的 getter。管理查询和列表创建的是休眠。我应该在我的 Spring JpaRepository 上创建一个方法吗?
  • 那么您是如何获得包含sharedPortfolios 关系的实体的呢?那是您可以“在真正需要时”检索关系的地方

标签: java hibernate


【解决方案1】:

要回答您的第一个问题,您可以查看两种方法的实现:

    /**
     * Unproxies a {@link HibernateProxy}. If the proxy is uninitialized, it automatically triggers an initialization.
     * In case the supplied object is null or not a proxy, the object will be returned as-is.
     *
     * @param proxy the {@link HibernateProxy} to be unproxied
     * @return the proxy's underlying implementation object, or the supplied object otherwise
     */
    public static Object unproxy(Object proxy) {
        if ( proxy instanceof HibernateProxy ) {
            HibernateProxy hibernateProxy = (HibernateProxy) proxy;
            LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer();
            return initializer.getImplementation();
        }
        else {
            return proxy;
        }
    }

和

    /**
     * Force initialization of a proxy or persistent collection.
     * <p/>
     * Note: This only ensures initialization of a proxy object or collection;
     * it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
     *
     * @param proxy a persistable object, proxy, persistent collection or <tt>null</tt>
     * @throws HibernateException if we can't initialize the proxy at this time, eg. the <tt>Session</tt> was closed
     */
    public static void initialize(Object proxy) throws HibernateException {
        if ( proxy == null ) {
            return;
        }

        if ( proxy instanceof HibernateProxy ) {
            ( (HibernateProxy) proxy ).getHibernateLazyInitializer().initialize();
        }
        else if ( proxy instanceof PersistentCollection ) {
            ( (PersistentCollection) proxy ).forceInitialization();
        }
        else if ( proxy instanceof PersistentAttributeInterceptable ) {
            final PersistentAttributeInterceptable interceptable = (PersistentAttributeInterceptable) proxy;
            final PersistentAttributeInterceptor interceptor = interceptable.$$_hibernate_getInterceptor();
            if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
                ( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( proxy, null );
            }
        }
    }

对于实体代理 (HibernateProxy) 的实例,这两种方法的作用基本相同,唯一的区别是 unproxy 返回未代理的实体。 从我目前的测试来看,使用返回值还是使用原始引用应该没有区别。

initialize 也适用于持久性集合 (PersistentCollection)。当您需要初始化 ToMany-Relation 时,例如在您的问题中,您将需要使用此方法。

初始化代理对象并不能解决问题。

您需要初始化集合sharedPortfolios,而不仅仅是包含该集合的对象:

Hibernate.initialize(entity.sharedPortfolios)

或者,您可能希望JOIN FETCH 查询中的关系或使用实体图以避免事后进行初始化并避免额外的查询。

【讨论】:

    猜你喜欢
    • 2010-09-25
    • 1970-01-01
    • 2013-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-28
    • 2013-01-10
    相关资源
    最近更新 更多