浏览 8519 次
锁定老帖子 主题:QBC 解惑
该帖已经被评为良好帖
作者 正文
   发表时间:2008-12-19  
    使用hibernate不久,加上公司对hibernate进了封装,平时使用的也都是他们封装后的代码。自己看过怎么实现的,发现是使用Criteria来进行的查询,并没有使用hibernate官方推荐的hql。平时总想抽时间看看源代码,看过几次之后,发现有点难,主要是自己时间没有连贯性。上上周,有个同事问了个问题。才终于下定决心看看QBC的实现。
    废话不多说,下面切入主题。

    有两个类,类Person和类Student,Student继承自Person,在hibernate.cfg.xml中
  
      <mapping resource="cn/com/study/hbm/Student.hbm.xml"/>
   

   只有子类进行了注册,父类并没有注册。
  
   然后在查询时
  
      Criteria criteria = session.createCriteria(Person.class.getName());
      List list = criteria.list();
   


   遇到这种情况时,起初同事和我认为这样list中都不会有值,然而,经过测试发现,这里list中是有值的,并且list大小正是子类Student对应的表中的值的个数。我们对此感到不理解。
   看看Hibernate是怎样实现的吧。

   首先是创建Criteria
  
      this.session = session;
      this.entityOrClassName = entityOrClassName;
      this.cacheable = false;
      this.rootAlias = alias;
   
 
   这个很简单,无非就是几个参数的传递。
   接着是Criteria 添加了 Expression (也可以不添加)

   然后调用 list()方法
  
   我们这里重点来关注下list()方法
  
   list()方法中调用了SessionImpl的list方法
  
       public List list(CriteriaImpl criteria) throws HibernateException {
            ...
              String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );
            ...
       }
   

  这里很关键,getImplementors方法,根据Criteria对应的实体类,来返回真正要查找的ClassName的数组。
  factory是SessionFactoryImpl对象。
 
  来看这个方法。(很关键,所以全引用了)
 
     final Class clazz;
		try {
			clazz = ReflectHelper.classForName(className);
		}
		catch (ClassNotFoundException cnfe) {
			return new String[] { className }; //for a dynamic-class
		}

		ArrayList results = new ArrayList();
		Iterator iter = entityPersisters.values().iterator();
		while ( iter.hasNext() ) {
			//test this entity to see if we must query it
			EntityPersister testPersister = (EntityPersister) iter.next();
			if ( testPersister instanceof Queryable ) {
				Queryable testQueryable = (Queryable) testPersister;
				String testClassName = testQueryable.getEntityName();
				boolean isMappedClass = className.equals(testClassName);
				if ( testQueryable.isExplicitPolymorphism() ) {
					if ( isMappedClass ) {
						return new String[] {className}; //NOTE EARLY EXIT
					}
				}
				else {
					if (isMappedClass) {
						results.add(testClassName);
					}
					else {
						final Class mappedClass = testQueryable.getMappedClass( EntityMode.POJO );
						if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) {
							final boolean assignableSuperclass;
							if ( testQueryable.isInherited() ) {
								Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass( EntityMode.POJO);
								assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass);
							}
							else {
								assignableSuperclass = false;
							}
							if ( !assignableSuperclass ) {
								results.add( testClassName );
							}
						}
					}
				}
			}
		}
		return (String[]) results.toArray( new String[ results.size() ] );
  
 
 
   循环SessionFactoryImpl entityPersisters属性里值,
  
      boolean isMappedClass = className.equals(testClassName);
      if ( testQueryable.isExplicitPolymorphism() ) {
	  if ( isMappedClass ) {
		return new String[] {className}; //NOTE EARLY EXIT
	  }
      }
   

  如果 isExplicitPolymorphism()方法返回true,那么很简单,只有你找的class和entityPersisters里 class是同一个时,才会返回值。 显然,我们要找的Person类在这里没有被返回。
  接着
 
     if (isMappedClass) {
	results.add(testClassName)
     }
  

  同上,也不回返回。
 
     if ( mappedClass!=null && clazz.isAssignableFrom( mappedClass ) ) {
         if ( testQueryable.isInherited() ) {
		Class mappedSuperclass = getEntityPersister( testQueryable.getMappedSuperclass() ).getMappedClass( EntityMode.POJO);
		assignableSuperclass = clazz.isAssignableFrom(mappedSuperclass);
	 }
         else {
	     assignableSuperclass = false;
        }
        if ( !assignableSuperclass ) {
	     results.add( testClassName );
        }
    }
  

  通过 clazz.isAssignableFrom( mappedClass ) 方法,就可以理解一点了,Person是Student的父类,符合这个条件。
  然后又是 isInherited()方法,但是根据查询结果,可以推出这里应该是false.
