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还没有其它的方法。先就这样吧。

在hibernate中查询使用list,map定制返回类型

    在使用hibernate进行查询时,使用得最多的还是通过构建hql进行查询了。在查询的过程当中,除使用经常的查询对象方法之外,还会遇到查询一个属性,或一组聚集结果的情况。在这种情况下,我们通常就需要对返回的结构进行处理。
    一般情况下,我们通过构建hql,并通过设置query的resultTransformer来定制返回结果的类型,一般设置为map属性,如下所示:

Query query = session.createQuery("hql");
query.setResultTransformer(CriteriaSpecification.ALIAS_TO_ENTITY_MAP);

    来指定查询结果的每一项为一个map。
    不过,随着hibernate的发展,可以在hql中直接使用集合查询语句,如list和map了。以下分别介绍使用list和map时的查询语句以及查询结果。首先,数据库的数据如下所示:

mysql> select * from p_dictionary;
+-----------------+----+---------+------+--------+--------+
| dictionary_type | id | version | code | forbid | value  |
+-----------------+----+---------+------+--------+--------+
| COUNTY          |  1 |       0 | 001  |        | 四川   |
| COUNTY          |  2 |       0 | 002  |        | 北京   |
| COUNTY          |  3 |       0 | 001  | NULL   | 四川   |
+-----------------+----+---------+------+--------+--------+
3 rows in set (0.00 sec)

    以下分别介绍使用list和map的查询语句和查询结果:

    使用list

String query = "select new list(p.code,p.value) from Dictionary p";
List list = session.createQuery(query).list();
System.out.println(list);
//结果
[[001, 四川], [002, 北京], [001, 四川]]

    使用map,首先不指定alias,则结果的键就按照查询出来的顺序结果,使用0,1来表示key

String query = "select new map(p.code,p.value) from Dictionary p";
List list = session.createQuery(query).list();
//结果
[{1=四川, 0=001}, {1=北京, 0=002}, {1=四川, 0=001}]

    使用map,指定alias,则结果中的key则为alias

String query = "select new map(p.code as code,p.value as value) from Dictionary p";
List list = session.createQuery(query).list();
//结果
[{value=四川, code=001}, {value=北京, code=002}, {value=四川, code=001}]

    如果部分使用alias,部分不使用,则使用了alias的将使用alias作为key,没有使用的则仍然使用序号代替,其中序号则为在查询结果的序号

String query = "select new map(p.code as code,p.value) from Dictionary p";
List list = session.createQuery(query).list();
//结果
[{1=四川, code=001}, {1=北京, code=002}, {1=四川, code=001}]

解决oracle中使用hibernate在某些条件下不能正确创建hibernate_sequence的问题

    项目中使用了ssh作为开发底层框架,由于在开发前期并没有限制项目运行在哪一个数据库之上,因此在hibernate的配置上使用Generated的id生成方式,使得能够在不修改源代码的情况下,无缝的在不同的数据库上跑,只需要修改配置文件中的dialect即可。
    在这种情况下,在oracle中,hibernate就会使用一个叫做hibernate_sequence的全局sequence来作为所有主键的生成sequence,在新增一条记录之上,均需要从此sequence中取一个数值,并作为其他记录的主键插入到数据库当中。

    然后,这个hibernate_sequence并不是在项目启动之前手动地创建的,而是使用了hiberante的create-update方式,即创建-修改的方式进行创建。既然是这样,那么hibernate在启动之前即需要去检测所要创建的sequence是否存在,如果存在,则不再创建此sequence。然而,在某些情况下,hibernate能够探测到其它用户所创建的sequence,而被认为不再需要创建sequence,这时候就会在项目运行中出错了。出错的原因,即找不到相应的sequence。
    这个错误和hibernate不会主动创建某些表一致,不一样的是hibernate用的是另一个判断语句来判断相应的sequence是否存在。
    相关链接:解决在oracle数据库中使用hibernate生成表不能正确创建表的问题.

继续阅读“解决oracle中使用hibernate在某些条件下不能正确创建hibernate_sequence的问题”

解决在oracle数据库中使用hibernate生成表不能正确创建表的问题

    最近在项目中使用hibernate的动态生成表,即将hbm2ddl.auto配置成update时,发现hibernate并没有按照默认的生成规则生成相应的数据表信息。但奇怪的是,只是部分表没有生成,而其它的表即生成成功了。重新启动项目,发现问题依旧。奇怪的是,虽然有些表没有生成,但它相关联的关联表即生成了,而且在生成时,会报一个找不到相关的引用表的错误。报的错误如下:

=2011-05-06 09:45:56 [org.hibernate.tool.hbm2ddl.SchemaUpdate]-[ERROR] Unsuccessful: alter table r_role_x_menu add constraint FK474DC862E1A553E2 foreign key (menu_id) references p_menu
=2011-05-06 09:45:56 [org.hibernate.tool.hbm2ddl.SchemaUpdate]-[ERROR] ORA-00942: 表或视图不存在

    找了半天,最后发现一个问题,即这里需要引用的表p_menu在另一个用户空间里已经存在了,而hibernate在创建表时,在另一个用户空间中找到了这个表,故不再在当前的用户空间中创建这个表了。而在创建关联表时,由于关联的是本用户空间的表,故有此错误。
    hibernate使用了jdbc默认的databasemeta来寻找相应表数据信息,当使用默认的配置时,由于某种原因(并不是每次都能发生,取决于数据库本身以及相应的驱动)。当使用当前用户连接到数据库时,使用databasemeta寻找数据库表信息时,会查询出其它用户的数据表信息(即使当前用户没有相应的权限)。
    解决此问题的方法很简单,只需要在hibernate.cfg.xml中配置一句:

<property name="default_schema">当前连接用户</property>

   这样,使用databasemeta时,就会强制性地在当前用户空间中寻找数据库信息了,这样就能正确的创建出表结构了。

hibernate非事务下session保存信息成功的问题

     我们在一般情况下使用session进行处理时,都使用了spring的HibernateTemplate或者是spring的事务控制。那么在这种情况下,所做的数据库操作,都是在事务的控制之下做的,如果没有事务控制的情况下,hibernate是不是就会回滚相应的事务呢?在这种情况下,是没有事务可言的,具体的操作情况取决于数据库驱动(jdbc)或者连接池提供者的具体实现。
    在大部分的实现里,使用session在进行一个insert操作时,在没有事务的情况下,这个操作仍然会成功。这是因为,大部分的jdbc驱动的connection的autocommit为true,它是处于自动提交模式的;而一般的连接池在提供connection时,也没有对它的自动提交模式作处理(除非特定的设置)。

继续阅读“hibernate非事务下session保存信息成功的问题”

Hibernate关联中对于mappedby 继承属性的描述和解决方法

    近期使用Hibernate进行开发,在开发的过程中碰到了几个问题,其中一些是由于不了解Hibernate,另外一些则属于设计或者Hibernate自身不支持一些操作。主要还是对Hibernate自身的一些东西都没有很好地了解,导致出了问题都往google上找,也不太知道其中的道理。现在把这些问题都列下来,以便以后容易查找。

继续阅读“Hibernate关联中对于mappedby 继承属性的描述和解决方法”