在mybatis条件查询中进行类关联查询(类Hibernate关联查询)

上一篇中,已经解决了如何使用构建条件进行数据查询。但在常规的查询中,并不总是单独查询某一个表信息,某些条件还需要关联其它数据表才能得出。我们希望在进行查询时,能够根据条件中所指定的关联表进行关联化查询,并在条件中自动处理关联化条件。如下的查询语句所示:

select a.* from tableA a inner join tableB b on b.a_id = a.id where b.c_id = ? and b.d = ? and a.e != ? 

那么在相应的条件中,即要处理模型之间的关联关系,同时处理在条件中的前缀别名信息,以保证所在表的正确性。那么相应的Criteria表达应该如下所示:

List<Criteria> criteriaList = Lists.newArrayList();
criteriaList.add(Criterias.link(B.class, "b", "aId", LinkMode.INNER));
criteriaList.add(Criterias.eq("b.c_id", cId));
criteriaList.add(Criterias.eq("b.d", d));

即在原来的基础之上,增加一个用于描述关联关系的LinkCriteria,同时在相应的条件上增加属性前缀,用于描述指定的条件主体(当然如果是当前主体不需要前缀)。在具体的生成Mql的过程当中,增加用于处理关联关系的逻辑,同时处理条件的前缀即可。

继续阅读“在mybatis条件查询中进行类关联查询(类Hibernate关联查询)”

在mybatis中使用Criteria式条件查询

在使用常规的mybatis时,我们经常碰到的问题就是条件式查询。在一个查询界面,查询条件较多,并且运算符并不总是=时,在后台就需要拼装sql语句。这种处理方式肯定不是使用mybatis的初衷,对于使用了hibernate的我来说,如果mybatis也有一套criteria查询就好了。在具体实现中,我们只需要按照hibernate的处理方式定义好相应的criteria,最后传递给mybatis,其自身处理相应的条件和参数信息,最终返回相应的数据即可。如下一个示例代码所示:

List<Criteria> criteriaList = Lists.newArrayList();
criteriaList.add(Criterias.eq("aaa",111));//等于某个值
criteriaList.add(Criterias.ge("date",new Date()));//大于或等于某个时间
criteriaList.add(Criterias.in("code", new String[]{"a","b","c"}));//代码值在一个集合当中

如果使用这种方式,无疑会大大降低编写表单式查询的代码复杂度。同时,在内部处理中也不需要作任何判断,而直接将生成的sql交给mybatis去执行即可。当然,我们不希望生成的sql连我们自己都看不懂(想一想hibernate生成的sql),最终生成的sql像下面这样即可。

select * from table where aaa = #{aaa1,jdbcType=NUMERIC} and date >= #{date2,jdbcType=TIMESTAMP} and code in (#{code1,jdbcType=VARCHAR},#{code2,jdbcType=VARCHAR},#{code3,jdbcType=VARCHAR})

这是标准的mybatis语句,在进行代码调试和处理时也方便进行查看并处理。那么整个处理逻辑即变成如何处理参数信息,即如下所示的语句

字段名 运算符 #{参数名,jdbcType=字段类型} //filed = #{param1,jdbcType=VARCHAR}

参考 数据库表与java域模型之间的mapping和自动生成(基于mybatis)。我们可以很容易地就完成这个处理。分别处理 字段名 运算符 参数名 字段类型 参数映射即可。

继续阅读“在mybatis中使用Criteria式条件查询”

Hibernate中criteria一对多关联查询时distinct的分页和数量问题

数据模型
班级:clazz(id,name,studentList)
学生:student(id,name,address,clazz)

经典查询
查询有地址在成都的班级信息,并分页显示。

一般情况下,以上的条件更多,也更复杂,且查询主体必须从班级入手。那么,使用Hibernate的criteria查询的话,一般情况下,编码如下:

DetachedCriteria criteria = DetachedCriteria.forClass(Clazz,"c");
criteria.createAlias("studentList", "sl");
criteria.add(Restrictions.eq("sl.address", "成都");

//保证Distinct
criteria.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);

//分页
listByCriteria(criteria,page);

单就逻辑本身,看似没有什么问题,且在数据较少(如没有分页数据时),会返回正确的结果。但是一旦出现分页数据时,你就会发现,分页数据是错的。表现结果为page对象所表现的数据总数明确不对,且每页所显示的数据也并不是分页信息中的20条,而是不确定的几条(明显少于默认的20条)。这就表明,在这个查询中,肯定有一步出问题了。

没错,在这个查询中,我们要求返回的主体为班级,即Clazz。但Hibernate出生的sql却并不能如我们所愿,它直接使用连接来组装sql语句,并尝试返回包括学生在类的一个复合对象,然后再组装成班级对象,最后根据集合信息进行distinct操作,最后就形成了不正确的结果。整个操作看起来如下所示:

//第一步:生成sql,使用联接查询
select a.*,b.* from clazz a inner join student b on b.aId = a.id where b.address = '成都'

//第二步:带参数 page信息,查询前20条,返回结果为 page.setTotal(XX条),当页数据条数:20
班级一 李一 成都
班级一 李二 成都
班级一 李三 成都
班级二 李四 成都
班级二 李五 成都
班级三 李六 成都
......
//第三步:组装班级对象,去重,此步操作在hibernate程序中进行,返回结果
班级一
班级二
班级三

//返回结果,实际情况:当前page显示数量:20,实际所看到数量 3!

继续阅读“Hibernate中criteria一对多关联查询时distinct的分页和数量问题”

hibernate中的qbc(query by criteria)不支持having

    最近在做一次查询时,需要对查询的结果进行二次过滤,即分组之后再过滤,这种情况下,需要使用having。如以下的例子,我需要查询一个字段和另一个字段的和,并对记录进行分组,但结果集中并不需要总记录数为0的结果。sql语句如下所示:

select a, sum(b) as bsum from t where clause1=? group by a having and sum(b) > 0

    这种查询语句,使用hql能够很好地写出来,并且能够很好的运行。但如果条件数不定时,使用hql就需要动态的拼接hql,并且需要根据参数来设置参数。如下所示:

if(StringUtils.hasText("clause1"))
			hql += "clause1 = :clause1";
		//.....其它判断和处理
		if(StringUtils.hasText("clause1"))
			query.setString("clause1", clause1);

    在这种情况下,许多的if判断,无论是在编码还是在逻辑处理上都让人麻烦。对于不定的参数查询,qbc是最合适的(当然还有qbe)。但是对于qbc,处理having的这种情况即是不能处理的。
    不管使用任何处理情况,都不能从criteria中构建出这个having子句。

    经过google之后,网上有一个hibernate的bug报告HHH-1043,http://opensource.atlassian.com/projects/hibernate/browse/HHH-1043 。这个链接中,论述了在hibernate中的问题,并且提供一些解决方案,但这些解决方案都不能很容易的理解和解决问题。

    最根本的问题在于,qbc中的criteria,在hibernate内部设计和编码时,都是作为where条件中的一部分来实现的.主要的代码在类CriteriaQueryTranslator的方法getWhereCondition中,在这个方法中,迭代所有的criteria,并作为where条件使用。作为用在group集合中的having子句,并不属于where条件中任何一句。如果强行在criteria中使用having(比如使用Restrictions.sqlRestriction),最终的结果就是将having最终加到了where中条件的一部分。不过,这样生成的sql,即是错的,不会得到任何结果。

    直到最新的hibernate 3.6版本,此问题仍然未解决。对于需要处理聚集函数的条件问题,现阶段,除了hql(当然sql也算)。hibernate还没有其它的方法。先就这样吧。