锁定老帖子 主题:QBC 解惑
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-12-19
废话不多说,下面切入主题。 有两个类,类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数据库中个数了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间: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中配置的。 这样,上述两个问题全部解开。 |
|
返回顶楼 | |
发表时间: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); } } |
|
返回顶楼 | |
发表时间:2008-12-20
如果还有一个类Teacher,一样继承PERSON.一样注册了.这样查person会查出什么呢?
|
|
返回顶楼 | |
发表时间:2008-12-20
zhengyu 写道 如果还有一个类Teacher,一样继承PERSON.一样注册了.这样查person会查出什么呢?
你确定你仔细看过我上述的分析了吗 |
|
返回顶楼 | |
发表时间: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会查出这两个表的总记录数? |
|
返回顶楼 | |
发表时间:2008-12-22
这个事情不用去看实现,仅从对象层面上理解我觉得也能够得出同样的结论。hibernate的管理下应用似乎得到了一个大的对象池,你去查Person,好比和这个对象池的管理员说“把所有的人都叫出来”。当然会返回池中所有的Person,如果不是,那么,你还当学生是人么?
类比,查询Object类的对象,好比说“芸芸众生啊,都来举个手”,结果可想而知。 当然,不论如何,赞你的考查究竟的态度。 |
|
返回顶楼 | |
发表时间:2008-12-22
学习一下..
|
|
返回顶楼 | |
发表时间:2008-12-22
梦秋雨 写道 这个事情不用去看实现,仅从对象层面上理解我觉得也能够得出同样的结论。hibernate的管理下应用似乎得到了一个大的对象池,你去查Person,好比和这个对象池的管理员说“把所有的人都叫出来”。当然会返回池中所有的Person,如果不是,那么,你还当学生是人么?
类比,查询Object类的对象,好比说“芸芸众生啊,都来举个手”,结果可想而知。 当然,不论如何,赞你的考查究竟的态度。 me 写道 看了这么多,起初还觉得挺有激情的。越到后来,越觉得,像这样的问题在实际应用中到底用的广不广,自己看这个到底有没有意义,后来想想,还是很有意义的,也就坚持看下去了。 看了几天,自己也意识到这个问题了。 最初是报着看看到底是怎么实现来读源码的,后来目的变了。不仅仅是为了看这个。 而是想从高手写的代码中学习。确实,也有收获。 BTW:你的例子举得很好。 |
|
返回顶楼 | |
发表时间:2008-12-23
最后修改:2008-12-23
根据我的实际使用结果,在Hibernate这一层使用继承的概念,不仅仅是高耦合(和需求耦合),而且在很多场景下,复杂的hql或qbc在继承的问题上不兼容的情况非常多。我们曾花费大量时间来研究针对继承如何来做技巧性的查询,耗费的时间代价实在太大了。
不知道楼主有没有在真实的项目中使用继承,有没有碰到类似问题。 |
|
返回顶楼 | |