-->
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.  [ 5 posts ] 
Author Message
 Post subject: Cache behavior not what we expect
PostPosted: Tue Jan 23, 2007 5:08 am 
Newbie

Joined: Mon Aug 22, 2005 10:39 am
Posts: 7
Hi all,

We are using the @Cache annotation on our reference model to avoid some useless roundtrips to the database, with mixed results. Following is the source code for our model: a Grappe that has 0 or 1 target Store, many sub-Grappes and dependent Grappes; the Store also maintains the inverse relationship to its owning Grappe, if present. This information practically never changes.

What we expect:
1- loading a Store by its ID should be done only once, then retrieved from the cache; same for Grappe
2- loading a Grappe's target Store should be done only once, then retrieved from the cache
3- loading a Grappe's sub-Grappes should be done only once, then retrieved from the cache
4- loading a Grappe's dependent Grappes should be done only once, then retrieved from the cache
5- loading a Store's owning Grappe should be done only once, then retrieved from the cache

What we see:
- points 1 to 4 work fine
- point 5 doesn't seem to work; with org.hibernate.SQL=debug we keep seeing queries like the following:

Code:
select grappe0_.id as id1_6_, grappe0_.code as code1_6_, grappe0_.name as name1_6_, grappe0_.status as status1_6_, grappe0_.TARGET_STORE_ID as TARGET6_1_6_, grappe0_.PARENT_ID as PARENT5_1_6_, grappe0_.LOGISTICS_PARENT_ID as LOGISTICS7_1_6_, store1_.id as id2_0_, store1_.LOGALI_NUMBER as LOGALI2_2_0_, store1_.code as code2_0_, store1_.name as name2_0_, assortment2_.id as id0_1_, assortment2_.code as code0_1_, assortment2_.name as name0_1_, assortment2_.status as status0_1_, assortment2_.PARENT_ID as PARENT6_0_1_, assortment2_.LOGISTICS_PARENT_ID as LOGISTICS5_0_1_, assortment2_.TARGET_STORE_ID as TARGET7_0_1_, subassortm3_.PARENT_ID as PARENT6_8_, subassortm3_.id as id8_, subassortm3_.id as id0_2_, subassortm3_.code as code0_2_, subassortm3_.name as name0_2_, subassortm3_.status as status0_2_, subassortm3_.PARENT_ID as PARENT6_0_2_, subassortm3_.LOGISTICS_PARENT_ID as LOGISTICS5_0_2_, subassortm3_.TARGET_STORE_ID as TARGET7_0_2_, logisticsd4_.LOGISTICS_PARENT_ID as LOGISTICS5_9_, logisticsd4_.id as id9_, logisticsd4_.id as id0_3_, logisticsd4_.code as code0_3_, logisticsd4_.name as name0_3_, logisticsd4_.status as status0_3_, logisticsd4_.PARENT_ID as PARENT6_0_3_, logisticsd4_.LOGISTICS_PARENT_ID as LOGISTICS5_0_3_, logisticsd4_.TARGET_STORE_ID as TARGET7_0_3_, subgrappes5_.PARENT_ID as PARENT5_10_, subgrappes5_.id as id10_, subgrappes5_.id as id1_4_, subgrappes5_.code as code1_4_, subgrappes5_.name as name1_4_, subgrappes5_.status as status1_4_, subgrappes5_.TARGET_STORE_ID as TARGET6_1_4_, subgrappes5_.PARENT_ID as PARENT5_1_4_, subgrappes5_.LOGISTICS_PARENT_ID as LOGISTICS7_1_4_, logisticsd6_.LOGISTICS_PARENT_ID as LOGISTICS7_11_, logisticsd6_.id as id11_, logisticsd6_.id as id1_5_, logisticsd6_.code as code1_5_, logisticsd6_.name as name1_5_, logisticsd6_.status as status1_5_, logisticsd6_.TARGET_STORE_ID as TARGET6_1_5_, logisticsd6_.PARENT_ID as PARENT5_1_5_, logisticsd6_.LOGISTICS_PARENT_ID as LOGISTICS7_1_5_ from GRAPPE grappe0_ left outer join STORE store1_ on grappe0_.TARGET_STORE_ID=store1_.id left outer join ASSORTMENT assortment2_ on store1_.id=assortment2_.TARGET_STORE_ID left outer join ASSORTMENT subassortm3_ on assortment2_.id=subassortm3_.PARENT_ID left outer join ASSORTMENT logisticsd4_ on subassortm3_.id=logisticsd4_.LOGISTICS_PARENT_ID left outer join GRAPPE subgrappes5_ on grappe0_.id=subgrappes5_.PARENT_ID left outer join GRAPPE logisticsd6_ on subgrappes5_.id=logisticsd6_.LOGISTICS_PARENT_ID where grappe0_.TARGET_STORE_ID=?


Hibernate version:
Hibernate Core 3.2.0 GA
Hibernate EntityManager 3.2.0 GA
Hibernate Annotations 3.2.0 GA

Mapping documents:
Code:
@Entity(name = "STORE")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "storeCache")
public class Store implements Serializable, Identifiable {
   @Id
   @SequenceGenerator(name = "MAINSEQ", sequenceName = "SQ$MAIN")
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MAINSEQ")
   private long id;

