日期:2014-05-16 浏览次数:20457 次
在最近一个项目中,由于项目管理的不善,导致了开发人员之间的相对独立的开发,所以就造成了本来是同一个数据库的表,但是映射成了两个Java类,本来相安无事,但是因为需求变动,造成此表要增加很多字段,本着节省工作的思想,于是在其中一个实体类中增加了成员变量,而由另外一个实体类继承之,由此引出了一下问题:
?
数据库表为test
两个实体类分别为testDomain1和testDomain2,其中testDomain2 extends testDomain1
?
在业务代码中,使用QBC查询testDomain1时的count时,是这么写的:
criteria.setProjection(Projections.rowCount()).uniqueResult();
?
在执行时抛出了异常:
org.hibernate.NonUniqueResultException: query did not return a unique result: 2 at org.hibernate.impl.AbstractQueryImpl.uniqueElement(AbstractQueryImpl.java:758) at org.hibernate.impl.CriteriaImpl.uniqueResult(CriteriaImpl.java:431) at com.ibm.b2e.framework.base.dao.BaseDaoImpl.pagedQuery(BaseDaoImpl.java:784) at com.ibm.b2e.framework.base.dao.BaseDaoImpl.pagedQuery(BaseDaoImpl.java:651)
?而看Hibernate打印出的SQL执行了两次select count(*)的操作,于是猜到是表被映射了两次的问题,想从网上查一下解决方法,但是不知道选择什么关键词,只好自己看源代码,
?
发现在uniqueResult()这个方法中调用的是SessionImpl的list(CriteriaImpl criteria)方法
而list()这个方法中调用了sessionFactory的getImplementors(?String className?)方法,这个方法返回的是一个字符串数组,调试发现,它的值正好是我两个实体类的全类名,后面的代码则是根据这两个类名来执行查询,
?
焦点就在sessionFactory的getImplementors(?String className?)中了,马上冲进去一看,发现其中有一个遍历所有已映射对象的循环操作:
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); } } } } }
??看代码的意思,首先是判断当前循环到的class是不是本次查询所查的class,其次再判断Mapped class的一个属性值:testQueryable.isExplicitPolymorphism() 如果此值为true的话,直接就返回一个值只有className的字符串数组;而如果isExplicitPolymorphism()方法返回的值是false的话,那么下面的代码将会继续检查当前循环到的类是否是QBC的类的子类,如果是,则加在字符串数组中一起返回,我所遇到的问题正是后者。
?
看来关键出在isExplicitPolymorphism() 的值上面。
?
查询了Hibernate的sessionFactory初始化代码后,发现此方法的返回值来自于Mapping配置文件,打开Mapping配置文件用代码提示看了一下,<class>元素果然有一个名为polymorphism的属性,此属性有两个候选值:explicit,implicit,在研究此值的实际作用时,发现了JavaEye上原来早已经有一篇文章QBC 解惑,里面很详尽的介绍了此方法,
而对于这样的设计梦秋雨童鞋的解释更是一针见血,非常形象,不得不在此处引用一下:
?
强人啊强人。。强人啊强人。。。(此处重复30遍)