是false,results中就会添加一个Student的Class Name.

好了,到这里大家应该就会明白了为什么查询Person会返回Student数据库中个数了。


   发表时间:2008-12-19  
但是,这里有几个关键点。
  1、entityPersisters属性中存的是什么
  2、isExplicitPolymorphism()方法和isInherited()方法

查了一下,isExplicitPolymorphism()方法和isInherited()方法,前者是EntityMetamodel类的explicitPolymorphism属性,后者是它的inherited属性。

哎,这肯定是在得到Configuration对象是赋得值,还是从Configuration来看吧。
   public Configuration configure() throws HibernateException {
	configure( "/hibernate.cfg.xml" );
	return this;
   }

所谓的默认从classpath中加载hibernate.cfg.xml。。。
最后调用此方法
   protected void add(org.dom4j.Document doc) throws MappingException {
	HbmBinder.bindRoot( doc, createMappings(), CollectionHelper.EMPTY_MAP );
   }


在HbmBinder类中
   private static void bindRootPersistentClassCommonValues(Element node,
			java.util.Map inheritedMetas, Mappings mappings, RootClass entity)
			throws MappingException {
      // POLYMORPHISM
      Attribute polyNode = node.attribute( "polymorphism" );
      entity.setExplicitPolymorphism( ( polyNode != null )
          && polyNode.getValue().equals( "explicit" ) );
  }

奥,原来这里是从*.hbm.xml中读取的 polymorphism属性,也就是如果此属性值为explicit时,调用isExplicitPolymorphism()方法时才会返回true。难怪上面会返回false了,我根本就不知道有此属性。

接着,来看entityPersisters属性
在SessionFactoryImpl的构造方法中,有
  entityPersisters = new HashMap();
  EntityPersister cp = PersisterFactory.createClassPersister(model, cache, this, mapping);
  if ( cache != null && cache.getCache() instanceof OptimisticCache ) {
	( ( OptimisticCache ) cache.getCache() ).setSource( cp );
  }
  entityPersisters.put( model.getEntityName(), cp );


那这里就明确了,entityPersisters里放的是 EntityPersister对象。

最后,来看下,isInherited()方法
  public SessionFactoryImpl(){
    EntityPersister cp = PersisterFactory.createClassPersister(model, cache, this, mapping);
  }

最后
    public EntityMetamodel(PersistentClass persistentClass, SessionFactoryImplementor sessionFactory) {
        inherited = persistentClass.isInherited();
    }
 

中,会设置这个值。

PersistentClass 的 isInherited();方法,共有两个实现
  Subclass
 
    public boolean isInherited() {
		return true;
    }
  

RootClass
    public boolean isInherited() {
		return false;
    }
 


而这里是RootClass,还是SubClass ,通过字面意思也可以看出来。RootClass是根Class,这是在HbmBinder中配置的。

这样,上述两个问题全部解开。


1 请登录后投票
   发表时间:2008-12-19  
通过上述的分析,在SessionImpl的list方法中,
   String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );

这里,即使,查询的Person类没有注册,也会把已经注册的Student子类给找出来。进而进行查询。

至于怎样得到list
    for( int i=0; i<size; i++ ) {
	final List currentResults = loaders[i].list(this);
	currentResults.addAll(results);
	results = currentResults;
    }


这个改天再开一个帖子,详细说明。

