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

121.给结果进行分页
常用的方法是分页(pagination)。用户可以看到他们搜索请求的结果页面。这个页面一次显示有限的子集,用户可以手工导航到后一个和前一个页面。在Hibernate中,Query和Criteria接口支持查询结果的这种分页:
对setMaxResults(10)的调用把查询结果集限制为由数控返回的前10个对象(行)。
在SQL中表达分页并没有标准的方法——Hibernate指导这些技巧,使得它能在特定的数据库中有效地工作。
Java Persistence查询接口也通过javax.persistence.Query接口,对JPA QL和原生的SQL查询支持分页和方法链。
122.考虑参数绑定
参数绑定(parameter binding)有两种方法:利用定位或者利用具名参数。Hibernate和Java Persistence都支持这两个选项,但是你无法给一个特定的查询同时使用这两个选项。
利用具名参数,可以重写这个查询为:
String queryString="from Item item where item.description like :search";
参数名称后面的冒号表示这是一个具名参数,然后,对search参数绑定一个值:
Query q=session.createQuery(queryString).setString("search",seachString);
同样的查询和代码,则Java Persistence中看起来稍有不同:
Query q=em.createQuery(queryString).setParameter("search",searchString)
.setParameter("minDate",mDate,TemporalType.DATE);
123.利用Hibernate的参数绑定
原生的Hibernate Query接口提供类似的便利方法,用来绑定大多数Hibernate内建类型的参数:从setInteger()到setTimestamp()和setLocale()的一切。它们中大多数是可选的;可以依赖setParameter()方法自动找出正确的类型(除了临时的类型之外)。
特别有用的一种方法是setEntity(),它让你绑定一个持久化实体。
setProperties()绑定使得JavaBean属性的名称与查询字符串中的具名参数一致,内部调用setParameter()来猜测Hibernate类型并绑定值。
124.利用定位参数
如果喜欢,可以在Hibernate和Java Persistence中用定位参数代替:
String queryString="from Item item where item.description like ? and item.date>?";
Query q=session.createQuery(queryString).setString(0,searchString).setDate(1,minDate);
Java Persistence也支持定位参数:
String queryString="from Item item where item.description like ?1 and item.date>?2";
Query q=em.createQuery(queryString).setParameter(1,searchString).setParameter(2,minDate,TemporalType.DATE);
如果必须使用定位参数,要记住Hibernate是从0开始计数,而Java Persistence则是从1开始计数,必须给JPA QL查询字符串中的每一个问号都添加数字。他们有着不同的遗留根:Hibernate的根是JDBC,Java Persistence的根是更早版本的EJB QL。
125.假设你在执行查询之前修改了持久化对象。这些修改只出现在内存中,因此Hibernate(和Java Persistence提供程序)在执行查询之前,把持久化上下文和所有的变化清除到数据库。这样保证了查询在当前的数据中进行,并保证在查询结果和内存之间不会出现冲突。
可以在Session或者EntityManager中,利用setFlushMode()禁用持久化上下文的清除。或者,如果你想要只在特定的查询之前禁用清除,可以在Query(Hibernate和JPA)对象中设置一个FlushMode:
Query q=session.createQuery(queryString).setFlushMode(FlushMode.COMMIT);
Query q=em.createQuery(queryString).setFlushMode(FlushMode.COMMIT);
Hibernate不会在执行任何这些查询之前清除持久化上下文。
给特定的持久化对象禁用脏查询的一种方法是设置session.setReadOnly(object,true);
在SQL中,查询提示是SQL语句中的一条注释,它给数据库管理系统的SQL优化器包含了一条指令。Hib和Java Persistence的API不支持任意的SQL提示,必须退回到原生的SQL并编写自己的SQL语句——当然,你可以通过所提供的API来执行语句。
    最后,可以控制查询是否应该在数据库管理系统中强制悲观锁——这是一只持续到数据库事务结束的锁:
    Query q=session.createQuery("from Item item").setLockMode("item",LockMode.UPGRADE);
    Criteria criteria=session.createCriteria(Item.class).setLockMode(LockMode.UPGRADE);
    这两个查询,如果得到数据库方言的支持,都会伸出一个包括...FOR UPDATE操作(或者其等价物,如果得到数据库系统和方言的支持的话)的SQL声明。
