Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid initializing HibernateProxy when invoking toString() on it?

Tags:

hibernate

jpa

I have following mappings:

Author:

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

Book:

@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();
    }
}

Here is a part of logs:

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(); line causes the Author entity initialization even if the toString() method was not overridden. Is there a way to avoid it?

Spring boot version: 1.5.8.RELEASE

Hibernate version: 5.0.12.Final

like image 370
Denis Zavedeev Avatar asked Nov 02 '25 02:11

Denis Zavedeev


1 Answers

Making toString() final solved the issue.

Background:

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

Hibernate uses Javassist for runtime proxy generation.

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()).

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

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

But before it Hibernate initializes the proxy (AbstractLazyInitializer):

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

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

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

like image 54
Denis Zavedeev Avatar answered Nov 04 '25 17:11

Denis Zavedeev