如何避免在调用toString()时初始化HibernateProxy?

时间:2017-11-22 19:25:59

标签: hibernate jpa

我有以下映射:

作者:

@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()行也会导致Author实体初始化。 有没有办法避免它?

Spring boot版本:1.5.8.RELEASE

Hibernate版本:5.0.12.Final

1 个答案:

答案 0 :(得分:2)

toString() final解决了这个问题。

<强>背景

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

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

生成的代理确实实现了javassist.util.proxy.ProxyObject接口,该接口具有Hibernate用来设置org.hibernate.proxy.pojo.javassist.JavassistLazyInitializersetHandler(MethodHandler)方法(后者拦截方法调用(包括toString())。

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

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

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

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

final方法未被拦截,因此将final修饰符添加到toString()将解决此问题。

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

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