最近用到了Hibernate来作为数据处理部分的框架,离线criteria的确很好用,但在分页上确实让人头疼。要想分页就需要记录的总条数,如果只是一般的查询只需要
criteria.setProjection(Projections.rowCount());
就可以了,但如果是distinct的查询,记录条数和你所需查询的列有关,那么可以这样
Projections.countDistinct(propertyName);
问题来了,好像只能有一列啊。没错在标准sql中的distinct count也只能是一列的,但当有多列查询的时候怎么办呢?criteria有个接口可以设定结果值为distinct,就这样
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
但是这样做的问题在于,他是先执行,后在程序中distinct,这样会导致每页的记录可能不同,而且性能低下。
换个思路在写sql的时候可以这样写
select count(*) from (select distinct xxxxxx)
也就是说在外层套一个count 而真实的查询语句在from子句中,成为一个子查询。那看看如何变成Hibernate的形式吧。我在网站上搜索了好久,终于得出一个结论(经过Hibernate文档证明了)Hibernate不支持from子句中存在子查询,他们认为from应该是一个映射了的对象,子查询和他们的思想不符。
好吧,那再换个思路,Hibernate本身只是一个映射的过程,那么实际执行还是需要JDBC实现。如果是JDBC就需要sql语句,我们把criteria中蕴含的sql语句提取出来,然后加上我们外层的计数部分,然后再像Hibernate一样绑定参数就可以了,因为外层没有参数占位符,所以也不会影响Hibernate按位置绑定参数,因为没有接口,那只能读源码了。果然,hibernate最后通过sql(就是log中看到的那个)生成一个PreparedStatement,并通过方法绑定参数。废话少说,直接来个破冰之旅。
/**
* 因为需要修改sql,就要传入你新的sql,criteria必须是绑定session的
* 因为我们用到了session
* @param sql
* @param criteria
* @return
*/
public static List<List> wrapAndExecute(String sql, Criteria criteria) {
ResultSet rs = null;//最后的结果集
PreparedStatement ps = null;//我们自己的PreparedStatement
Connection connection = null;//session中获取的连接
try {
//先转型
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
// 获取SessionImplementor类型的session
SessionImplementor session = criteriaImpl.getSession();
// 获取factory
SessionFactoryImplementor factory = session.getFactory();
// 获取CriteriaQueryTranslator对象,按照Hibernate源码中的方式生成
CriteriaQueryTranslator translator = new CriteriaQueryTranslator(factory, criteriaImpl, criteriaImpl
.getEntityOrClassName(), CriteriaQueryTranslator.ROOT_SQL_ALIAS);
// 从CriteriaQueryTranslator对象中获取保存参数的QueryParameters对象
QueryParameters queryParameters = translator.getQueryParameters();
// 从factory中获取implementors,一般只用一个
String[] implementors = factory.getImplementors(criteriaImpl.getEntityOrClassName());
// 因为只用一个,所以只需要一个loader,按照Hibernate源码中的方式新建
// 注意有些地方由于非public所以需要用反射,反射部分代码就不贴了,注意invokeMethod要从子类找到父类,直到object
// 因为有些方法是写在父类中的
Loader loader = new CriteriaLoader((OuterJoinLoadable) ReflectUtil.invokeMethod(session,
"getOuterJoinLoadable", new Class[] { String.class }, new Object[] { implementors[0] }), factory,
criteriaImpl, implementors[0], ((SessionImpl) session).getLoadQueryInfluencers());
// 按照Hibernate源码方法获取walker
CriteriaJoinWalker walker = new CriteriaJoinWalker((OuterJoinLoadable) factory
.getEntityPersister(implementors[0]), translator, factory, criteriaImpl, criteriaImpl
.getEntityOrClassName(), session.getLoadQueryInfluencers());
// 获取了criteria的sql
String criteriaSql = walker.getSQLString();
// 用传入的sql代替,注意PLACEHOLDER代表criteriaSql的占位符
sql = sql.replace(PLACEHOLDER, criteriaSql);
// 获取session中的连接
connection = session.getJDBCContext().getConnectionManager().getConnection();
// 构建新的PreparedStatement
ps = connection.prepareStatement(sql);
// 注意一定要过滤过参数才能绑定
queryParameters.processFilters(criteriaSql, session);
// 通过反射绑定参数
ReflectUtil.invokeMethod(loader, "bindParameterValues", new Class[] { PreparedStatement.class,
QueryParameters.class, int.class, SessionImplementor.class }, new Object[] { ps, queryParameters,
1, session });
// 获取结果集
rs = session.getBatcher().getResultSet(ps);
......
呼,好了,终于抠出了criteria的东西了。
最后值得注意的是,由于ResultSet和PreparedStatement是自己创建的,所以需要自己维护,就是要注意关闭咯。至于connection由于是session中获取的,可以不用管,否则这个session如果还有其他操作connection关了就要抛错了。
分享到:
相关推荐
这意味着你需要为这个临时的子查询结果创建一个新的Java类,然后在Hibernate映射文件中定义这个类,使用`subselect`属性指定子查询的SQL语句。这样,Hibernate就会根据这个子查询来获取和管理数据,而不是直接操作...
在多表查询中,表之间的关联关系非常重要。 ##### 表中的数据 为了演示多表查询,我们继续使用前面提到的`student`、`course`和`sc`表。 ##### 修改持久化类 为了实现关联关系,需要在持久化类中进行相应的修改...
- HQL支持在SELECT、FROM、WHERE子句中使用子查询。 - 子查询可以返回单个值,也可以返回对象列表。 8. **参数化查询** - 使用问号(?)作为占位符,防止SQL注入攻击。 - 可以使用Query接口的setParameter()...
本文将深入探讨如何使用Hibernate实现递归查询,以解决在数据层次结构中涉及父节点与子节点关系时的问题。递归查询通常用于处理树形结构的数据,例如组织结构、菜单系统或者文件目录等。 首先,我们需要了解递归的...
14.2. from子句 14.3. 关联(Association)与连接(Join) 14.4. select子句 14.5. 聚集函数 14.6. 多态查询 14.7. where子句 14.8. 表达式 14.9. order by子句 14.10. group by子句 14.11. 子查询 14.12. HQL示例 14.13...
14.2. from子句 14.3. 关联(Association)与连接(Join) 14.4. join 语法的形式 14.5. select子句 14.6. 聚集函数 14.7. 多态查询 14.8. where子句 14.9. 表达式 14.10. order by子句 14.11. group by子句 ...
HQL允许在`WHERE`子句中使用子查询,例如: ```java query = session.createQuery("from User u where u in (select u2 from User u2 where u2.age > 30)"); ``` 这里查询所有年龄大于30岁的用户。 ### 组合查询 ...
- 在查询中使用JOIN操作,例如`from Customer as customer join fetch customer.buySet`实现Eager Loading,一次性加载关联数据;`from Customer as customer join customer.buySet`则表示Lazy Loading,仅在访问...
7. **子查询**:在SELECT或WHERE子句中使用子查询,提供更灵活的查询方式。 8. **动态实例化**:允许在查询中创建新的对象实例,例如"FROM User AS u WHERE u.email LIKE '%%' ORDER BY u.username DESC"。 HQL还...
2. 子查询:可以在HQL中嵌套查询,例如`select u from User u where u.id in (select a.userId from Address a where a.city='上海')`,找出所有有上海地址的用户。 3. 排序:使用`order by`子句可以对查询结果进行...
- **其他构造**:包括聚合函数、表达式、排序子句、分组子句、多态选择以及子查询。 ### 4. HQL与SQL的区别 - **面向对象**:HQL完全面向对象,能够处理继承、多态和关联等概念。 - **大小写敏感性**:HQL对查询...
14.2. from子句 14.3. 关联(Association)与连接(Join) 14.4. join 语法的形式 14.5. select子句 14.6. 聚集函数 14.7. 多态查询 14.8. where子句 14.9. 表达式 14.10. order by子句 14.11. group by子句 ...
在软件开发领域,尤其是...总的来说,当遇到类似问题时,开发者需要仔细检查SQL语句的语法和结构,同时评估是否可以通过调整查询策略、优化数据模型或利用其他工具来避免直接在Hibernate映射文件中使用复杂的子查询。
2. 避免在HQL中使用SQL函数:尽量用Hibernate提供的函数,以确保跨数据库兼容性。 3. 优化查询性能:合理设计数据库索引,避免全表扫描,尽量减少JOIN操作。 总之,Hibernate-HQL是Java开发中处理数据库查询的重要...
除了基本的查询,HQL 还支持更复杂的操作,如联接查询、子查询、聚合函数等。例如,如果你需要根据用户名查找用户,可以这样写: ```java String hql = "from User where username = :username"; query = session....
14.2. from子句 14.3. 关联(Association)与连接(Join) 14.4. join 语法的形式 14.5. select子句 14.6. 聚集函数 14.7. 多态查询 14.8. where子句 14.9. 表达式 14.10. order by子句 14.11. group by子句 ...
- **子查询**:可以在HQL中嵌套查询,如`from Cat as cat where cat.id in (select dog.id from Dog as dog)`。 HQL的灵活性和面向对象的特性使得它成为在Hibernate中进行复杂查询的理想选择。通过理解和熟练使用...
例如,可以使用子查询来找出所有平均成绩超过90分的课程:`FROM Course c WHERE AVG(SELECT sc.grade FROM SC sc WHERE sc.cno = c.cno) > 90`。 QBC(Query By Criteria)是另一种查询方式,它提供了一种基于Java ...