126.执行查询
    (1)列出所有结果
    在Hibernate中,list()方法执行查询并返回结果为java.util.List:
    List result=myQuery.list();
    List result=myCriteria.list();
    Java Persistence用相同的语义提供了一种方法,但是用不同的名称:
    List result=myJPAQuery.getResultList();
    (2)循环访问结果
    Hibernate Query接口也提供iterate()方法来执行查询。它返回与list()一样的数据,但是依赖不同的策略来获取结果。
    调用iterate()执行查询时,Hibernate在第一个SQL SELECT中只获取实体对象的主键(标识符)值,然后视图在持久化上下文高速缓存和(如果启用)二级高速缓存中查找对象的其他状态。
    Hibernate保持迭代器开着,直到你循环访问了所有的结果,或者直到Session关闭。也可以通过org.hibernate.Hibernate.close(iterator)显式地关闭它。
    (3)利用数据库游标滚动
    简单的JDBC提供一种特性,称作可滚动的结果集(scrollable resultset)。这种方法使用一种保存在数据库管理系统中的游标。游标指向查询结果中一个特定的行,应用程序可以前后移动游标。甚至可以通过游标跳到一个特定的饿行。
    应该翻阅查询结果(而不是把他们全部加载到内存中)的其中一种情形是结果集太大而无法载入内存。通常可以尝试通过增加查询中的条件进一步限制结果。但有时候这是不太可能的,或许因为你需要所有的数据,却又想要分几步获取。
    ScrollableResults itemCursor=session.createQuery("from Item").scroll();
    执行Hibernate Criteria查询也可以通过滚动而不是list(),返回的ScrollableResults游标的工作原理相同。注意,在终止数据库事务之前、用完游标之后绝对必须关闭它。
127.Hibernate让你把查询字符串具体化到映射元数据,这种技术称作为具名查询(named query)。它让你把与特定的持久化类或者一组类(该类封装了它的其他元数据)相关的所有查询都保存在一个XML映射文件中。或者,如果使用注解,就可以创建具名查询作为特定实体类的元数据,或者把它们放在一个XML部署描述符中。查询的名称被用来从应用程序代码中进行调用。
<query>定义具名的HQL或者JPA QL查询。
具名查询不一定是HQL或者JPA QL字符串;它们甚至可以是原生的SQL查询——并且你的Java代码也不需要知道这种差别:
<sql-query name="findItemsByDescription">
    <return alias="item" class="Item">
    <![CDATA[select {item.*} from item where description like :desc ]]>
</sql-query>
Java Persistence标准制定@NameQuery和@NamedNativeQuery注解。可以把这些注解放进特定类的元数据内,也可以放在JPA XML描述符文件内。注意,查询名称在任何情况下都必须是全局唯一的;不会自动加上任何类或者包名称作为前缀。
138.我们应用选择来指定数据源,用限制使记录与条件相匹配,并用投影选择想要从查询中返回的数据。
HQL和JPQ QL对于关键字并不区分大小写。
作为面向对象的查询语言,HQL和JPA QL支持多态查询(polymorphic query)——相应地查询类的实例和它子类的所有实例。WHERE子句用来表达SQL、HQL和JPA QL中的限制。这些表达式可能跟你正在寻找的需要缩小的代码块一样复杂。
可以在语句和条件中用单引号包括文字。HQL和JPA QL中常用的其他文字是TRUE和FLASE。
什么是三重逻辑?当且仅当WHERE子句取值为true时,行才被包括在SQL结果集中。在Java中,notNullObject==null取值为false,null==null取值为true。在SQL中,NOT_NULL_COLUMN=null和null=null都取值为null,而不是true。这样SQL就需要一个特定的操作符IS NULL来测试一个值是否为null。这个三重逻辑是处理表达式的一种方法,可以应用到空的列值。不要把null当做特殊的标记,而是作为普通的值处理,这是对关系模型中常见的二重逻辑的SQL扩展。HQL和JPA QL必须利用三重操作符支持这个三重逻辑。
(1)比较表达式
    HQL和JPA QL提供SQL风格的IS [NOT] NULL操作符。
    LIKE操作符允许通配符搜索,在这里通配符符号是%和_,就像SQL中的一样:
    from User u where u.firstname like 'G%'
(2)包含集合的表达式
    from Item i where i.bids is not empty
    from Item i, Category c where i.id = '123' and i member of c.items
