`
raymond2006k
  • 浏览: 295877 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

解决Hibernate SQL Query Cache的一个可靠性问题(附源码)

阅读更多
    上篇 帖子 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. 
  
6
1
分享到:
评论

相关推荐

    hibernate-3.2源码包

    Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的数据交互。在 Hibernate 3.2 版本中,它引入了许多改进和新特性,使得开发人员能够更加高效地处理数据库操作。...

    hibernate源码

    文件`hibernate_fetch_1`与`hibernate_query_cache`涉及到的是Hibernate的数据检索和查询缓存。Hibernate提供了多种加载策略,包括立即加载(Eager Fetching)和延迟加载(Lazy Loading)。立即加载会一次性获取...

    hibernate源码下载

    《深入理解Hibernate源码》 Hibernate,作为Java领域中的一款著名持久化框架,极大地简化了数据库操作。本文将从给出的文件名出发,探讨Hibernate在数据获取、缓存策略、扩展功能以及对象映射等方面的原理与实践。 ...

    hibernate5.0.9源码

    通过深入学习和理解Hibernate 5.0.9的源码,开发者不仅可以提升对ORM框架的理解,还能更好地利用Hibernate解决实际问题,提升开发效率,降低维护成本。同时,这也有助于理解其他ORM框架或Java持久化技术,为日后的...

    hibernate3.6.1源码

    Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的交互。在本文中,我们将深入探讨Hibernate 3.6.1版本的源码,了解其内部工作原理以及关键组件的功能。 源码分析...

    hibernate源码包

    Hibernate 是一个著名的开源Java对象关系映射(ORM)框架,它极大地简化了数据库与Java应用程序之间的交互。在Java开发中,Hibernate 提供了一种抽象层,使得开发者无需直接编写SQL语句,就能对数据库进行操作。这个...

    hibernate3.2_src官方源码

    Hibernate 3.2 是一个非常重要的Java持久化框架,它为开发者提供了ORM(对象关系映射)服务,使得在Java应用程序中操作数据库变得更加方便。在这个`hibernate3.2_src`官方源码中,我们可以深入理解Hibernate的核心...

    hibernate4.22源码

    Hibernate是一个开源的对象关系映射(ORM)框架,它允许Java开发者在Java对象和数据库之间建立映射,从而简化数据操作。对于深入理解Hibernate的工作原理,分析其源码是至关重要的。`hibernate4.2.21.Final`是...

    struts+hibernate+spring源码

    通过源码,我们可以深入了解Hibernate如何将Java对象与数据库表进行映射,以及Query和Criteria API如何执行SQL查询。同时,事务管理(Transaction)和缓存(Cache)机制也是重要的学习点,它们对于提高性能和保证...

    hibernate3.2lib及源码jar包纯净版

    这个标题指的是一个针对Hibernate 3.2版本的精简版库,包含了两个关键的JAR文件:`hibernate3.jar`和`hibernate-3.2-src.jar`。 `hibernate3.jar`是编译后的二进制JAR,包含了Hibernate框架的所有运行时类和资源。...

    Hibernate参数设置一览表

    在Java企业级开发中,Hibernate是一个非常重要的对象关系映射(ORM)框架,它简化了数据库操作,使开发者能够用Java对象来操作数据库。本篇文章将详细阐述Hibernate中的主要参数设置,帮助开发者更好地理解和配置...

    hibernate3.2源码

    【hibernate3.2源码】是Java领域中著名的对象关系映射(ORM)框架Hibernate的一个特定版本的源代码。Hibernate允许开发人员在Java应用中以面向对象的方式操作数据库,消除了传统的JDBC繁琐的代码,提高了开发效率。...

    spring-orm-hibernate4源码

    9. **Query和Criteria API**: Hibernate提供了SQL语句的HQL(Hibernate Query Language)和更面向对象的Criteria API,它们都是在Session上执行的。Spring的HibernateTemplate提供了与这些API的交互方法。 10. **...

    Hibernate学习源码

    Hibernate是一个开源的Java库,它允许开发者用面向对象的方式来处理关系数据库。通过Hibernate,开发者可以将Java类与数据库表进行映射,然后通过操作对象来实现对数据库的增删改查操作,避免了手动编写大量的SQL...

    java hibernate上课源码6

    Java Hibernate 是一个强大的持久化框架,它简化了Java应用程序与关系数据库之间的交互。这个"java hibernate 上课源码6"应该包含了一系列示例代码,用于教授如何在实际项目中应用Hibernate。通过深入理解这些源码,...

    hibernate4.5.1源码

    1. **对象关系映射(ORM)**:ORM是Hibernate的核心功能,它通过提供一个接口,使得Java对象可以直接与数据库表进行交互,消除了传统JDBC中的大量繁琐代码。在源码中,`org.hibernate.cfg`包下的配置类和`org....

    hibernate总结练习源码

    Hibernate 是一个开源的对象关系映射(ORM)框架,它允许Java开发者将数据库操作与对象模型进行松耦合,使得在处理数据库数据时可以使用面向对象的方式。本资源包含的"hibernate总结练习源码"提供了对Hibernate ORM...

Global site tag (gtag.js) - Google Analytics