上篇 帖子
Hibernate查询缓存的一个可靠性问题 说发现Hibernate 的 Query Cache 在使用 SQL Query 时的一个可靠性 Bug, 即在关联表数据修改后,无法查询出最新数据的问题。 经过源码分析,找到问题所在,并通过修改 Hibernate 源代码,成功解决了此问题。
Hibernate 3.1, JDK 1.4
1. 问题原因:
走了一下源码,大致找到了问题所在, SQLQueryReturnProcessor 在分析 query 时,用于判断缓存失效的 querySpaces[]只分析出了返回结果对应的表,例如例子中的权限点表:sys_perm, 而sql中的关联表没有分析出来,并放到 querySpaces[] 中。
具体分析:
类 org.hibernate.loader.custom.SQLCustomQuery, 它是解析 SQL Query 的一个包装类。
SQLCustomQuery 类有一个与缓存相关的属性,querySpaces 集合 :
private final Set querySpaces = new HashSet();
通过源码分析,发现 querySpaces 主要用来进行Cache更新检查,querySpaces 存放的是基本VOClass 对应的 tableName, 例如: {SYS_PERM,SYS_USER} . Hibernate 在执行查询时,会检查这个集合中的所有 tableName, 如果该任意一个 tableName 对应 VOClass 二级缓存 有增,删,改的更新操作,即 UpdateTimestampsCache 不是最新的 ,那么该 Query 的 cache 就失效,就会重新去数据库中查询 ID 集合。
SQLCustomQuery 在构造函数中即进行 sql 的解析和querySpaces[]的判断,其中中有这样一段代码:
public SQLCustomQuery(.....) throws HibernateException {
SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(queryReturns, scalarQueryReturns, factory);
processor.process();
....
SQLLoadable[] entityPersisters = (SQLLoadable[]) processor.getPersisters().toArray( new SQLLoadable[0] );
....
for (int i = 0; i < entityPersisters.length; i++) {
SQLLoadable persister = entityPersisters[i];
//alias2Persister.put( aliases[i], persister );
//TODO:Does not consider any other tables referenced in the query
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
....
}
....
//TODO: Does not consider any other tables referenced in the query
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
其中红色部分是将 persister 的querySpaces[] 赋给 sqlQuery 的 querySpaces, 但是 persister 代表返回结果集类型对应的表,测试用例中是权限表 SYS_PERM; 因此漏了关联表 (Reference Table),这就是问题所在。 我们看到作者 author Gavin King, Max Andersen 也打了 TODO 注释:
//TODO: Does not consider any other tables referenced in the query
“不要考虑其他查询中的关联表”, 看来作者也留下一手,也许是遗忘了该 TODO 的处理。
2. 解决原理
关键就是要对 sql 分析出关联表,将其加入 querySpaces[] , 这样sql query查询时,就能自动检查关联表是否有更新。
分析关联表,用正则表达式解析 sql 中的所有单词 word,并逐个检查word 是否为 sessionFactory 中已经映射的表,凡是映射的表,就作为 reference table 加入 querySpaces[] 。
1) 新增分析类: org.hibernate.loader.custom.SQLQueryReferencesAnalyzer.
/**
* analyze reference table of a specified sql query
* @author Raymond He, Guangzhou China
* Oct 8, 2008
*
*/
public class SQLQueryReferencesAnalyzer {
private static Pattern p = Pattern.compile( "\\w{3,}" );
private static Map sqlquery2ReferenceTableMapping = new HashMap();
private static Map tableName2EntityNameMapping = new HashMap(100);
private static final Log log = LogFactory.getLog( SQLQueryReferencesAnalyzer.class );
public List analyzeSQL(SessionFactory sessionFactory,String sql) {
if(sqlquery2ReferenceTableMapping.containsKey(sql)) {
List refTables = (List)sqlquery2ReferenceTableMapping.get(sql);
if(log.isDebugEnabled())
log.debug("Got ref tables:" + refTables + "\r\n of SQL: " + sql);
return refTables;
}else {
if(tableName2EntityNameMapping.size() == 0) { //init it once
initTableName2EntityNameMapping(sessionFactory);
}
List refTables = new ArrayList(3);
Matcher m = p.matcher(sql);
while(m.find()) {
String word = m.group();
word = word.toUpperCase();
//check if the word is a table name in sessionFactory
//cache table for every sessionFactory independently, for multi sessionFactory env.
String key = "SF" + sessionFactory.hashCode() + "_" + word;
if(tableName2EntityNameMapping.containsKey( key )) {
if(log.isDebugEnabled()) log.debug("word is table: "+ word);
refTables.add( word);
}
}
if(log.isDebugEnabled())
log.debug("To cache sqlquery2ReferenceTableMapping, ref tables:" + refTables + "\r\n of SQL: " + sql);
//cache it
sqlquery2ReferenceTableMapping.put(sql, refTables);
return refTables;
}
}
2) 调用reference table分析类,将关联表加入 querySpaces[]
在 SQLCustomQuery 中 完成 Gavin King 的 TODO 任务。
//2008-10-6,Raymond He fix bug here,old text: Does not consider any other tables referenced in the query
/**start: analyze ref tables and add it to querySpaces ***********/
SQLQueryReferencesAnalyzer referencesAnalyzer = new SQLQueryReferencesAnalyzer();
List refTables = referencesAnalyzer.analyzeSQL(factory, sql);
for (int k = 0; k < refTables.size(); k++) {
querySpaces.add(refTables.get(k));
}
/**end ***********/
3. 验证
还是之前那个测试用例,观察日志:
Execute No. 0 ********************
2008-10-08 17:32:50,140 [DEBUG](AbstractBatcher.java,346) - select this.PERMCODE as PERM1_15_0_, this.MODULECODE as MODULE2_15_0_, this.PERMTYPECODE as PERM3_15_0_, this.PERMNAME as PERM4_15_0_, this.PERMDESC as PERM5_15_0_, this.PORTNO as PORT6_15_0_ from (select t.perm_code as permCode,
t.module_code as moduleCode,
t.perm_name as permName,
t.perm_desc as permDesc,
t.port_no as portNo,
t.perm_type_code as permTypeCode
from sys_perm t join sys_role_perm o
on t.perm_code = o.perm_code
where o.role_code = ? ) this
(No.0)result size:1
Execute No. 1 ********************
(No.1)result size:1
2008-10-08 17:32:50,187 [DEBUG](AbstractBatcher.java,346) - delete from SYS_ROLE_PERM where PERM_CODE=? and ROLE_CODE=?
Execute No. 2 ********************
2008-10-08 17:32:50,187 [DEBUG](AbstractBatcher.java,346) - select this.PERMCODE as PERM1_15_0_, this.MODULECODE as MODULE2_15_0_, this.PERMTYPECODE as PERM3_15_0_, this.PERMNAME as PERM4_15_0_, this.PERMDESC as PERM5_15_0_, this.PORTNO as PORT6_15_0_ from (select t.perm_code as permCode,
t.module_code as moduleCode,
t.perm_name as permName,
t.perm_desc as permDesc,
t.port_no as portNo,
t.perm_type_code as permTypeCode
from sys_perm t join sys_role_perm o
on t.perm_code = o.perm_code
where o.role_code = ? ) this
(No.2)result size:0
注意到第3次又执行了sql 语句,并且 (No.2)result size:0, 表明第二次查询后, 删除了 角色授权记录,因此第3次查询 角色 STESTOR 授权限结果为 0 。 表明成功修复此问题。
4. fix包及源码下载:
用法:
1) remove one class from hibernate3.jar
org.hibernate.loader.custom.SQLCustomQuery.class
2) add hibernate3-sqlquerycache-fix.jar to classpath, it provides another SQLCustomQuery impl to solve the bug.
分享到:
相关推荐
Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的数据交互。在 Hibernate 3.2 版本中,它引入了许多改进和新特性,使得开发人员能够更加高效地处理数据库操作。...
文件`hibernate_fetch_1`与`hibernate_query_cache`涉及到的是Hibernate的数据检索和查询缓存。Hibernate提供了多种加载策略,包括立即加载(Eager Fetching)和延迟加载(Lazy Loading)。立即加载会一次性获取...
《深入理解Hibernate源码》 Hibernate,作为Java领域中的一款著名持久化框架,极大地简化了数据库操作。本文将从给出的文件名出发,探讨Hibernate在数据获取、缓存策略、扩展功能以及对象映射等方面的原理与实践。 ...
通过深入学习和理解Hibernate 5.0.9的源码,开发者不仅可以提升对ORM框架的理解,还能更好地利用Hibernate解决实际问题,提升开发效率,降低维护成本。同时,这也有助于理解其他ORM框架或Java持久化技术,为日后的...
Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的交互。在本文中,我们将深入探讨Hibernate 3.6.1版本的源码,了解其内部工作原理以及关键组件的功能。 源码分析...
Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的交互。在Java开发中,Hibernate 提供了一种抽象层,使得开发者无需直接编写SQL语句,就能对数据库进行操作。这个...
Hibernate 3.2 是一个非常重要的Java持久化框架,它为开发者提供了ORM(对象关系映射)服务,使得在Java应用程序中操作数据库变得更加方便。在这个`hibernate3.2_src`官方源码中,我们可以深入理解Hibernate的核心...
Hibernate是一个开源的对象关系映射(ORM)框架,它允许Java开发者在Java对象和数据库之间建立映射,从而简化数据操作。对于深入理解Hibernate的工作原理,分析其源码是至关重要的。`hibernate4.2.21.Final`是...
通过源码,我们可以深入了解Hibernate如何将Java对象与数据库表进行映射,以及Query和Criteria API如何执行SQL查询。同时,事务管理(Transaction)和缓存(Cache)机制也是重要的学习点,它们对于提高性能和保证...
这个标题指的是一个针对Hibernate 3.2版本的精简版库,包含了两个关键的JAR文件:`hibernate3.jar`和`hibernate-3.2-src.jar`。 `hibernate3.jar`是编译后的二进制JAR,包含了Hibernate框架的所有运行时类和资源。...
在Java企业级开发中,Hibernate是一个非常重要的对象关系映射(ORM)框架,它简化了数据库操作,使开发者能够用Java对象来操作数据库。本篇文章将详细阐述Hibernate中的主要参数设置,帮助开发者更好地理解和配置...
【hibernate3.2源码】是Java领域中著名的对象关系映射(ORM)框架Hibernate的一个特定版本的源代码。Hibernate允许开发人员在Java应用中以面向对象的方式操作数据库,消除了传统的JDBC繁琐的代码,提高了开发效率。...
9. **Query和Criteria API**: Hibernate提供了SQL语句的HQL(Hibernate Query Language)和更面向对象的Criteria API,它们都是在Session上执行的。Spring的HibernateTemplate提供了与这些API的交互方法。 10. **...
Hibernate是一个开源的Java库,它允许开发者用面向对象的方式来处理关系数据库。通过Hibernate,开发者可以将Java类与数据库表进行映射,然后通过操作对象来实现对数据库的增删改查操作,避免了手动编写大量的SQL...
Java Hibernate 是一个强大的持久化框架,它简化了Java应用程序与关系数据库之间的交互。这个"java hibernate 上课源码6"应该包含了一系列示例代码,用于教授如何在实际项目中应用Hibernate。通过深入理解这些源码,...
1. **对象关系映射(ORM)**:ORM是Hibernate的核心功能,它通过提供一个接口,使得Java对象可以直接与数据库表进行交互,消除了传统JDBC中的大量繁琐代码。在源码中,`org.hibernate.cfg`包下的配置类和`org....
Hibernate 是一个开源的对象关系映射(ORM)框架,它允许Java开发者将数据库操作与对象模型进行松耦合,使得在处理数据库数据时可以使用面向对象的方式。本资源包含的"hibernate总结练习源码"提供了对Hibernate ORM...