-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: JPA getReference call always hits database
PostPosted: Thu Mar 19, 2015 5:54 am 
Newbie

Joined: Wed Jan 21, 2015 7:24 am
Posts: 6
Hi I am developing some examples on how to use JPA and Hibernate and I have found one thing that I am not sure why it is happening.

I have a typical application with two POJOS related with a onetomany relationship:

Code:
@OneToMany(mappedBy="movie", cascade = CascadeType.ALL, orphanRemoval = true) //<1>
private List<Comment> comments = new ArrayList<Comment>();


And the counterpart:

Code:
@ManyToOne
private Movie movie;


Then I have one stateless bean that creates a new comment to a known movie.

Code:
public void createComment(Long movieId, Comment comment) {
        Movie movie = entityManager.getReference(Movie.class, movieId);
        comment.setMovie(movie);
        entityManager.persist(comment);
}


And my Arquillian test looks like:

Code:
@Test
@ApplyScriptBefore("scripts/drop-referential-integrity.sql")
@UsingDataSet("datasets/movies-with-comments.yml")
@ShouldMatchDataSet("datasets/expected-movies-with-comments.yml")
public void shouldAddCommentPerformant() {
    Comment comment = new Comment();
    comment.setReview("must see");
    moviesService.createComment(1L, comment);
}


No secrets here. But when I execute the test I see next queries that are executed by hibernate:

Code:
Hibernate: select movie0_.id as id1_1_1_, movie0_.releasedYear as released2_1_1_, movie0_.title as title3_1_1_, moviedetai1_.id as id1_2_0_, moviedetai1_.directedBy as directed2_2_0_, moviedetai1_.producedBy as produced3_2_0_ from Movie movie0_ left outer join MovieDetail moviedetai1_ on movie0_.id=moviedetai1_.id where movie0_.id=?
Hibernate: insert into Comment (id, movie_id, review) values (default, ?, ?)


Why this select is executed? I thought that using getReference was exactly to avoid this. You can see full code at https://github.com/Scytl/jpaguide/tree/master/sources/hibernate-performance-example

Thank you so much for your help.


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Tue Mar 24, 2015 4:29 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
Both the Session.get (EntityManager.find) and Session.load (EntityManager.getReference) fire the same LoadEvent.

This is the logic that decides whether a Proxy should be used:

Code:
if ( !persister.hasProxy() ) {
   return load( event, persister, keyToLoad, options );
}

final PersistenceContext persistenceContext = event.getSession().getPersistenceContext();

// look for a proxy
Object proxy = persistenceContext.getProxy( keyToLoad );
if ( proxy != null ) {
   return returnNarrowedProxy( event, persister, keyToLoad, options, persistenceContext, proxy );
}


You can enable entity proxying using bytecode instrumentation or simply annotate your entity with:

@org.hibernate.annotations.Proxy(lazy = true)


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Tue Mar 24, 2015 4:33 am 
Newbie

Joined: Wed Jan 21, 2015 7:24 am
Posts: 6
Thank you so much, so in case of using JPA there is no way to avoid this hit but using Hibernate annotation or with instrumentation.

Ok thank you so much for your help because I was getting crazy with this behaviour.


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Tue Mar 24, 2015 5:03 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
If you want to stick to Hibernate you can even do it like this:

Code:
Movie movie = new Movie();
movie.setId(movieId);
comment.setMovie(movie);
entityManager.persist(comment);


Hibernate is able to set the FK accordingly even when you supply a transient skeleton entity, but this is not a JPA compliant feature, being Hibernate specific.


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Thu Apr 23, 2015 5:00 am 
Newbie

Joined: Wed Jan 21, 2015 7:24 am
Posts: 6
Hi sorry for the delay but I was involved in another tasks: I have tried what you told me but it doesn't work.

Debugging Hibernate events I have seen that method calls proxyOrLoad but when execute Object proxy = persistenceContext.getProxy(keyToLoad); it returns null. I am going to continue investigate, and I will keep you updated. Only notice you about this, maybe you know what's happening :)

Any idea?


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Thu Apr 23, 2015 7:00 am 
Newbie

Joined: Wed Jan 21, 2015 7:24 am
Posts: 6
Debugging I have seen that when I call getReference th eloadProxy is called correctly, but then when I set the updated value a select is executed.

The repo is still in https://github.com/Scytl/jpaguide with latest code. Maybe it is something related with Arquillian but int heory it should not be the case.


Top
 Profile  
 
 Post subject: Re: JPA getReference call always hits database
PostPosted: Thu Apr 23, 2015 8:30 am 
Hibernate Team
Hibernate Team

Joined: Thu Sep 11, 2014 2:50 am
Posts: 1628
Location: Romania
Actually it works as expected. This is my test case:

Code:
@Test
public void testGetPost() {
   doInTransaction(session -> {
      LOGGER.info("Load Post entity is cached after load");
      Post post = (Post) session.load(Post.class, 1L);
   });

   doInTransaction(session -> {
      LOGGER.info("Check Post entity is cached after load");
      Post post = (Post) session.get(Post.class, 1L);
   });
}


and this is the output:

Code:
INFO  [Alice]: c.v.h.m.l.c.SecondLevelCacheTest - Load Post entity is cached after load
DEBUG [Alice]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection
DEBUG [Alice]: o.h.e.t.i.j.JdbcTransaction - re-enabling autocommit
DEBUG [Alice]: o.h.e.t.i.j.JdbcTransaction - initial autocommit status: true
DEBUG [Alice]: o.h.e.t.i.j.JdbcTransaction - disabling autocommit
INFO  [Alice]: c.v.h.m.l.c.SecondLevelCacheTest - Check Post entity is cached after load
DEBUG [Alice]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:1 Num:1 Query:{[select secondleve0_.id as id1_1_0_, secondleve0_.name as name2_1_0_, secondleve1_.id as id1_2_1_, secondleve1_.createdOn as createdO2_2_1_ from Post secondleve0_ left outer join PostDetails secondleve1_ on secondleve0_.id=secondleve1_.id where secondleve0_.id=?][1]}


As you can see, the load method, which is the same as entityManager.getReference() doesn't execute the proxy if we don;t access the entity.

The moment, the entity gets accessed:

Code:
@Test
public void testGetPost() {
   doInTransaction(session -> {
      LOGGER.info("Load Post entity is cached after load");
      Post post = (Post) session.load(Post.class, 1L);
                post.getName();
   });

   doInTransaction(session -> {
      LOGGER.info("Check Post entity is cached after load");
      Post post = (Post) session.get(Post.class, 1L);
   });
}


The select is being triggered by the proxy:

Code:
INFO  [Alice]: c.v.h.m.l.c.SecondLevelCacheTest - Load Post entity is cached after load
DEBUG [Alice]: n.t.d.l.SLF4JQueryLoggingListener - Name: Time:1 Num:1 Query:{[select secondleve0_.id as id1_1_0_, secondleve0_.name as name2_1_0_, secondleve1_.id as id1_2_1_, secondleve1_.createdOn as createdO2_2_1_ from Post secondleve0_ left outer join PostDetails secondleve1_ on secondleve0_.id=secondleve1_.id where secondleve0_.id=?][1]}


This is my test.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.