PS:最近在看曹晓刚同学的《深入浅出Hibernate》,中写到
   我们同学polymorphism="explict"声明了一个显式多态关系。声明为显示多态的类,只有在明确指定类名的时候才会返回此类实例。
   这里,确实帮助自己对上述方法的理解。

看了这么多,起初还觉得挺有激情的。越到后来,越觉得,像这样的问题在实际应用中到底用的广不广,自己看这个到底有没有意义,后来想想,还是很有意义的,也就坚持看下去了。
现在,不管结果如何,总算是有点小的收获了,很高兴。

在第一次在hibernate代码中看见Connection的时候,那个激动啊。

大家都知道hibernate是基于JDBC的,但只是听说,自己从没仔细看过。

   public class SettingsFactory implements Serializable {
     public Settings buildSettings(Properties props) {
          Connection conn = connections.getConnection();

          DatabaseMetaData meta = conn.getMetaData();

          connections.closeConnection(conn);
     }
   }


2 请登录后投票
   发表时间:2008-12-20  
如果还有一个类Teacher,一样继承PERSON.一样注册了.这样查person会查出什么呢?
0 请登录后投票
   发表时间:2008-12-20  
zhengyu 写道
如果还有一个类Teacher,一样继承PERSON.一样注册了.这样查person会查出什么呢?


你确定你仔细看过我上述的分析了吗
0 请登录后投票
   发表时间:2008-12-20  
kaka2008 写道
通过上述的分析,在SessionImpl的list方法中,
   String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() );

这里,即使,查询的Person类没有注册,也会把已经注册的Student子类给找出来。进而进行查询。

至于怎样得到list
    for( int i=0; i<size; i++ ) {
	final List currentResults = loaders[i].list(this);
	currentResults.addAll(results);
	results = currentResults;
    }



呵...仔细看了下..如果teacher类和Student类都注册了.
这样查person会查出这两个表的总记录数?
0 请登录后投票
   发表时间:2008-12-22  
这个事情不用去看实现,仅从对象层面上理解我觉得也能够得出同样的结论。hibernate的管理下应用似乎得到了一个大的对象池,你去查Person,好比和这个对象池的管理员说“把所有的人都叫出来”。当然会返回池中所有的Person,如果不是,那么,你还当学生是人么?

类比,查询Object类的对象,好比说“芸芸众生啊,都来举个手”,结果可想而知。

当然,不论如何,赞你的考查究竟的态度。
1 请登录后投票
   发表时间:2008-12-22  
学习一下..
0 请登录后投票
   发表时间:2008-12-22  
梦秋雨 写道
这个事情不用去看实现,仅从对象层面上理解我觉得也能够得出同样的结论。hibernate的管理下应用似乎得到了一个大的对象池,你去查Person,好比和这个对象池的管理员说“把所有的人都叫出来”。当然会返回池中所有的Person,如果不是,那么,你还当学生是人么?

类比,查询Object类的对象,好比说“芸芸众生啊,都来举个手”,结果可想而知。

当然,不论如何,赞你的考查究竟的态度。


me 写道

  看了这么多,起初还觉得挺有激情的。越到后来,越觉得,像这样的问题在实际应用中到底用的广不广,自己看这个到底有没有意义,后来想想,还是很有意义的,也就坚持看下去了。


看了几天,自己也意识到这个问题了。

最初是报着看看到底是怎么实现来读源码的,后来目的变了。不仅仅是为了看这个。

而是想从高手写的代码中学习。确实,也有收获。

BTW:你的例子举得很好。

0 请登录后投票
   发表时间:2008-12-23   最后修改:2008-12-23
根据我的实际使用结果,在Hibernate这一层使用继承的概念,不仅仅是高耦合(和需求耦合),而且在很多场景下,复杂的hql或qbc在继承的问题上不兼容的情况非常多。我们曾花费大量时间来研究针对继承如何来做技巧性的查询,耗费的时间代价实在太大了。

不知道楼主有没有在真实的项目中使用继承,有没有碰到类似问题。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics