Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [eclipselink-users] Caching and Relational Objects

>> The cache will cache anything you read, so if you read it again (by primary key) you avoid accessing the database, and (by query) avoid rebuilding the object and its relationships.

If I understood this right, there is no way to prevent the database query (as in the case of using get) when using the "Query" objects, the cache only avoid the translation of tabular data to objects, correct?

On the other questions, I must have done a real bad job of explaining .. :)
Hopefully this will help (I am using eclipselink as my EJB3 persistence framework to a relational datastore)

Problem 1:
I get NullPointerException in my application due to entities that eclipselink returns when I retrieve (get or Query) them. The entities returned from eclipselink are objects it has cached (I figured this out by stepping into the eclipselink code). The entities in the cache have null values for attributes/fields which I know are constrained by the database schema to be not null, leading me to not do a null check and ultimately to a NPE

I tried to figure out how the entities got into the cache (with a bad state) and tracked it down to the saving of another entity having a relation with the one in bad state

I have a One-To-Many relationship between TSDATES - RSRHOUR, represented earlier by TimesheetPeriod and ResourceHour respectively. Saving (in this case it is a create or presist from your response) ResourceHour resulted in TimesheetPeriod being added to the eclipselink cache.

My code that resulted in TimesheetPeriod being added to the cache (Note : Timesheet period instance is already in the database, when creating ResourceHour, I just need to provide a relational object reference as RSRCHOUR has a non null foreign key constraint)

TimesheetPeriod tp = new TimesheetPeriod();
tp.setTimesheetPeriodId(Integer.valueOf(timesheetTask.getTsId())); // only setting the key field, ignoring other fields, as I am only using this for foreign key reference

ResourceHour hour = new ResourceHour();
hour.setTimesheetPeriod(tp); // sets the TimesheetPeriod on ResourceHour
m_hoursDao.save(hour);  // Create/Persist the resource hour

Starting with an empty cache for TimesheetPeriod prior to the persist call above, I end up with the following cache state after the persist