   @Column(name = "LOGALI_NUMBER")
   private long number;

   private String code;
   private String name;

   @OneToOne(mappedBy = "targetStore", optional = true, fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
   private Grappe grappe;

   public Store() {}
...
}

Code:
@Entity
@Table(name = "GRAPPE")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "grappeCache")
public class Grappe implements Serializable, RegionalAccess {
   @Id
   @SequenceGenerator(name = "MAINSEQ", sequenceName = "SQ$MAIN")
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MAINSEQ")
   private long id;

   private String code;
   private String name;
   private boolean status = true;

   @OneToOne(fetch = FetchType.EAGER)
   @JoinColumn(name = "TARGET_STORE_ID")
   @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "grappeStoreCache")
   private Store targetStore = null;

   @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
   @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "subGrappesCache")
   private Set<Grappe> subGrappes = new HashSet<Grappe>();

   @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.EAGER)
   @JoinColumn(name = "PARENT_ID")
   private Grappe parent = null;

   @OneToMany(mappedBy = "logisticsParent", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
   @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "grappeLogDependentsCache")
   private Set<Grappe> logisticsDependents = new HashSet<Grappe>();

   @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.EAGER)
   @JoinColumn(name = "LOGISTICS_PARENT_ID")
   private Grappe logisticsParent = null;

   public Grappe() {}
...
}


Code between sessionFactory.openSession() and session.close():

Seems to happen when executing the following:

Code:
grappeDao.loadGrappeById(grappeId);


Full stack trace of any exception that occurs:

No exception

Name and version of the database you are using:

Oracle 9.2

The generated SQL (show_sql=true):

Seen above

Debug level Hibernate log excerpt:

Above

Does anyone have any idea why this query is being sent time and again, even though the Grappe class is marked as being Cached?

Thanks a bunch!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 23, 2007 8:00 am 
Newbie

Joined: Mon Aug 22, 2005 10:39 am
Posts: 7
Sure enough when setting the fetch type of all associations to FetchType.LAZY the problem disappears and requests hit the cache when expected. Of course we now have to take care of some LazyInitializationExceptions, but at least this will take some load off the database.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 24, 2007 4:53 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
note that @LazyToOne(FALSE) @Fetch(SELECT) works well in conjunction with a cached object and it avoids LIE
Same for collections: @LazyCollection

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 01, 2007 5:25 am 
Newbie

Joined: Mon Aug 22, 2005 10:39 am
Posts: 7
How should these annotations be used? Documentation on @LazyToOne and @Fetch is rather short and I couldn't find any example of their use.

I've tried different combinations but without much success. My latest try goes like this:

Code:
@Entity
@Table(name = "GRAPPE")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Grappe implements Serializable, RegionalAccess {
   @Id
   @SequenceGenerator(name = "MAINSEQ", sequenceName = "SQ$MAIN")
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MAINSEQ")
   private long id;

   @OneToOne(fetch = FetchType.LAZY)
   @LazyToOne(LazyToOneOption.FALSE)
   @Fetch(FetchMode.SELECT)
   @JoinColumn(name = "TARGET_STORE_ID")
   private Store targetStore = null;

   @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @LazyCollection(LazyCollectionOption.FALSE)
   @Fetch(FetchMode.SELECT)
   @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
   private Set<Grappe> subGrappes = new HashSet<Grappe>();

   @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
   @LazyToOne(LazyToOneOption.FALSE)
   @Fetch(FetchMode.SELECT)
   @JoinColumn(name = "PARENT_ID")
   private Grappe parent = null;

   @OneToMany(mappedBy = "logisticsParent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @LazyCollection(LazyCollectionOption.FALSE)
   @Fetch(FetchMode.SELECT)
   @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
   private Set<Grappe> logisticsDependents = new HashSet<Grappe>();

   @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.LAZY)
   @LazyToOne(LazyToOneOption.FALSE)
   @Fetch(FetchMode.SELECT)
   @JoinColumn(name = "LOGISTICS_PARENT_ID")
   private Grappe logisticsParent = null;

   ...
}

@Entity(name = "STORE")
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Store implements Serializable, Identifiable {
   @Id
   @SequenceGenerator(name = "MAINSEQ", sequenceName = "SQ$MAIN")
   @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MAINSEQ")
   private long id;

   @OneToOne(mappedBy = "targetStore", optional = true, fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
   @LazyToOne(LazyToOneOption.FALSE)
   @Fetch(FetchMode.SELECT)
   private Grappe grappe;

   ...
}


This approach still produces too many queries to the database and seems to cache the same object too often. What is wrong with this?

Thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jul 20, 2007 5:05 am 
Newbie

Joined: Fri Jul 20, 2007 4:52 am
Posts: 1
Quote:
What is wrong with this?

The problem with your code is that you write these two different annotations :
Code:
   @OneToOne(fetch = FetchType.LAZY)
   @LazyToOne(LazyToOneOption.FALSE)

The second one means the contrary and erease the first one. See this page :
http://www.hibernate.org/hib_docs/annotations/api/org/hibernate/annotations/LazyToOneOption.html
In fact, your are just doing a
Code:
   @OneToOne(fetch = FetchType.EAGER)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 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.