我有以下映射:
作者:
@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
答案 0 :(得分:2)
让toString()
final
解决了这个问题。
<强>背景强>
我花了一些时间来弄清楚究竟发生了什么。 这是我发现的
Hibernate
使用Javassist
进行运行时代理生成。
生成的代理确实实现了javassist.util.proxy.ProxyObject
接口,该接口具有Hibernate用来设置org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer
的setHandler(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来避免这种情况。 但是你真的需要触发初始化来打印你的实体进行记录吗?
如果我错了或者有更好的解决方案,请告诉我