[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-3644) Add support for "WITH UR" isolation clause on DB2
Ricardo Fernandes (JIRA)
noreply at atlassian.com
Wed Mar 18 10:29:38 EDT 2009
[ http://opensource.atlassian.com/projects/hibernate/browse/HHH-3644?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=32665#action_32665 ]
Ricardo Fernandes commented on HHH-3644:
----------------------------------------
Fortunately I've found a way around this issue by using Hibernate's Interceptors.
The idea is to create a specific DB2 interceptor which transform the queries before they are sent to the database.
The interceptor code goes like this:
/**
* A class for intercepting the calls to a DB2 database in order
* to change the query string for including some special instructions
* useful in DB2, according to the following rules:<br>
* <ul>
* <li>FOR READ ONLY - added to all select queries</li>
* <li> WITH XX - where XX stands for one of the 4 different isolation
* levels known by DB2: CS, RS, RR or UR
* </li>
* </ul>
* @author Ricardo Fernandes
*
*/
public class DB2Interceptor extends EmptyInterceptor {
/** */
private static final long serialVersionUID = 1L;
@Override
public String onPrepareStatement(String str) {
// Transform the original query into lower case
String compstr = str.toLowerCase();
// Check if we're dealing with a simple select statement
if (compstr.matches("^select.*") && !compstr.matches(".*for update.*")) {
// We are, so add the 'for read only' clause
if (!compstr.matches(".*for read only.*")) {
str += " for read only";
}
// Checks whether the 'client' asked for a specific isolation level
// to be applied to this query
TransactionManager manager = ThreadTools.getTransactionManager();
if (manager != null && manager.getIsolationLevel() != TransactionManager.ISOLATION_DEFAULT) {
String suffix = null;
String expr = null;
switch(manager.getIsolationLevel())
{
case TransactionManager.ISOLATION_READ_COMMITTED:
// The same as CURSOR STABILITY (CS) in the DB2 world
suffix = " with cs ";
expr = ".*with cs.*";
break;
case TransactionManager.ISOLATION_REPEATABLE_READ:
// The same as READ STABILITY (RS) in the DB2 world
suffix = " with rs ";
expr = ".*with rs.*";
break;
case TransactionManager.ISOLATION_SERIALIZABLE:
// The same as REPEATABLE READ (RR) in the DB2 world
suffix = " with rr ";
expr = ".*with rr.*";
break;
case TransactionManager.ISOLATION_UNCOMMITTED_READ:
suffix = " with ur ";
expr = ".*with ur.*";
break;
default:
// This is empty
break;
}
// Check if we should apply the isolation level to the query
if (suffix != null && !compstr.matches(expr)) {
str += suffix;
}
}
}
return str;
}
}
TransactionManager is a class used for storing state information for the current execution thread.
In this case it holds the isolation level that should be applied to the current query.
It also has a set of constants for defining the isolation level.
ThreadTools is wrapper for a Hashtable associated with the current thread.
HibernateSingleton is a singleton for storing application scope variables (such as the sessionFactory)
A sample usage of these classes might be:
public List sampleQuery(String classname)
List list = null;
try {
ThreadTools.getTransactionManager().setIsolationLevel(TransactionManager.ISOLATION_UNCOMMITTED_READ);
Criteria criteria = HibernateSingleton.instance().getSessionFactory().getCurrentSession().createCriteria(classname);
list = criteria.list();
} catch (Exception e) {
log.error("Error executing query for " + classname, e);
throw new DatabaseException("Error executing query for " + classname, e);
}
finally {
// Reset the isolation level
ThreadTools.getTransactionManager().resetIsolationLevel();
}
return list;
}
The interceptor might be associated with the hibernate's session factory on startup. Something like this:
...
// Configure the session factory
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
configuration.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JDBCTransactionFactory");
configuration.setProperty("hibernate.current_session_context_class", "thread");
configuration.setInterceptor(new DB2Interceptor());
sessionFactory = configuration.buildSessionFactory();
HibernateSingleton.instance().setSessionFactory(sessionFactory);
...
Hope this may help others having the same issues that I had.
Best regards,
Ricardo Fernandes
> Add support for "WITH UR" isolation clause on DB2
> -------------------------------------------------
>
> Key: HHH-3644
> URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3644
> Project: Hibernate Core
> Issue Type: Sub-task
> Components: core
> Affects Versions: 3.3.1
> Environment: HIbernate 3.3.1 / DB2 9.x
> Reporter: Ricardo Fernandes
> Original Estimate: 2 days
> Remaining Estimate: 2 days
>
> The question is basically the same as Steve's, i.e. to force DB2 to use as few locks as possible on heavy load scenarios.
> Although some might consider the use of the WITH UR clause a bad approach (since it allows dirty reads), the fact is that there are some cases where this is in fact acceptable, such as computing the total amount of rows of a query (for information purposes) or producing a high-level listing of items with very little detailed info. The bottom line is: if I need to trade performance for extremely accurate data, I surely want to be able to decide when this should happen.
> I've already performed the changes that makes it possible to use both the FOR READ ONLY and the WITH UR clauses and I will be submitting a patch shortly so you can have a look at it. The strategy I've used was basically the following:
> 1. Added two new methods on the Dialect class:
> String getDatabaseReadOnlyString(String sql) - for adding the READ ONLY clause
> String getDirtyReadsString(String sql) - for adding the WITH UR clause
> 2. Changed the Query interface in order to allow the user to say whether he/she wants the query to allow dirty reads:
> Query setAllowDirtyReads(boolean allowDirtyReads);
> 3. Added a default implementation on the AbstractQueryImpl which initializes the flag a false
> 4. Added a similar attribute on the QueryParameters class
> 5. Changed the prepareQueryStatement() method of the Loader class (just after the useLimit part in order:
> a) Ask the dialect for the getDatabaseReadOnlyString() is there are no LockModes set (as did Steve)
> b) Ask the dialect for the getDirtyReadsString() is the queyParameters allows dirty reads.
> All the tests were well succeeded.
> Hope you find these changes, at least, worth looking at.
> Best Regards,
> Ricardo
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
More information about the hibernate-issues
mailing list