我有以下映射:
作者:
@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避免这种情况。但是,您是否真的需要触发初始化才能打印您的实体进行日志?
请让我知道我是否错了还是有更好的解决方案