Hibernate实战(第2版)学习笔记七

131.报表查询
报表查询利用数据库执行有效的数据分组和聚集的能力。它们天生更为相关;它们并非始终返回实体。
HQL和JPA QL允许使用几个最常用于报表的SQL特性——虽然它们也用于其他目的。在报表查询中,给投影使用SELECT子句,给聚集使用GROUP BY和HAVING子句。
(1)利用统计函数投影
HQL识别的并在JPA QL中标准化的统计函数式count()、min()、max()、sum()和avg()。
特殊的COUNT(DISTINCT)函数忽略重复:
select count(distinct i.description) from Item i
(2)给统计结果分组
就像在SQL中一样,出现在HQL或者JPA QL中但在SELECT子句中的一个统计函数之外的任何属性或者别名,也必须出现杂GROUP BY子句中。
(3)用having限制分组
WHERE子句用来在行上执行限制的相关操作。HAVING子句在分组上执行限制。
管理SELECT和HAVING子句的规则一样:只有被分了组的属性才可能出现在统计函数之外。
(4)利用动态的实例化
一般用于报表查询的,字节组(tuple)特别不方便,因此HQL和JPA QL提供了SELECT NEW构造器嗲用。除了用这种技术动态创建新对象之外,也可以把它与聚集和分组组合起来使用。
如果用含有Long、Long和BigDecimal的构造器,定义一个称作为itemBidSummary的类,就可能用到下列查询:
select new ItemBidSummary(bid.item.id,count(id),avg(bid)))from Bid bid
where bid.item.successfulBid is null
group by bid.item.id
注意,此处必须编写一个完全限定的类名,带有包名,除非类已经被导入到HQL命名空间。这个方法是类型安全的,并且数据前一类可以轻松地对报表中值得特殊格式打印进行扩展。
(5)利用报表查询提示性能
报表查询导致被分配的内存的更快是否,因为对象没有被保存在持久化上下文高速缓存中,知道上下文关闭——执行报表之后,一旦它们没有被应用程序引用,可能就立即被当做垃圾收集起来。
132.利用子查询
子查询是内嵌在另一个查询中的查询,一般在SELECT、FROM或者WHERE子句中。
HQL和JPA QL支持WHERE中的子查询。FROM子句中的子查询不受HQL和JPA QL的支持(虽然规范把它们列为一项可能的未来扩展),因为这两种语言都没与传递闭包。查询的结果可能不是表格,因此不能在FROM子句中重新用于选择。SELECT子句中的子查询也不受查询语言的支持,但可以通过公式(formula)映射到属性。
(1)相关的和不相关的嵌套
from User u where 10<(
    select count(i) from u.items i where i.successfulBid is not null
)
from Bid bid where bid.amount+1>=(
    select max(b.amount) from bid b
)
(2)量词
如果一个子查询返回多行,它就是与量词(quantification)组合了。ANSI SQL、HQL和JPA QL定义了下列量词:
ALL——如果对子查询的结果中所有值的比较为真,表达式就取值为true。如果子查询结果的单个值没有通过比较测试,就取值为false。
ANY——如果对子查询的结果中有些(任何)值的比较为真,表达式就取值为true。如果子查询结果为空或者没有值满足比较,就取值为false。关键字SOME是ANY的同义词。
IN——这个二进制的比较操作符可以针对一个子查询的结果比较一系列值,并且如果在结果中找到所有值,则取值为true。
例如这个查询返回所有出价都小于100的货品:
from Item i where 100>all(select b.amount from i.bids b)
下一个查询返回所有其他的货品,即出价大于100的:
from Item i where 100<=any(select b.amount from i.bids b)
这个查询返回出价正好为100的货品:
from Item i where 100= some(select b.amount from i.bids b)
这个也一样:
from Item i where 100 in (select b.amount from i.bids b)
HQL对在元素或者集合的索引上操作的子查询支持快捷语法。
List result=session.createQuery("from Category c where :givenItem in elements(c.items)").setEntity("givenItem",item).list();
133.Criteria和Example API都只可在Hibernate中使用;Java Persistence没有标准化这些接口。
最简单的条件查询看起来像这样:
session.createCriteria(Item.class);
它获取Item类的所有持久化实例。这也称作条件查询的根实体(root entity)。
(1)应用限制
对于条件查询来说,你必须构建Criterion对象来表达约束。Restrictions类给内建的Criterion类型提供工厂方法。
session.createCriteria(User.class).add(Restrictions.eq("homeAddress.street","Foo"));
(2)创建比较表达式
所有一般的SQL(和HQL、JPA QL)比较操作符也可以通过Restrictions类使用:
Criterion restriction=Restrictions.between("amount",new BigDecimal(100),new BigDecimal(200));
三重逻辑操作符也可用;这个查询返回没有电子邮件地址的用户:
session.createCriteria(User.class).add(Restrictions.isNull("email"));
(3)字符串匹配
对于条件查询,通配符搜索可以使用与HQL和JPA QL相同的通配符符号(%和_),或者指定MatchMode。MatchMode是不用字符串操作来表达子字符串匹配的一种方便的方法。
得到之后的MatchMode是START、END、ANYWHERE和EXACT。
(4)组合表达和逻辑操作符
如果把多个Criterion实例添加到同一个Criteria实例,它们被联结起来应用(利用and)
(5)添加任意的SQL表达式
session.createCriteria(User.class).add(Restrictions.sqlRestriction("length{alias}.PASSWORD<?",5,Hibernate.INTEGER));
这个查询返回密码少于5个字符的所有User对象。
(6)编写子查询
条件查询中子查询是一个WHERE子句查询。就像在HQL、JPA QL和SQL中一样,子查询的结果可能包含单行或多行。
134.联结和动态抓取
(1)给限制联结关联
在Criteria API中表达联结有两种方法:因此你有两种给限制使用别名的方法。第一种是Criteria接口的createCriteria()方法。这个方法一般意味着你可以嵌套对createCriteria()的调用。这个方法一般意味着你可以嵌套对createCriteria()的调用:
Criteria itemCriteria=session.createCriteria(Item.class);
itemCriteria.add(
    Restrictions.like("description","Foo",MatchMode.ANYWHERE)
);
用Criteria API表达内部联结的第二种方法是为被联结的实体分配一个别名:
session.createCriteria(Item.class).createAlias("bids","b")
.add(Restrictions.like("description","%Foo%"))
.add(Restrictions.gt("b,amount",new BigDecimal(99)));
(2)通过调节查询动态获取
在HQL和JPA QL中,你用join fetch操作即时填入一个集合,或者初始化一个被映射为延迟否则将被代理的对象。你可以用Criteria API完成同样的事:
session.createCriteria(Item.class).setFetchMode("bids",FetchMode.JOIN)
.add(Restrictions.like("description","%Foo%"));
并行即时抓取不止一个集合,会造成一个SQL笛卡尔积,它或许比两个单独的查询更慢。如果你给集合使用即时抓取,那么给限制分页结果集也在内存中完成。
可以在jieguoList中移除重复的引用,通过把它包装在LinkedHashSet中(常规的HashSet不保存查询结果的顺序)。在HQL和JPA QL中,也可以使用DISTINCT关键字;然而,在Criteria中,没有它的直接等加入。这就是ResultTransformer变得有用的地方。
(3)应用结果转换器
结果转换器可以被应用到查询结果,以便你可以用自己的过程而不是Hibernate的默认行为过滤或者封送结果。Hibernate的默认行为时你可以自己替换和(或定制的一组默认转换器)。
Criteria.ROOT_ENTITY是org.hibernate.transform.ResultTransformer接口的默认实现。前一个查询无论是否使用这个转换器设置都生成相同的结果。
135.投影和报表查询
(1)简单的投影列表
Criteria中的setProjection()方法,接受任何单个的被投影的属性,或者接受要包括在结果中的几个属性的的一个列表。
(2)统计和分组
一般的统计函数和分组选项在条件查询中也可以用。有一种简单的方法统计结果中的行数:
session.createCriteria(Item.class).setProjection(Projections.rowCount())
session.createCriteria(Bid.class).createAlias("bidder","u")
.setProjection(Projections.projectionList())
.add(Projections.groupProperty("u.id"))
.add(Projections.groupProperty("u.username").as("uname"))
.add(Projections.count("id"))
.add(Projections.avg("amount"))
)
.addOrder(Order.asc("uname"));
(3)利用SQL投影
SQL投影是被添加到生成的SQL SELECT子句的一个任意片段。
String sqlFragment="(select count(*) from ITEM i where i.ITEM_ID=ITEM_ID) as numOfItems";
session.createCriteria(Bid.class).createAlias("bidder","u")
.setProjection(Projections.projectionList()
.add(Projections.groupProperty("u.id"))
.add(Projections.groupProperty("u.username"))
.add(Projections.count("id"))
.add(Projections.avg("amount"))
.add(Projection.sqlProjection(
sqlFragment,
new String[]{"numOfItem"},
new Type[]{HIbernate.LONG})
));
136.按示例查询
public List findUsers(String firstname,String lastname)
{
    Criteria crit=getSession().createCriteria(User.class);
    if(firstname!=null)
    {
        crit.add(Restrictions.ilike("firstname",firstname,MatchMode.ANYWHERE));
    }
    if(lastname!=null)
    {
        crit.add(Restrictions.ilike("lastname",lastname,MatchMode.ANYWHERE));
    }
    crit.addOrder(Order.asc("username"));
    return crit.list();
}
public List findUserByExample(User u) throws{
    Example exampleUser=Example.create(u)
                                                          .ignoreCase()
                                                          .enableLike(MatchMode.ANYWHERE)
                                                          .excludeProperty("password") ;
    return getSession().createCriteria(User.class).add(exampleUser).list();
}
137.利用原生的SQL查询
(1)自动结果集处理
利用Hibernate API执行SQL语句的最大好处在于,自动把表格式的结果集封送到业务对象中去。
List result=session.createSQLQuery("select * from CATEGORY").addEntity(Category.class).list();
(2)获取标量值
标量值可以是任何Hibernate值类型。
List result=session.createSQLQuery("select * from ITEM").list();
这个查询的result是Object[]的一个List,实际上是一张表。每个数组中的每个字段都是标量类型即字符串、数字或者时间戳。除了包装在一个Object[]中之外,结果与类似的简单JDBC查询结果完全一样。
如果没有用*投影每件东西,就要告诉Hibernate你想要从结果中返回哪些标量值:
session.createSQLQuery("select u.FIRSTNAME as fname from USER u").addScalar("fname");
addScalar()方法告诉Hibernate,你的fname SQL别名应该作为标量值返回,并且类型应该被自动推测。
(3)Java Persistence中的原生SQL
Java Persistence通过在EntityManager中用createNativeQuery()方法支持原生的SQL查询。原生的SQL查询可以返回实体实例、标量值或者两者的混合。然而,不同于Hibernate,Java Persistence中的API利用映射元数据来定义结果集处理。
138.过滤集合
集合过滤器——可以应用到持久化集合(或者数组)的一种特殊查询。它常用于进一步限制或者排序结果。
Hibernate集合过滤器不是在内存中执行的。此外,不要把过滤器应用到瞬时的集合或者查询结果。它们只能应用到当前被添加到Hibernate持久化上下文的一个实体实例引用的持久化集合。术语“过滤器”有点误导,因为过滤的结果是一个全新的不同集合,并没有接触到原始的集合。
139.高速缓存查询结果
高速缓存查询结果是一个完全不同的问题。查询结果高速缓存在默认情况下是禁用的,并且每一个HQL、JPA QL、SQL和Criteria查询都始终先命中数据库。
(1)启用查询结果高速缓存
查询高速缓存必须用Hibernate配置属性来启用:
hibernate.cache.use_query_cache=true
然而,单独这个设置对于Hibernate高速缓存查询结果还不够。默认情况下,所有的查询都始终忽略高速缓存。为了个特定的查询启用查询高速缓存(允许它的结果被添加到高速缓存,并允许从高速缓存中提取结果),那么使用org.hibernate.Query接口。
Query categoryByName=session.createQuery("from Category c where c.name=:name");
categoryByName.setString("name",categoryName);
categoryByName.setCacheable(true);
setCacheable()方法启用了结果高速缓存。它在Criteria API上也可用。如果想给javax.persistence.Query启用结果高速缓存,就使用setHint("org.hibernate.cacheable",true);
高速缓存实体的状态,是二级高速缓存区域auction.model.Category(结合持久化上下文)的责任。这类似于iterate()的查询策略,如前所述。换句话说,如果你查询实体,并决定启用高速缓存,就要确保你也给这些实体启用了常规的二级高速缓存。如果没有,可能在启用高速缓存之后,以更多的数据库命中而告终。
如果查询结果高速缓存在Hibernate中被启用,另一个始终需要的高速缓存区域也出现了:org.hibernate.cache.UpdateTimestampsCache。这是Hibernate内部使用的一个高速缓存区域。
Hibernate用时间戳区域来决定被高速缓存的查询结果集是否为失效的。当你重新执行一个启用了高速缓存的查询时,Hibernate就在时间戳高速缓存中查找对被查询的(几张)表所做的最近插入、更新、或者删除的时间戳。如果找到时间戳晚于高速缓存查询结果的时间戳,高速缓存结果就被丢弃,并产生一个新查询。
通常要把构成自然键的属性映射为Hibernate中的常规属性。可以在数据库级启用一个unique约束来表示这个键。

猜你喜欢

转载自bsr1983.iteye.com/blog/2099131
今日推荐