-->
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.  [ 1 post ] 
Author Message
 Post subject: Native query caching issue (no query spaces)
PostPosted: Tue Aug 11, 2009 6:34 pm 
Newbie

Joined: Tue Aug 11, 2009 5:49 pm
Posts: 2
I ran into some trouble using a named native query with setCacheable(true). I found a solution, but I'd like feedback on what I did; and maybe I have some suggested improvements to Hibernate and Hibernate Annotations.

The setup: The idea of the named query is to get the list of users who are managers (RIs) for a specified project. On the project page, the user can update the list of RIs (the RIs are a collection on the project). In other parts of the application, I need to know who the RIs are for the project (and it's child objects) in order to determine permissions.

The problem: After the user updates the RI list on our project page, the old (cached) RI list is still returned by the named native query.

My solution: Cast the Query to a SQLQuery, and associated it with the relevant tables using the addSynchronizedQuerySpace(String) method.



The details:

This is a much-simplified version of my named SQL query, for clarity:

@NamedNativeQuery(name = "user.managers", query = "select u.* from users_web u where u.unique_id IN ( select ri_id from ri_id_project_id where project_id = :projectId)", resultClass = User.class)

The ri_id_project_id table maps the project entity to a collection of User entities. The mapped property on the project entity looks like this:

@OneToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE }, fetch = FetchType.LAZY)
@JoinTable(name = "ri_id_project_id", joinColumns = @JoinColumn(name = "project_id"), inverseJoinColumns = @JoinColumn(name = "ri_id"))
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
...
public SortedSet<User> getManagers() { ... }

Here is the build(Session) method of my IQueryBuilder, which uses the "user.managers" named native query:

/**
* @see net.databinder.models.IQueryBuilder#build(org.hibernate.Session)
*/
public Query build(final Session hibernateSession) {
Query query = hibernateSession.getNamedQuery("user.managers");
query.setCacheable(true);

/*
* When using a cacheable SQL query, we have to explicitly set the query spaces (tables) that the query relies on.
* This ensures that the query cache doesn't return stale results when the relevant tables are updated.
*/
SQLQuery sqlQuery = (SQLQuery) query;
//get the database catalog name
String catalogName = null;
try {
catalogName = hibernateSession.connection().getCatalog();
}
catch (SQLException e) {
throw new RuntimeException("Could not retrieve the DB catalog name", e);
}
//add query spaces <-- here is my fix
sqlQuery.addSynchronizedQuerySpace(catalogName + ".dbo.users_web");
sqlQuery.addSynchronizedQuerySpace(catalogName + ".dbo.ri_id_project_id");

/*
* Set the query parameters
*/
...
return query;
}

One of my co-workers and I spent an hour or so stepping through Hibernate code in the debugger, and we determined that the "querySpaces" property was the source of the problem. When the named native query is constructed, the querySpaces property is null. querySpaces are the tables involved in the query; each query space is associated with a timestamp in the cache, and when an update is performed through hibernate, the timestamp changes. If a query cached result has an older timestamp than of any of its query spaces, the cached result is expired, and a fresh query is run against the DB.

In our case, the problem was that the named native query had no query spaces; i.e., no tables or entities were associated with the query which had to be checked for updates. I was able to add the query spaces; but I think that Hibernate should:
A. Try to determine the appropriate query spaces from a SQL query automatically
or:
B. Provide an easier/more obvious/well-documented mechanism for specifying the query spaces of a native query.

I would have liked to see a Hibernate annotation to accomplish this; ex:
@NamedNativeQuery(name = "user.managers", query = "select u.* from users_web u where u.unique_id IN ( select ri_id from ri_id_project_id where project_id = :projectId)", resultClass = User.class, querySpaces=@QuerySpaces({@QuerySpace(name="dbo.users_web"), @QuerySpace(name="dbo.ri_id_project_id")}))
I think additional documentation is also warranted.

What do you guys think? Is my solution the correct one? If not, what should I be doing differently? If so, can I add the suggestions to the JIRA?


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

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.