一批量修改和删除
在Hibernate 中如果需要对任何数据进行修改和删除操作都需要先执行查询操作在得到要修改或者删除的数据后再对该数据进行相应的操作处理在数据量少的情况下采用这种处理方式没有问题但需要处理大量数据的时候就可能存在以下的问题
占用大量的内存
需要多次执行update/delete语句而每次执行只能处理一条数据
以上两个问题的出现会严重影响系统的性能因此在Hibernate 中引入了用于批量更新或者删除数据的HQL语句这样开发人员就可以一次更新或者删除多条记录而不用每次都一个一个地修改或者删除记录了
如果要删除所有的User对象(也就是User对象所对应表中的记录)则可以直接使用下面的HQL语句
delete User
而在执行这个HQL语句时需要调用Query对象的executeUpdate()方法具体的实例如下所示
String HQL=delete User;
Query query=sessioncreateQuery(HQL);
int size=queryexecuteUpdate();
采用这种方式进行数据的修改和删除时与直接使用JDBC的方式在性能上相差无几是推荐使用的正确方法
如果不能采用HQL语句进行大量数据的修改也就是说只能使用取出再修改的方式时也会遇到批量插入时的内存溢出问题所以也要采用上面所提供的处理方法来进行类似的处理
二 使用SQL执行批量操作
在进行批量插入修改和删除操作时直接使用JDBC来执行原生态的SQL语句无疑会获得最佳的性能这是因为在处理的过程中省略或者简化了以下处理内容
● HQL语句到SQL语句的转换
● Java对象的初始化
● Java对象的缓存处理
但是在直接使用JDBC执行SQL语句时有一个最重要的问题就是要处理缓存中的Java对象因为通过这种底层方式对数据的修改将不能通知缓存去进行相应的更新操作以保证缓存中的对象与数据库中的数据是一致的
三 提升数据库查询的性能
数据库查询性能的提升也是涉及到开发中的各个阶段在开发中选用正确的查询方法无疑是最基础也最简单的
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()方法在一级缓存没有找到的情况下会直接查询数据库不会去二级缓存中查找在使用中对使用了二级缓存的对象进行查询时最好使用load()方法以充分利用二级缓存来提高检索的效率
list()方法和iterator()方法之间的区别可以从以下几个方面来进行比较
执行的查询不同
list()方法在执行时是直接运行查询结果所需要的查询语句而iterator()方法则是先执行得到对象ID的查询然后再根据每个ID值去取得所要查询的对象因此对于list()方式的查询通常只会执行一个SQL语句而对于iterator()方法的查询则可能需要执行N+条SQL语句(N为结果集中的记录数)
iterator()方法只是可能执行N+条数据具体执行SQL语句的数量取决于缓存的情况以及对结果集的访问情况
缓存的使用
list()方法只能使用二级缓存中的查询缓存而无法使用二级缓存对单个对象的缓存(但是会把查询出的对象放入二级缓存中)所以除非重复执行相同的查询操作否则无法利用缓存的机制来提高查询的效率
iterator()方法则可以充分利用二级缓存在根据ID检索对象的时候会首先到缓存中查找只有在找不到的情况下才会执行相应的查询语句所以缓存中对象的存在与否会影响到SQL语句的执行数量
对于结果集的处理方法不同
list()方法会一次获得所有的结果集对象而且它会依据查询的结果初始化所有的结果集对象这在结果集非常大的时候必然会占据非常多的内存甚至会造成内存溢出情况的发生
iterator()方法在执行时不会一次初始化所有的对象而是根据对结果集的访问情况来初始化对象因此在访问中可以控制缓存中对象的数量以避免占用过多缓存导致内存溢出情况的发生使用iterator()方法的另外一个好处是如果只需要结果集中的部分记录那么没有被用到的结果对象根本不会被初始化所以对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素
所以在使用Query对象执行数据查询时应该从以上几个方面去考虑使用何种方法来执行数据库的查询操作
四 使用正确的抓取策略
所谓抓取策略(fetching strategy)是指当应用程序需要利用关联关系进行对象获取的时候Hibernate获取关联对象的策略抓取策略可以在O/R映射的元数据中声明也可以在特定的HQL或条件查询中声明
Hibernate 定义了以下几种抓取策略
连接抓取(Join fetching)
连接抓取是指Hibernate在获得关联对象时会在SELECT语句中使用外连接的方式来获得关联对象
查询抓取(Select fetching)
查询抓取是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式这也是通过外键的方式来执行数据库的查询与连接抓取的区别在于通常情况下这个SELECT语句不是立即执行的而是在访问到关联对象的时候才会执行
子查询抓取(Subselect fetching)
子查询抓取也是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式与查询抓取的区别在于它所采用的SELECT语句的方式为子查询而不是通过外连接
批量抓取(Batch fetching)
批量抓取是对查询抓取的优化它会依据主键或者外键的列表来通过单条SELECT语句实现管理对象的批量抓取
以上介绍的是Hibernate 所提供的抓取策略也就是抓取关联对象的手段为了提升系统的性能在抓取关联对象的时机上还有以下一些选择
立即抓取(Immediate fetching)
立即抓取是指宿主对象被加载时它所关联的对象也会被立即加载
延迟集合抓取(Lazy collection fetching)
延迟集合抓取是指在加载宿主对象时并不立即加载它所关联的对象而是到应用程序访问关联对象的时候才抓取关联对象这是集合关联对象的默认行为
延迟代理抓取(Lazy proxy fetching)
延迟代理抓取是指在返回单值关联对象的情况下并不在对其进行get操作时抓取而是直到调用其某个方法的时候才会抓取这个对象
延迟属性加载(Lazy attribute fetching)
延迟属性加载是指在关联对象被访问的时候才进行关联对象的抓取
介绍了Hibernate所提供的关联对象的抓取方法和抓取时机这两个方面的因素都会影响Hibernate的抓取行为最重要的是要清楚这两方面的影响是不同的不要将这两个因素混淆在开发中要结合实际情况选用正确的抓取策略和合适的抓取时机
抓取时机的选择
在Hibernate 中对于集合类型的关联在默认情况下会使用延迟集合加载的抓取时机而对于返回单值类型的关联在默认情况下会使用延迟代理抓取的抓取时机
对于立即抓取在开发中很少被用到因为这很可能会造成不必要的数据库操作从而影响系统的性能当宿主对象和关联对象总是被同时访问的时候才有可能会用到这种抓取时机另外使用立即连接抓取可以通过外连接来减少查询SQL语句的数量所以也会在某些特殊的情况下使用
然而延迟加载又会面临另外一个问题如果在Session关闭前关联对象没有被实例化那么在访问关联对象的时候就会抛出异常处理的方法就是在事务提交之前就完成对关联对象的访问
所以在通常情况下都会使用延迟的方式来抓取关联的对象因为每个立即抓取都会导致关联对象的立即实例化太多的立即抓取关联会导致大量的对象被实例化从而占用过多的内存资源
抓取策略的选取
对于抓取策略的选取将影响到抓取关联对象的方式也就是抓取关联对象时所执行的SQL语句这就要根据实际的业务需求数据的数量以及数据库的结构来进行选择了
在这里需要注意的是通常情况下都会在执行查询的时候针对每个查询来指定对其合适的抓取策略指定抓取策略的方法如下所示
User user = (User) sessioncreateCriteria(Userclass)
setFetchMode(permissions FetchModeJOIN)
add( RestrictionsidEq(userId) )
uniqueResult();
五 查询性能提升小结
在本小节中介绍了查询性能提升的方法关键是如何通过优化SQL语句来提升系统的查询性能查询方法和抓取策略的影响也是通过执行查询方式和SQL语句的多少来改变系统的性能的这些都属于开发人员所应该掌握的基本技能避免由于开发不当而导致系统性能的低下
在性能调整中除了前面介绍的执行SQL语句的因素外对于缓存的使用也会影响系统的性能通常来说缓存的使用会增加系统查询的性能而降低系统增加修改和删除操作的性能(因为要进行缓存的同步处理)所以开发人员应该能够正确地使用有效的缓存来提高数据查询的性能而要避免滥用缓存而导致的系统性能变低在采用缓存的时候也应该注意调整自己的检索策略和查询方法这三者配合起来才可以达到最优的性能
另外事务的使用策略也会影响到系统的性能选取正确的事务隔离级别以及使用