本文介绍了在调用toString()时如何避免初始化HibernateProxy?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下映射:

作者:

@Entity
@Getter
@Setter
public class Author {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Access(PROPERTY)
  private Long id;
}

图书:

@Entity
@Getter
@Setter
public class Book {
  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Access(PROPERTY)
  private Long id;

  @ManyToOne(fetch = LAZY)
  private Author author;
}

以下代码演示了该问题:

Following code demonstrates the issue:

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
    @Autowired
    private EntityManagerFactory emf;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        saveBookAndAuthor();


        EntityManager em = emf.createEntityManager();
        Book book = em.find(Book.class, 1L);
        Author author = book.getAuthor();
        System.out.println(author.getClass());
        author.toString();
    }

    private void saveBookAndAuthor() {
        EntityManager entityManager = emf.createEntityManager();
        entityManager.getTransaction().begin();
        Author author = new Author();
        Book book = new Book();
        book.setAuthor(author);
        entityManager.persist(author);
        entityManager.persist(book);
        entityManager.getTransaction().commit();
        entityManager.close();
    }
}

这是日志的一部分:

class com.example.demo.Author_$$_jvst5e0_0
2017-11-22 22:12:56.671 DEBUG 9426 --- [           main] org.hibernate.internal.SessionImpl       : Initializing proxy: [com.example.demo.Author#1]
2017-11-22 22:12:56.671 DEBUG 9426 --- [           main] org.hibernate.SQL                        : select author0_.id as id1_0_0_ from author author0_ where author0_.id=?

即使未覆盖toString()方法,

author.toString();行也会导致Author实体初始化.有办法避免吗?

author.toString(); line causes the Author entity initialization even if the toString() method was not overridden.Is there a way to avoid it?

春季启动版本:1.5.8.RELEASE

Spring boot version: 1.5.8.RELEASE

休眠版本:5.0.12.Final

Hibernate version: 5.0.12.Final

推荐答案

使toString() final得以解决.

背景:

我花了一些时间弄清楚到底发生了什么.这是我发现的东西

I've spent some time to figure out what is really going on.Here is what I've found

Hibernate使用Javassist生成运行时代理.

Hibernate uses Javassist for runtime proxy generation.

生成的代理确实实现 javassist.util.proxy.ProxyObject 具有Hibernate使用的setHandler(MethodHandler)方法设置 org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer (依次拦截方法调用(包括toString()).

Generated proxies do implement javassist.util.proxy.ProxyObject interface which has setHandler(MethodHandler) method used by Hibernate to set org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer ( which in turn intercepts method calls (including toString()).

当调用toString()时,原始方法应继续( JavassistLazyInitializer.invoke(Object, Method, Method,Object[] ):

When toString() is invoked the original method should proceed (JavassistLazyInitializer.invoke(Object, Method, Method,Object[]):

if ( result == INVOKE_IMPLEMENTATION ) {
  Object target = getImplementation();
...

但是在此之前,Hibernate初始化代理( AbstractLazyInitializer ):

But before it Hibernate initializes the proxy (AbstractLazyInitializer):

@Override
public final Object getImplementation() {
    initialize();
    return target;
}

final方法不会被拦截,因此在toString()中添加final修饰符将解决此问题.

final methods are not intercepted, so adding final modifier to toString() will solve the issue.

但是请记住,如果您直接访问toString()字段并且未初始化代理,即使这些字段确实存在,您也会看到null.您可以使用getter来避免这种情况. 但是您真的需要触发初始化以打印您的实体进行记录吗?

But remember that if your toString() access fields directly and the proxy is not initialized you will see nulls even if these fields are really present.You can use getters in to avoid this. But do you really need to trigger initialization to print your entity to log?

如果我错了或者有更好的解决方案,请告诉我

Please let me know if I'm wrong or there is a better solution

这篇关于在调用toString()时如何避免初始化HibernateProxy?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-02 23:09