如何在其上调用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;
}

以下代码演示了问题:

@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=?

author.toString();行会导致作者实体初始化,即使没有覆盖toString()方法。有没有办法避免它?

春季启动版本:1.5.8.Release

Hibernate版本:5.0.12.Final

制作toString() final解决了问题。

背景:

我花了一些时间弄清楚发生了什么。这是我发现的

Hibernate使用Javassist进行运行时代理生成。

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

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

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

但是,在冬眠之前,它初始化了代理(AbstractLazyInitializer):

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

final未截获,因此将final修饰符添加到toString()将解决该问题。

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

请让我知道我是否错了还是有更好的解决方案

最新更新