1.深入Hibernate
一、Hibernate数据查询
HQL:Hibernate查询语言,它是Hibernate提供的一种面向对象的查询语言。
(1)在查询语句中设定各种查询条件
(2)支持动态绑定参数
(3)支持投影查询、分页查询、连接查询、分组查询,子查询
(4) 内置了一些聚集函数
Hibernate中的Query接口就是专门用来执行HQL语句的查询接口。
1.创建查询对象
Query query=session.createQuery(“from Dept”);
2.执行查询列出结果
1)列出所有的结果
List<Dept> depts=query.list();
for(Dept dept:depts){ …}
2)列出单个结果
Query query=session.createQuery(“from Dept where id=1”);
query.setMaxResults(1);
Dept dept=(Dept)query.uniqueResult();
3)迭代访问结果
Iterator<Dept> it=query.iterate();
While(it.hasnext()){
}
二、HQL 基本语法:持久化类名区分大小写
1)选择要查询的持久化类。
String hql=“from Dept”
Query query=session.createQuery(hql);
String hql=“from Dept as d”;
2)投影查询
String hql="select id,name from Dept";
Query query=session.createQuery(hql);
List list=query.list();
int size=list==null?0:list.size();
for(int i=0;i<size;i++){
Object[] obj=(Object[])list.get(i);
System.out.println(obj[1]+“ ”+obj[2]);
}
针对选定属性创建一个类,提供以这两个属性为参数的构造方法,使用此类来封装查询结果。
3)where 条件子句
String hqlString="from Dept d where d.createdTime>='2010-01-01'";
Query query=session.createQuery(hqlString);
String hqlString="from Dept d where d.name like'java%' and d.employees is not empty and d.createdTime<current_date()";
4)绑定查询参数:
按参数名字绑定
String hql=“from Dept where id>:id and name like:likeName”
List<Dept> depts=session.createQuery(hql).setLong(“id”,new Long(ingputID)).setString(“likeName”,”%”+inputName+”%”).list();
按参数位置绑定
5)distinct 过滤重复值
Select distinct createTime from Dept
6)聚集函数
select count(d) from Dept d
7)order by 排序
From Dept d order by d.createdTime asc,d.name desc
8)group by 对记录分组
Select count(e) from Employee e group by e.dept;
9)having 对分组后数据进行条件过滤
Select e.dept.name from Eployee e group by e.dept having count(e)>1
三、分页查询
Query接口提供两个用于分批显示查询结果的方法
setFirstResult(int firstResult):在记录集中从第几条记录开始取数据。默认是0。
setMaxResults(int maxResults):设置每次查询返回的最大对象数。
session.beginTransaction();
String hql = "from Employee";
List<Employee> empls = session.createQuery(hql)
.setMaxResults(pageSize)
.setFirstResult((pageNo - 1) * pageSize)
.list();
session.getTransaction().commit();
批量修改和删除:直接通过SQL语句操作数据库,执行完后,当前session缓存中的数据与数据库中的数据不是同步的。
String hql=“delete Dept as d where d.name like:likeName”;
Query query=session.createQuery(hql).setString(“likeName”,”%三%”);
int count= query.executeUpdate();
四、连接查询
Inner join left outer join
Right outer join full join
String hql = "FROM Dept d JOIN d.employees e "
+ " WHERE d.name LIKE :likeName";
抓取连接查询 fetch
抓取:是指从数据库加载一个对象数据时,同时把它所关联的对象和集合的数据一起加载出来,以便减少SQL语句的数据,从而提高查询效率。
From Dept d left join Fetch d.employees e where d.name like “%web%”
子查询
From Dept d where (Select count(e) from d.employees e)>1
命名查询:是指将SQL查询语句编写在映射文件里,在程序中通过Session的getNameQuery()方法来获取该查询语句。
<query name="findDeptsByCondition">
<![CDATA [from Dept d where d.name like :likeName ]]>
</query>
session.getNamedQuery("findDeptsByCondition")
.setString("likeName", "%a%")
.list();
命名查询:是指将SQL查询语句编写在映射文件里,在程序中通过Session的getNameQuery()方法来获取该查询语句。
<query name="findDeptsByCondition">
<![CDATA [from Dept d where d.name like :likeName ]]>
</query>
session.getNamedQuery("findDeptsByCondition")
.setString("likeName", "%a%")
.list();
五、Criteria Queries
Criteria叫标准化条件查询,是比HQL更面向对象的查询语句。称为QBC
Criteria接口:代表一个查询。它是一个查询条件的容器,通过add()方法向其实例中添加查询条件。
Criterion接口:代表一种面向对象的查询条件。它的实例是作为Criteria接口add()方法的参数添加到Criteria实例中的。
Restrictions类:是用来创建查询条件Criterion实例的工具类,它提供了一系列的静态方法用来设定查询条件并作为Criterion实例返回。
Criteria criteria = session.createCriteria(Employee.class);
Criterion criterion1 = Restrictions.ge("salary", new Double(4000.00));
Criterion criterion2 = Restrictions.like("loginName", "z%");
criteria.add(criterion1);
criteria.add(criterion2);
criteria.addOrder(Order.desc("id"));
List<Employee> list = criteria.list();
List<Employee> list2= session.createCriteria(Employee.class)
.add(Restrictions.ge("salary", new Double(4000.00)))
.add(Restrictions.like("loginName", "z%"))
.addOrder(Order.desc("id"))
.list();
六、离线查询
DetachedCriteria类支持离线查询。所谓离线查询就是指一个Session范围之外创建好一个查询,然后在需要使用是再附加到一个Session实例上来执行它。
对于分层的web应用程序来说,web层需要传递一个查询条件列表给业务逻辑层,业务层对象获得这个条件后依次取出条件值,然后拼装出SQL查询语句。这里的一个难点是如何将用户的多项查询条件传入业务逻辑层。
Criteria与session是绑定的。
在web层,使用DetachedCriteria来构造查询条件,然后将这个DetachedCriteria作为方法调用参数传递给业务逻辑层对象。而业务层对象获得DetachedCriteria之后,可以在session范围内直接构造Criteria,进行查询。这样,查询语句的构造脱离了session范围,完全被移植到web层实现。
//StudentDAO.java
public List findStudents(DetachedCriteria detchedCriteria){
List list=null;
//打开session,开启事务
Criteria Criteria=detachedCriteria.getExecutableCriteria(session);
list=criteria.list();
//提交事务,关闭session
return list;
}
DetachedCriteria detchedCriteria=DetachedCriteria.forClass(Student.class);
String name=“获取名字”;
String age=“获取班级”;
if(name!=null&&name!=“”){ //如果填写了名字,添加查询名字的条件
detchedCriteria.add(Restrictions.like(“name”,name));
}
If(classes!=null&&classes!=“”}{
detchedCriteria.add(Restrictions.eq(“classes”,classes));
}
detchedCriteria.addOrder(Order.asc(“name”);
List list=StudentDAO.findStudents(detchedCriteria);
for(){ …}
七、示例查询(QBE)
根据一个给定的实例类实例来构建一个条件查询的方式。
先创建一个对象样板,然后检索出所有和这个样板相同的对象。
在查询表单中填写的项,可以封装成一个对象,这就是对象样板。
public static void testQBE(Employee employee){
//开启事务
//根据传入的employee实例来创建查询条件
Example example = Example.create(employee)
.excludeZeroes() //排除0值的属性
.excludeProperty("color") //排除指定的属性
.ignoreCase() //对所有的字符串类型的属性值忽略大小写比较
.enableLike(); //对所有的字符串类型的属性值使用like比较
List<Employee> results = session.createCriteria(Employee.class)
.add(example) .list();
for (Employee empl : results) {
System.out.println(empl.getLoginName() + "," + empl.getSalary()); }
//提交事务 //关闭Session }
Employee empl=new Employee();
empl.setLonginName(“%z%”);
testQBE(empl);
八、Native SQL Queries
原生SQL查询,就是指直接使用标准SQL语句或特定数据库的SQL进行查询。对原生SQL查询执行的控制是通过SQLQuery接口进行的,通过Session上调用createSQLQuery()来获取这个接口。
1.实体查询
Hibernate执行原生SQL查询后,自动把查询到的表格式的数据集封装到实体对象中。
九、小结
HQL功能最强大,适合各种情况,但动态查询构造起来不方便。
Criteria最适合动态条件查询,不太适合统计查询,QBE还不够强大,只适合相当简单的查询。
NativeSQL可以实现特定数据库的SQL,但可移植不好。
针对Web应用来说,动态查询,首先Criteria,但是涉及统计查询和非常复杂的关联查询,Criteria就无能为力了,这种情况下选择HQL。
HQL常用来进行实体检索,要注意返回的list中的元素是实体还是实体数组
QBC 不会忽略配置文件中的预先抓取策略。
2.
为了进一步优化Hibernate的性能,可以使用延迟加载技术、管理数据抓取策略进行缓存管理 等方面考虑来提高Hibernate的性能。
一、
1、 延迟加载
延迟加载(load)是Hibernate为提高程序执行效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
场合一:当用户要取数据库的一张表的一个字段,这个字段很可能就是一个字符,总而言之长度是比较短的。
场合二:当用户要取数据库的一张表的一个字段的值,而这个值很可能是blob类型,也许存取的是一个很大的视频文件。
两种场合的取数据的方法一样吗?是用load还是用get方法?
延迟加载的过程:通过代理(Proxy)机制来实现延迟加载。Hibernate从数据库获取某一个对象数据时、获取某一个对象的集合属性值时,或获取某一个对象所关联的另一个对象时,由于没有使用该对象的数据(除标识符外),Hibernate并不从数据库加载真正的数据,而只是为该对象创建一个代理对象来代表这个对象,这个对象上的所有属性都为默认值;只有在真正需要使用该对象的数据时才创建这个真正的对象,真正从数据库中加载它的数据。
对象的延迟加载
对象里的属性延迟加载
集合延迟加载
Account acc=(Account)session.load(Account.class,new Long(1)); //返回一个代理对象
System.out.println(acc.getId);
System.out.prontln(acc.getLonginName());
在只需要Account类的一个引用时,这种延迟加载就很有用。
如果只是访问对象标示符属性,就没有必要初始化代码。
Account acc=(Account)session.load(Account.class,new Long(1)); //返回一个代理对象
Order order=new Order();
order.setCreateTime(new Date());
order.setAccount(acc);
Session.save(order);
在这只需要Account实例来创建一个新的Order订单对象,当调用session.save(order)时,也只需要Account的主标示符值作为外键保存到订单表的对应字段中。这样就少执行一条select语句,从而提高查询效率。
Hibernate中默认采用延迟加载的情况主要有以下几种:
当调用Session上的load()方法加载一个实体时,会采用延迟加载。
当Session加载某个实体时,会对这个实体中的集合属性值采用延迟加载。(one-to-many)
当Session加载某个实体时,会对这个实体所单端关联(one-to-one, many-to-one)的另一个实体对象采用延迟加载。
能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理对象)的属性(getId和getClass除外)hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来初始化代理对象;当相关联的session关闭后,再访问懒加载的对象将出现异常。
2、 关闭延迟加载
在加载单个实体时,如果不需要延迟加载,就可以使用session的get()方法。
当Session加载某个实体时,不需要对这个实体中的集合属性值延迟加载,而是要立即加载。这时可以在映射文件中针对 这个集合属性的配置元素(<set>,<bag>,<list>…)添加属性lazy=“false”。
当Session加载某个实体时,不需要对这个实体所单端关联的另一个实体对象延迟加载,就可以在映射文件中对这个单端关联的配置元素(<one-to-one>,<many-to-one> )添加属性lazy=“false”。
注意:one-to-one不能有constrained=true
二、抓取策略
在HQL语句中使用抓取连接查询,通过写一条left join fetch 语句把相关联的两个实体的数据一次性从数据库中加载上来。这样可以在特定情况下(同时需要使用到这两个实体的数据)减少SQL的数量来提高查询效率。
通过配置“抓取策略”来直接影响session的get()和load()方法的查询效果。
1.单端关联<many-to-one><one-to_one>上的抓取策略。
可以给单端关联的映射元素添加fetch属性。fetch属性有2个可选值
select:作为默认值,它的策略是当需要使用到关联对象的数据时,另外单独发送一条select语句抓取当前对象的关联对象的数据。即延迟加载。
join:它的策略是在同一条select语句使用连接来获得对象的数据和它关联对象的数据,此时关联对象的延迟加载失效。
2.集合属性上的抓取策略
在集合属性的映射元素上可以添加fetch属性,它有3个可选值。
select:作为默认值,它的策略是当需要使用所关联集合的数据时,另外单独发送一条select语句抓取当前对象的关联集合,即延迟加载。
join:在同一条select语句使用连接来获得对方的关联集合。此时关联集合上的lazy会失效。
subselect:另外发送一条查询语句(或子查询语句)抓取在前面查询到的所有实体对象的关联集合。这个策略对HQL的查询也起作用。
当fetch为join时,执行左外连接,这个时候,在加载Customer时,Customer所对应的Order值全部被加载到缓存中,如果Order中没有大数据,这个策略是一个不错的选择。
当fetch为subselect时,针对in有效,如果为select时,from Customer where id in(1,2,3),hibernate会把ID取出来,逐一的去取Order的值,效率比较低。这个时候subselect效率比较高,不管in里含有多少数据,在查询Order是,只会发出一条sql语句。把<set>集合中batch-size设置为一个比较合适的数值时也相当于fetch为subselect,你可以根据项目的因素来选择发出sql语句的次数。
HQL总是忽略映射文件中设置的预先抓取策略,即在HQL中使用预先抓取时,必须显示指明fetch关键字。然而不同的是,QBC则不会忽略映射文件中的预先抓取策略。
在实践开发项目过程中,不仅需要根据实际情况选择合适的抓取策略,而且需要通过不断的测试来验证这个策略是不是最有效率的。
三、缓存管理
1.缓存概述
缓存(cache)在java应用程序中是一组内存中的集合实例。它保存着永久性存储源(如硬盘上的文件或者数据库)中数据的备份,它的读写速度比读写硬盘的速度快。应用程序在运行时直接读写缓存中的数据,只在某些特定时刻安装缓存中的数据来同伴更新数据存储源。如果缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。
缓存的作用就是降低应用程序直接读写永久性数据存储源的频率,从而增强应用的运行性能。
缓存的实现不仅需要作为物理介质的硬件(内存),同时还需要用于管理缓存并发访问和过期等策略的软件。
2 缓存范围分类
缓存的范围决定了缓存的生命周期及其可以被谁访问。缓存的范围分为以下三类:
1)事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。
2)进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。
3)集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据一致性。对于大多数应用来说,应该慎用集群范围的缓存,因为访问的速度并不一定比直接访问数据库数据的速度快很多。
3. 缓存的并发访问策略
在进程范围或集群范围的缓存,会出现并发问题。因此可以设置4中类型的并发访问策略,每一种策略对应一种事务隔离级别。事务的隔离级别越高,并发性能就越低。
1)事务型(Transactional)策略
2)读写型(Read-Write)策略
3)非严格读写型(Nonstrict-read-write)策略
4)只读型(Read-only)策略
4.Hibernate中的缓存
Hibernate中提供两级缓存,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。
第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围范围的缓存,这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。
Hibernate还为查询结果提供一个查询缓存,它依赖于二级缓存。
5.一级缓存的管理
Session级别的缓存由hibernate自动管理。当应用程序调用Session的CRUD方法及调用查询接口的list(),iterate()等方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把改对象加入到Session缓存中。如果在Session缓存中已经存在这个对象,就不需要再去数据库加载而是直接使用缓存中的这个对象,可以减少访问数据库的频率,提高程序的运行效率。当Hibernate清理缓存时(默认是提交事务的时候),Hibernate会根据缓存中对象的状态来同步数据库中的数据状态,在关闭Session时,会清空Session缓存中的所有对象。
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用
evict(Object obj):从缓存中清除指定的持久化对象。
clear():清空缓存中所有持久化对象。
flush(): 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务。
commit():先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。
可以写一个for循环,Session可以批量插入上万条数据。如下面的代码:
For(int i=0;i<10000;i++){
Session.save(object);
}
这样写的代码会产生一个什么问题?会使一万个对象的缓存全部存在于内存中,这样做加大了内存的压力。所以应该定期清理session的缓存。
当做批量插入或批量更新时,必须通过经常调用Session的flush()以及稍后调用clear()来控制一级缓存的大小,这样内存才能保证足够的空间。
for(int i=1;i<=50;i++){
Department dept = new Department();
dept.setName("软件"+i);
session.save(dept);
for(int j=1;j<=100;j++){
Employee emp = new Employee();
emp.setName("张三"+j);
emp.setSalary(j);
emp.setDept(dept
session.save(emp);
if(j%50==0){
session.flush();
session.clear();
}
}
}
6.二级缓存的管理
二级缓存是SessionFactory级别的缓存,它的使用过程如下:
1)执行条件查询的时候,发出“select * from table_name where …”这样的SQL语句查询数据库,一次获得所有的数据对象。
2)把获得的所有数据对象根据ID放入到二级缓存中。
3)当Hibernate根据ID访问数据对象时,首先从Session缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;还没查到,再查询数据库,把结果按照ID放入到二级缓存。
4)删除、更新和增加数据的时候,同时会更新到二级缓存中。
Hibernate的二级缓存策略是针对ID查询的缓存策略,对于调节查询则毫无作用。为此,hibernate提高了单独针对条件查询的查询缓存。
适合存放到二级缓存中的数据有:
1)很少被修改的数据。
2)不是很重要的数据,允许出现偶尔的并发的数据。
3)很多系统模块都要用到
4)不是私有的数据,是共享的
什么样的数据不适合放在二级缓存中???
财务数据 安全性的数据 也就是不想让别人看到的数据和特别重要的数据
1、二级缓存的管理
二级缓存是SessionFactory级别的缓存,它的使用过程如下:
1)执行条件查询的时候,发出“select * from table_name where …”这样的SQL语句查询数据库,一次获得所有的数据对象。
2)把获得的所有数据对象根据ID放入到二级缓存中。
3)当Hibernate根据ID访问数据对象时,首先从Session缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;还没查到,再查询数据库,把结果按照ID放入到二级缓存。
4)删除、更新和增加数据的时候,同时会更新到二级缓存中。
Hibernate的二级缓存策略是针对ID查询的缓存策略,对于调节查询则毫无作用。为此,hibernate提高了单独针对条件查询的查询缓存。
适合存放到二级缓存中的数据有:
1)很少被修改的数据。
2)不是很重要的数据,允许出现偶尔的并发的数据。
3)很多系统模块都要用到
4)不是私有的数据,是共享的
什么样的数据不适合放在二级缓存中???
财务数据 安全性的数据 也就是不想让别人看到的数据和特别重要的数据
2、查询缓存
如果在实际使用中对于某个条件查询语句经常使用相同的条件值进行查询,就可以启用查询缓存。
Hibernate的查询缓存策略的过程如下。
1)在第一次执行条件查询时,Hibernate首先根据这些条件值组成一个Query key,Query key包括条件查询的请求信息。
2)在后续的过程中用相同的条件值执行这个查询是,Hibernate就先根据这个Query key到查询缓存中查找对应的结果列表,如果存在,返回。如果不存在,查数据库,再把结果列表根据Query 可以存放在查询缓存中。
所以,只有当经常使用相同的参数值进行相同的条件查询时,才能从查询缓存策略中得到好处。
使用查询缓存的步骤
1.配置查询缓存
Hibernate提供了3种与查询相关的缓存区域。
默认的查询缓存区域:org.hibernate.cache.StandardQueryCache
用户自定义的查询缓存区域
时间戳缓存区域:org.hibernate.cache.UpdateTimestampCache
在ehcache的配置文件ehcache.xml中设置查询缓存区域的属性。
<ehcache>
<!-- 设置默认的查询缓存区域的属性 -->
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
/>
<!-- 设置时间戳缓存区域的属性 -->
<cache name="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="true"
/>
<!-- 设置自定义命名查询缓存区域的属性 -->
<cache name="myCacheRegion"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="3600"
timeToLiveSeconds="7200"
/>
</ehcache>
3、
打开查询缓存
在hibernate的全局配置文件中添加如下配置来启动查询缓存。
<!--启用查询缓存 -->
<property name="cache.use_query_cache">true</property>
在应用程序代码中使用查询缓存
虽然按以上步骤设置好查询缓存,但hibernate在执行条件查询时默认是忽略查询缓存的。如果希望启用查询缓存,应该调用Query接口的setCacheeable(true)方法。
二、配置连接池
<!-- 配置C3p0连接池 -->
<!-- 启用c3p0连接池 设置连接池提供的供应商 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!-- 最大连接数 -->
<property name="c3p0.max_size">20</property>
<!-- 最小连接数 -->
<property name="c3p0.min_size">5</property>
<!-- 每次请求连接的数目 -->
<property name="c3p0.acquire_increment">5</property>
<!-- 设置过期时间,以秒为单位,如果连接池中 -处于空闲状态的连接超过了这个时间,该连接就会从连接池中删除-->
<property name="c3p0.timeout">120</property>
<!-- 每个3000秒检查连接池中的空闲连接 -->
<property name="c3p0.idle_test_period">3000</property>
三、Hibernate不适合的场景
不适合OLAP(On-Line Analytical Processing联机分析处理),以查询分析数据为主的系统;适合OLTP(on-line transaction processing联机事务处理)。
对于些关系模型设计不合理的老系统,也不能发挥hibernate优势。
数据量巨大,性能要求苛刻的系统,hibernate也很难达到要求, 批量操作数据的效率也不高。
四、在Web应用程序中使用Hibernate
1.Hibernate 整合Struts
Hibernate框架主要使用在持久层中,完成实体类的CRUD操作。
2. 泛型DAO模式Hibenate实现
3.OpenSessionInView模式
五、总结:
Hibernate HQL查询、Criteria查询、调用存储过程
Hibernate事务管理和并发控制
Hibernate 延迟加载策略、抓取策略、一级二级缓存管理
Hibernate整合Struts
3.如何提高Hibernate数据库查询性能
数据库查询性能的提升也是涉及到开发中的各个阶段,在开发中选用正确的查询方法无疑是最基础也最简单的。
SQL语句的优化
使用正确的SQL语句可以在很大程度上提高系统的查询性能。获得同样数据而采用不同方式的SQL语句在性能上的差距可能是十分巨大的。
由于Hibernate是对JDBC的封装,SQL语句的产生都是动态由Hibernate自动完成的。Hibernate产生SQL语句的方式有两种:一种是通过开发人员编写的HQL语句来生成,另一种是依据开发人员对关联对象的访问来自动生成相应的SQL语句。
至于使用什么样的SQL语句可以获得更好的性能要依据数据库的结构以及所要获取数据的具体情况来进行处理。在确定了所要执行的SQL语句后,可以通过以下三个方面来影响Hibernate所生成的SQL语句:
● HQL语句的书写方法。
● 查询时所使用的查询方法。
● 对象关联时所使用的抓取策略。
使用正确的查询方法
在前面已经介绍过,执行数据查询功能的基本方法有两种:一种是得到单个持久化对象的get()方法和load()方法,另一种是Query对象的list()方法和iterator()方法。在开发中应该依据不同的情况选用正确的方法。
get()方法和load()方法的区别在于对二级缓存的使用上。load()方法会使用二级缓存,而get()方法在一级缓存没有找到的情况下会直接查询数据库,不会去二级缓存中查