(3)调用函数
    HQL一项极为强大的特性是在WHERE子句中调用SQL函数的能力。
   标准的JPA QL函数
  UPPER(s),LOWER(s)                            字符串值。返回一个字符串值。
  CONCAT(s1,s2)                                    字符串值。返回一个字符串值。
  SUBSTRING(s,offset,length)                字符串值(从1开始偏移)。返回一个字符串值。
  TRIM([[BOTH|LEADING|TRAILING] char [FROM] s) 覆盖没有指定char或者其他的规范,就去除s两边的空白。返回一个字符串值。
   LOCATE(search,s,offset)                     从offset开始搜索s字符串中search字符串所在的位置。返回一个数字值。
  ABS(n),SQRT(n),MOD(dividend,divisor)   数字值。返回一个与输入类型相同的绝对值,double类型的平方根,以及一个整数除以另一个整数之后产生的integer类型的余数。
  SIZE(c)                                                 集合表达式。返回一个integer,或者如果为空则返回0
所有标准的JPA QL函数都可以用在查询的WHERE和HAVING子句中。
其他的HQL函数
BIT_LENGTH(s)                                                               返回s的位数
CURRENT_DATE(),CURRENT_TIME(),CURRENT_TIMESTAMP()       返回数据库管理系统机器的日期和(或)时间
SECOND(d),MINUTE(d),HOUR(d),
DAY(d),MONTH(d),YEAR(d)                                            从临时的实参中提取时间和日期
CAST(t as Type)                                                              把指定的类型t转换为Hibernate的Type
INDEX(joinedCollection)                                               返回被联结的集合元素的索引
MINELEMENT(c),MAXELEMENT(c),
MININDEX(c),MAXINDEX(c),
ELEMENTS(c),INDICES(c)                                              返回被索引集合(映射、列表、数组)的元素或者索引
已经在org.hibernate.Dialect中注册的                          以一种方言扩展包含其他函数的HQL
(4)排序查询结果
HQL和JPA QL提供了ORDER BY子句(类似于SQL)。
129.SELECT子句在HQL和JPA QL中执行投影。它允许你准确指定需要查询结果中的哪些对象或者对象的哪些属性。
(1)实体和标量值的简单投影
Query q=session.createQuery("from Item i,Bid b");
//Query q=em.createQuery("select i,b from Item i,Bid b");
Iterator pairs=q.list().iterator();
//Iterator pairs=q.getResultList().iterator();
while(pairs.hasNext()){
    Object[] pair=(Object[])pairs.next();
    Item item=(Item)pair[0];
    Bid bid=(Bid)pair[1];
}
这个查询返回Object[]的一个List。索引0处为Item,索引1处为Bid。因为这是一个乘积,结果包含了在两张底层的表中找到的Item和Bid行的每一种可能的组合。
(2)获取独特的结果
当你使用SELECT子句时,结果的元素不再保证是唯一的。如果你认为可能有重复,一般就使用DISTINCT关键字。
(3)调用函数
 (对于某些Hibernate SQL方言来说)也可能从SELECT子句中调用数据库特定的SQL函数。
SELECT子句中数据库函数的技术不受限于依赖数据库的函数。它也适用于其他更多一般的(或者标准的)SQL函数。
Hibernate不知道的函数并没有作为SQL函数调用被传递到数据库,就像在WHERE子句中一样。你必须在org.hibernate.Dialect中注册一个函数,在HQL中的SELECT子句中启用它。
130.连接关系和关联
(1)HQL和JPA QL连接选项
在Hibernate查询中,通常不显示指定联结条件,而是指定被映射的Java类关联的名称。
事实上,HQL和JPA QL提供了4中表达(内部和外部)联结的方法:
  • 隐式关联联结;
  • FROM子句中的普通联结;
  • FROM子句中的抓取联结;
  • WHERE子句中的theta类型的连接。
(2)隐式关联联结
    HQL和JPA QL支持大部分属性路径表达式,用一个点号表示两种不同的目的:
  •     查询组件
  •     表达式隐式关联联结。
隐式联结始终采用多对一或者一对一关联,从来不通过集合值的关联。
(3)在FROM子句中表达的联结
from Item i left join i.bids b with b.amount>100 where i.description like '%Foo%'
可以选择编写LEFT OUTER JOIN和RIGHT OUTER JOIN,但是我们通常更喜欢简短的形式。第二个变化是WITH关键字后面的额外联结条件。
注意包含WITH关键字的额外联结条件只在HQL中可用;JPA QL只支持由被映射的外键关联所表示的基本的外部联结条件。
(4)利用联结的动态抓取策略
在HQL和JPA QL中,可以指定被关联的实体实例或者集合应该在FROM子句中通过FETCH关键字即时抓取。
from Item i left join fetch i.bids where i.description like '%Foo%'
(5)Theta类型的联结
乘积让你获取两个或者多个类的实例的所有可能的组合。这个查询返回所有有序的User和Category对象对:
from User,Categroy
很显然,这通常没用。它常用语一个案例:theta类型的联结。
在传统的SQL中,theta类型的联结是一个笛卡尔积和WHERE子句中的一个联结条件。在乘积上应用它用来限制结果。
在HQL和JPA QL中,当联结条件不是一个被映射到类关联的外键关系时,theta类型的语法就很有用。例如,假设你在日志记录中保存了User的名称,而不是映射一个从LogRecord到User的关联。类与类相互之间不知道对方的任何信息,因为它们没有被关联。然后可以通过下列theta类型的联结,找到所有User和它们的LogRecord:
from User user,LogRecord log where user.username=log.username
此处的联结条件是username的一个比较,在两个类中都以属性出现。如果两个类都有相同的username,就在结果中被联结(用一个内部联结)。这个查询结果由有序的对组成。
(6)比较标识符
如果以更面向对象的方式考虑标识符比较,实际上就是正在比较对象引用。HQL和JPA QL支持下列查询:
from Item i,User u where i.seller=u and u.username='steve'

猜你喜欢

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