2

I'm using the play framework and JPA and Hibernate.

I have several entities :

@Entity
public class Player extends Model {

    public enum Gender {

        MALE, FEMALE
    }

    @Required
    public String name;

    @Required
    public Gender gender;

    @Required
    public Long gold;
}

@Entity
public class Building extends Model {

    @Required
    @ManyToOne(fetch = FetchType.LAZY)
    public Player owner;

    @Required
    public String buildingType;

    @Required
    public Long buildingId;
}

@Entity
public class Stock extends Model {

    // Either a product type or a raw material
    @Required
    public Long goodId;

    @Required
    public Boolean isProduct;

    @Required
    public String image;
}

@Entity
public class Contract extends Model {

    @Required
    @ManyToOne(fetch = FetchType.LAZY)
    public Player player;

    @Required
    @ManyToOne(fetch = FetchType.LAZY)
    public Building building;

    @Required
    @ManyToOne(fetch = FetchType.LAZY)
    public Stock stock;

    @Required
    public Long ordersLeft;

    @Required
    public Long cyclesLeft;
}

With these entities I would like to retrieve all my Contract and their associated Building, Stock and Player

Here is the query I' using :

public static List<Contract> retrieveContractsForNewOrder() {
        return find("select distinct c from Contract c "
                + "left join fetch c.player "
                + "left join fetch c.stock "
                + "left join fetch c.building "
                + "where c.cyclesLeft = 0 and c.ordersLeft > 0").fetch();
    }

The query is working, I retrieve my list of Contract. However, the building and stock variables are loaded but the player variable is not (The class name in the Contract object is Player_$$_javassist_22 and it has a handler named JavassistLazyInitializer).

I don't know what I'm doing wrong and why Hibernate refuses to fetch the player model while it's fetching the other models ...

Thank you for your help

EDIT

After some tests the query executed by hibernate seems correct : all joins are there and all fields from all models are in the select.

However the results are strange : I'm only retrieving fields from Contract and Stock (not from Building and Player).

But my Building is loaded, why ? Because of the cache ? And why Building and Player does not appear in results ?

EDIT 2

I tried to execute the exact same request directly in MySQL and I'm retrieve all the variables I need, while Hibernate seems to skip some variables (they are not all in the results).

What's happening ?

EDIT 3

I tried to clear() the session cache before doing my query and guess what ? The Player is loaded ...

I tried to only load the Player using the left join fetch but it was still not working (but Building was loaded thanks to the cache). Player's fields were not in the results. However, as soon as I cleared the cache the Player's fields appeared in the results and the Player is loaded in my Contract.

Why clearing the cache solved the issue? Player was in the cache ? If so, why wasn't it loaded? I don't understand what happens, but I can't just clear the cache as I need to make a lot of other queries after this one and I need the cache for them.

Fabien Henon
  • 783
  • 15
  • 37
  • What's the generated SQL query? If you call `contract.getPlayer().getId()` on your retrieved contracts, do you see additional SQL queries being logged? I don't think that having a proxy and not an instance in the player field is a good indication that the player is not feteched. Your proxy might very well be initialized already. – JB Nizet Aug 30 '13 at 15:47
  • @JBNizet The generated query seems correct : there are all the needed joins and the select tries to retrieve all fields of all models. However the results are strange : I only retrieve fields from Contract and from Stock, not fields from the other models. Why am I retrieving only those 2 models? Why have I the building that is loaded? (because of hibernate cache?) I'll edit my question – Fabien Henon Aug 30 '13 at 22:52
  • Could you post the generated SQL and the SQL you manually wrote in order to compare each other ? – willome Sep 02 '13 at 09:29
  • @willome I copy/paste the requests, they are absolutely the same but with Hibernate there is no reference to Player and Building in the results while with MySQL I can see everything – Fabien Henon Sep 02 '13 at 09:33
  • By retrieving only those 2 models, do you mean that the returned Contract's referenced player and building are null or incomplete? How are you checking that they are not fetched or incomplete? JPA requires managed entities be cached in a first level cache and allows for a second level cache, so check that you are not merging in incomplete entities somehow corrupting the EM's cache, though it sounds like you are interpreting the existence of proxies as not being fetched. Check that another query without the fetch occurred so that contact exists in the cache with the proxy. – Chris Sep 03 '13 at 16:07
  • @Chris Building is loaded (all fields are filled) and Player is a javaassist class (hibernate proxy). I'm checking via the debugger. Actually Players are in the cache as when I access Player's field, no more query is executed. But with Players in the cache, I don't understand why I get proxy players and not loaded players – Fabien Henon Sep 03 '13 at 21:17
  • Have you accessed Contracts or Players previously in this EntityManager? If so, that is the instance you get back from subsequent queries. So if it had proxy instances, it should still reference proxy instances. Clearing the cache clears the instances read in previously. This should all be transparent to your application though - it shouldn't matter if you get a proxy or your application's instance. – Chris Sep 04 '13 at 13:40
  • @Chris Actually it matters for me as I'm trying to update my entities with a stateless session (for optimization purposes) and it does not work if it's a proxy (and I can't fetch my data from this stateless session as it does not have the cache which makes the fetch quicker in my case). However you're right, I'm retrieving these entities previously in the code. What's is weird is that I'm always fetching them using left join fetch, but maybe I missed something. I didn't know a proxy would be stored as-is in the cache. Maybe you can write this answer, so that I can mark it as accepted answer ;) – Fabien Henon Sep 04 '13 at 14:32

3 Answers3

4

JPA forces providers to cache managed entities read through the EntityManager to maintain object identity, so that each time you query for an entity or get a reference to it within that context, you obtain the same instance back. So if you read your entity in such a way that it returns a proxy instance early on, this proxy instance is what should remain in the cache until the EntityManager is cleared. All subsequent queries should return the same instance of the object back.

The solution would be to clear or close and reobtain a new EntityManager at key points, and then ensure that the query to load the objects as required is the first that executes in the context.

Chris
  • 20,138
  • 2
  • 29
  • 43
  • Thank you. Indeed, I loaded the entity before, and it was returned as a proxy. When clear the cache it's working, but as I can't clear it because I need it I'll fin a workaround – Fabien Henon Sep 06 '13 at 19:22
0

Try changing to FetchType.EAGER

JSS
  • 2,061
  • 1
  • 20
  • 26
  • This is not working better. But what is weird is that when I try to access the Player variable I have the correct values but no query is done by Hibernate (loaded from cache ?). Why is my Player the only model that is not loaded ? – Fabien Henon Sep 02 '13 at 09:54
0

Enum in Hibernate, persisting as an enum Focus on Accepted Answer.
And for hibernate related concepts like cache go through this tutorial.
http://www.youtube.com/playlist?list=PL4AFF701184976B25

Community
  • 1
  • 1
Niks
  • 885
  • 1
  • 7
  • 17
  • The Enum does not seem to be the problem because when I get rid of it in my model I still have the same issue : Player is not loaded (even when Player is the only left join fetch I make). And it is stored as an int in the database. Cache could be the issue ? – Fabien Henon Sep 03 '13 at 08:09
  • hey i dont know about mapping much.but you are having player as a member of building and your explicitly fetching player in your contract class is it possible??.Try removing player from building class or from contract class. – Niks Sep 03 '13 at 11:00
  • It still does not work. Clearing the cache makes it work but I can't clear the cache for the rest of my code :( – Fabien Henon Sep 03 '13 at 12:25