UnitOfWorkIdentityMap for: TimesheetPeriod
Key: [10]    Identity Hash Code: 20015579    Object: TimesheetPeriod[TimesheetPeriodId: 10 StartDate: null EndDate: null

Now when I load the TimesheetPeriod with TimesheetPeriodId (primary key @Id) 10, eclipselink returns the object in cache, with StartDate and EndDate null. My solution to fixing this was to replace the 2 lines of code

TimesheetPeriod tp = new TimesheetPeriod();
tp.setTimesheetPeriodId(Integer.valueOf(timesheetTask.getTsId())); // only setting the key field, ignoring other fields, as I am only using this for foreign key reference

with

TimesheetPeriod tp =m_timesheetPeriodDao.get(Integer.valueOf(timesheetTask.getTsId()));

this results in the object in the database being loaded into the cache. The rest of the code remains the same.

UnitOfWorkIdentityMap for: TimesheetPeriod
Key: [10]    Identity Hash Code: 20015579    Object: TimesheetPeriod[TimesheetPeriodId: 10 StartDate: 01/01/09 EndDate: 01/31/09


Now back to the questions
* What is the expected/accepted usage pattern with regards to cache, is the use case of creating objects in memory with only primary key fields invalid/incorrect?
   Should I be using

  TimesheetPeriod tp = new TimesheetPeriod();
  tp.setTimesheetPeriodId(Integer.valueOf(timesheetTask.getTsId()));
  ResourceHour hour = new ResourceHour();
  hour.setTimesheetPeriod(tp);
  m_hoursDao.save(hour);

  or

  TimesheetPeriod tp =m_timesheetPeriodDao.get(Integer.valueOf(timesheetTask.getTsId()));
  ResourceHour hour = new ResourceHour();
  hour.setTimesheetPeriod(tp);
  m_hoursDao.save(hour);

  or something else.

* Does eclipselink make use of the hashcode/equals implemented in domain objects (TimesheetPeriod) for its caching. I am guessing not as eclipse link seems to use primary key equality (via ClassDescriptor instances) and not the hashcode/equals combination for its cache (based on the code I stepped through).

Thank you for the reply.

-Bhaskar

On Thu, Feb 5, 2009 at 8:48 AM, James Sutherland <jamesssss@xxxxxxxxx> wrote:

I'm not sure what you are trying to do?

The cache will cache anything you read, so if you read it again (by primary
key) you avoid accessing the database, and (by query) avoid rebuilding the
object and its relationships.

What is your save() doing?  Are you calling persist()?  Persist is for new
objects, it inserts the object into the database.  You cannot insert an
object if it already exists.  If the object exists, just read the object
using find().

Are you trying to put an object into the EclipseLink cache from another
source?  Where did you get this object from?


Bhaskar Maddala wrote:
>
> Hello,
>
>    I have the following domain objects (retained only the relevant
> information - or what I thought to be relevant)
>
> @Entity
> @Table(name = "RSRCHOUR")
> public class ResourceHour
>   implements Serializable {
>     private Integer m_hourId;
>     private TimesheetPeriod m_timesheetPeriod;
>
>     @TableGenerator(name = "rsrchour_rsrc_hr_id", table = "NEXTKEY",
> pkColumnValue = "rsrchour_rsrc_hr_id", allocationSize = 1, pkColumnName =
> "KEY_NAME", valueColumnName = "KEY_SEQ_NUM")
>     @Id
>     @GeneratedValue(strategy = TABLE, generator = "rsrchour_rsrc_hr_id")
>     @Column(name = "RSRC_HR_ID")
>     public Integer getHourId() {
>         return m_hourId;
>     }
>     public void setHourId(Integer hourId){
>         m_hourId = hourId;
>     }
>
>     @OneToOne(fetch = FetchType.LAZY)
>     @JoinColumn(name="TS_ID", referencedColumnName="TS_ID")
>     public TimesheetPeriod getTimesheetPeriod() {
>         return m_timesheetPeriod;
>     }
>     public void setTimesheetPeriod(TimesheetPeriod timesheetPeriod) {
>         m_timesheetPeriod = timesheetPeriod;
>     }
> }
>
> @Entity
> @Table(name = "TSDATES")
> public class TimesheetPeriod
>   implements Serializable {
>     private Integer m_timesheetPeriodId;
>     private Date m_startDate;
>     private Date m_endDate;
>
>     @TableGenerator(name = "timesht_ts_id", table = "NEXTKEY",
> pkColumnValue
> = "timesht_ts_id", allocationSize = 1, pkColumnName = "KEY_NAME",
> valueColumnName = "KEY_SEQ_NUM")
>     @Id
>     @Column(name = "TS_ID", nullable = false, length = 10)
>     public Integer getTimesheetPeriodId() {
>         return m_timesheetPeriodId;
>     }
>     public void setTimesheetPeriodId(Integer timesheetPeriodId) {
>         m_timesheetPeriodId = timesheetPeriodId;
>     }
>
>     @Temporal(TemporalType.DATE)
>     @Column(name = "END_DATE", nullable = false)
>     public Date getEndDate(){
>         return m_endDate;
>     }
>     public void setEndDate(Date endDate){
>         m_endDate = endDate;
>     }
>
>     @Temporal(TemporalType.DATE)
>     @Column(name = "START_DATE", nullable = false)
>     public Date getStartDate(){
>         return m_startDate;
>     }
>     public void setStartDate(Date startDate){
>         m_startDate = startDate;
>     }
>
>     @Override
>     public int hashCode() {
>         return (m_timesheetPeriodId == null) ? 0 :
> m_timesheetPeriodId.intValue();
>     }
>     @Override
>     public boolean equals(Object obj) {
>         if (this == obj) {
>             return true;
>         }
>         if (obj == null) {
>             return false;
>         }
>         if (getClass() != obj.getClass()) {
>             return false;
>         }
>         final TimesheetPeriod other = (TimesheetPeriod)obj;
>         if (m_timesheetPeriodId == null) {
>             if (other.m_timesheetPeriodId != null) {
>                 return false;
>             }
>         }
>         else if (!m_timesheetPeriodId.equals(other.m_timesheetPeriodId)) {
>             return false;
>         }
>         return true;
>     }
> }
>
> // I exercise these domain objects as follows
> TimesheetPeriod tp = new TimesheetPeriod();
> tp.setTimesheetPeriodId(Integer.valueOf(timesheetTask.getTsId())); // only
> setting the key field, ignoring other fields
> ResourceHour hour = new ResourceHour();
> hour.setTimesheetPeriod(tp);
> m_hoursDao.save(hour);  // JPA call to save the domain object
>
> // assuming an initial cache state of empty, I end up with the following
> cache state
> UnitOfWorkIdentityMap for: TimesheetPeriod
> Key: [10]    Identity Hash Code: 20015579    Object:
> TimesheetPeriod[TimesheetPeriodId: 10 StartDate: null EndDate: null
>
> I then load the TimesheetPeriod using a Query, the timesheet period
> returned
> is the one from cache. I noticed that a "database query is issued" to load
> the actual database row and prior to using ObjectBuilder a check is made
> in
> the cache to retreive an existing object if available in the cache.
>
>>From the database schema I know that both startdate and enddate fields
> cannot be null. I therefore use them field values without a check which
> results in a NPE.
>
> I can think of only 1 fix which is to load the TimesheetPeriod rather than
> creating one in memory and setting the primary key as I have at this time.
>
> Questions:
> * What is the expected/accepted usage pattern with regards to cache, is
> the
> use case of creating objects in memory with only primary key fields
> invalid/incorrect?
> * When using a query to load from the database eclipselink always issues a
> query and only uses the results (AbstractRow instances) to use primary key
> references to retrieve objects from cache, avoidinh new object creation,
> am
> I doing something wrong or can I get eclipselink to make proper use of the
> cache avoiding network round trips (database queries) when loading using
> queries?
> * What is the correct implementation for hashcode and equals for domain
> objects, should they just be off primary key fields or all fields, will
> using all fields help with my caching issue, I am guessing not as eclipse
> link seems to use primary key field equals (via ClassDescriptor instances)
> and not the hashcode/equals combination for its cache.
>
> Any other fixes are welcome
>
> Thanks
> Bhaskar
>
>


-----
---
http://wiki.eclipse.org/User:James.sutherland.oracle.com James Sutherland
http://www.eclipse.org/eclipselink/
 EclipseLink ,  http://www.oracle.com/technology/products/ias/toplink/
TopLink
Wiki:  http://wiki.eclipse.org/EclipseLink EclipseLink ,
http://wiki.oracle.com/page/TopLink TopLink
Forums:  http://forums.oracle.com/forums/forum.jspa?forumID=48 TopLink ,
http://www.nabble.com/EclipseLink-f26430.html EclipseLink
Book:  http://en.wikibooks.org/wiki/Java_Persistence Java Persistence
--
View this message in context: http://www.nabble.com/Caching-and-Relational-Objects-tp21835126p21851677.html
Sent from the EclipseLink - Users mailing list archive at Nabble.com.

_______________________________________________
eclipselink-users mailing list
eclipselink-users@xxxxxxxxxxx
https://dev.eclipse.org/mailman/listinfo/eclipselink-users


Back to the top