一个良好的数据库结构有利于系统性能的提升。这里所说的良好结构的数据库并不单纯是指满足数据库设计范式的数据库结构。这是因为,按照数据库范式所设计的数据库只能说在结构上是最优的,没有冗余数据等问题,但在生产过程中并不一定能获得最佳的性能。有时候适当地增加一些数据的冗余虽然增加了数据维护的难度,但可以极大地简化业务的查询,提高数据检索的效率。 在使用Java访问数据库的时候,还存在另外一个问题,就是面向对象的Java语言与关系型数据库之间的矛盾。在这两者之间必然要涉及到一个相互转化的问题,对于这个问题是否能够正确的处理也是影响系统性能的一个重要因素。
综合以上提出的各种问题,在数据库设计阶段要综合考虑以下三个方面的因素。
● Java建模
在建立Java对象模型的时候,要考虑数据库持久化的方便性,所建立的Java对象模型应该可以很容易地被数据所存储,并且数据库中表的结构也是越简单越好。
● 数据库结构
在设计数据库结构的时候也要考虑到是否可以很容易地用Java对象去表示。这里并不是简单的一个表对应一个对象的直接转换,更重要的是转换后的Java对象应该能够描述出数据间的关系。
所以在设计阶段,对于Java对象和数据库结构要进行综合考虑,也就是可以从两个方向进行考虑,毕竟两者之间不是一个时代的产物,设计的结果应该在两者之间达到一个平衡,虽然不能每一方都达到最优,但也不能造成有一方结构很差的情况。就像装水的木桶,最矮的板子决定水桶的容量。
● 业务需求
前面两个因素都是纯技术方面的考虑,在设计的过程中,更重要的是要紧扣业务需求。这是因为任何的软件系统都是以业务为中心的,那么对于系统的设计也不例外,在设计的阶段就应该考虑业务实现的方便性以及执行的效率。一个良好的结构设计不但使业务功能的实现变得非常容易并且可以避免很多复杂的操作,还可以达到提升系统性能的目的。
设计阶段是整个应用系统开发中的根基,其对软件的影响仅次于对系统需求的把握。所以在设计阶段应该对整个软件系统有一个整体的考虑,这里所说的具体设计也只是设计阶段中的很少的一部分,综合考虑多方面的因素才能达到更佳的性能。
接下来介绍一些影响系统性能的操作以及如何处理这些操作。
批量插入、更新和删除
在进行大批量数据操作的时候,如果处理不当,很可能会出现执行效率低下的情况。下面首先来介绍一下批量插入数据时应该注意的问题。
批量插入数据 在项目开发中,经常会遇到需要向数据库中一次插入大量数据的时候,如果在开发中一味地调用Session对象的save()方法向数据库中保存对象,那么就很可能会出现OutOfMemoryError(内存溢出)异常。
之所以出现这种情况是由于Hibernate缓存的影响。由于Hibernate的一级缓存是由Hibernate自己进行管理的,并且只会存在于内存中,所以,在调用save()方法的时候会将所保存的对象都缓存起来,这样当数量巨大的时候就会出现内存溢出的情况了。
为了避免内存溢出情况的发生,需要在调用save()方法的同时,阶段性地刷新和清空一级缓存。具体的处理方法请参考14.2.2节中的介绍。
虽然通过设置可以使二级缓存不会发生溢出,但在进行大批量的插入操作时,最好还是要禁用二级缓存,毕竟将对象保存到二级缓存是要耗费一定时间的,在禁用二级缓存后可以避免录入大量数据所带来的性能问题。
批量修改和删除 在Hibernate 2中,如果需要对任何数据进行修改和删除操作,都需要先执行查询操作,在得到要修改或者删除的数据后,再对该数据进行相应的操作处理。在数据量少的情况下采用这种处理方式没有问题,但需要处理大量数据的时候就可能存在以下的问题:
● 占用大量的内存。
● 需要多次执行update/delete语句,而每次执行只能处理一条数据。
以上两个问题的出现会严重影响系统的性能。因此,在Hibernate 3中引入了用于批量更新或者删除数据的HQL语句。这样,开发人员就可以一次更新或者删除多条记录,而不用每次都一个一个地修改或者删除记录了。
如果要删除所有的User对象(也就是User对象所对应表中的记录),则可以直接使用下面的HQL语句:
delete User
而在执行这个HQL语句时,需要调用Query对象的executeUpdate()方法,具体的实例如下所示:
String HQL="delete User";
Query query=session.createQuery(HQL);
int size=query.executeUpdate();
采用这种方式进行数据的修改和删除时与直接使用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+1条SQL语句(N为结果集中的记录数)。
iterator()方法只是可能执行N+1条数据,具体执行SQL语句的数量取决于缓存的情况以及对结果集的访问情况。
● 缓存的使用
list()方法只能使用二级缓存中的查询缓存,而无法使用二级缓存对单个对象的缓存(但是会把查询出的对象放入二级缓存中)。所以,除非重复执行相同的查询操作,否则无法利用缓存的机制来提高查询的效率。
iterator()方法则可以充分利用二级缓存,在根据ID检索对象的时候会首先到缓存中查找,只有在找不到的情况下才会执行相应的查询语句。所以,缓存中对象的存在与否会影响到SQL语句的执行数量。
● 对于结果集的处理方法不同
list()方法会一次获得所有的结果集对象,而且它会依据查询的结果初始化所有的结果集对象。这在结果集非常大的时候必然会占据非常多的内存,甚至会造成内存溢出情况的发生。
iterator()方法在执行时不会一次初始化所有的对象,而是根据对结果集的访问情况来初始化对象。因此在访问中可以控制缓存中对象的数量,以避免占用过多缓存,导致内存溢出情况的发生。使用iterator()方法的另外一个好处是,如果只需要结果集中的部分记录,那么没有被用到的结果对象根本不会被初始化。所以,对结果集的访问情况也是调用iterator()方法时执行数据库SQL语句多少的一个因素。
所以,在使用Query对象执行数据查询时应该从以上几个方面去考虑使用何种方法来执行数据库的查询操作。
使用正确的抓取策略 所谓抓取策略(fetching strategy)是指当应用程序需要利用关联关系进行对象获取的时候,Hibernate获取关联对象的策略。抓取策略可以在O/R映射的元数据中声明,也可以在特定的HQL或条件查询中声明。
Hibernate 3定义了以下几种抓取策略。
● 连接抓取(Join fetching)
连接抓取是指Hibernate在获得关联对象时会在SELECT语句中使用外连接的方式来获得关联对象。
● 查询抓取(Select fetching)
查询抓取是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式。这也是通过外键的方式来执行数据库的查询。与连接抓取的区别在于,通常情况下这个SELECT语句不是立即执行的,而是在访问到关联对象的时候才会执行。
● 子查询抓取(Subselect fetching)
子查询抓取也是指Hibernate通过另外一条SELECT语句来抓取当前对象的关联对象的方式。与查询抓取的区别在于它所采用的SELECT语句的方式为子查询,而不是通过外连接。
● 批量抓取(Batch fetching)
批量抓取是对查询抓取的优化,它会依据主键或者外键的列表来通过单条SELECT语句实现管理对象的批量抓取。
以上介绍的是Hibernate 3所提供的抓取策略,也就是抓取关联对象的手段。为了提升系统的性能,在抓取关联对象的时机上,还有以下一些选择。
● 立即抓取(Immediate fetching)
立即抓取是指宿主对象被加载时,它所关联的对象也会被立即加载。
● 延迟集合抓取(Lazy collection fetching)
延迟集合抓取是指在加载宿主对象时,并不立即加载它所关联的对象,而是到应用程序访问关联对象的时候才抓取关联对象。这是集合关联对象的默认行为。
● 延迟代理抓取(Lazy proxy fetching)
延迟代理抓取是指在返回单值关联对象的情况下,并不在对其进行get操作时抓取,而是直到调用其某个方法的时候才会抓取这个对象。
● 延迟属性加载(Lazy attribute fetching)
延迟属性加载是指在关联对象被访问的时候才进行关联对象的抓取。
介绍了Hibernate所提供的关联对象的抓取方法和抓取时机,这两个方面的因素都会影响Hibernate的抓取行为,最重要的是要清楚这两方面的影响是不同的,不要将这两个因素混淆,在开发中要结合实际情况选用正确的抓取策略和合适的抓取时机。
抓取时机的选择
在Hibernate 3中,对于集合类型的关联在默认情况下会使用延迟集合加载的抓取时机,而对于返回单值类型的关联在默认情况下会使用延迟代理抓取的抓取时机。
对于立即抓取在开发中很少被用到,因为这很可能会造成不必要的数据库操作,从而影响系统的性能。当宿主对象和关联对象总是被同时访问的时候才有可能会用到这种抓取时机。另外,使用立即连接抓取可以通过外连接来减少查询SQL语句的数量,所以,也会在某些特殊的情况下使用。
然而,延迟加载又会面临另外一个问题,如果在Session关闭前关联对象没有被实例化,那么在访问关联对象的时候就会抛出异常。处理的方法就是在事务提交之前就完成对关联对象的访问。
所以,在通常情况下都会使用延迟的方式来抓取关联的对象。因为每个立即抓取都会导致关联对象的立即实例化,太多的立即抓取关联会导致大量的对象被实例化,从而占用过多的内存资源。
抓取策略的选取
对于抓取策略的选取将影响到抓取关联对象的方式,也就是抓取关联对象时所执行的SQL语句。这就要根据实际的业务需求、数据的数量以及数据库的结构来进行选择了。
在这里需要注意的是,通常情况下都会在执行查询的时候针对每个查询来指定对其合适的抓取策略。指定抓取策略的方法如下所示:
User user = (User) session.createCriteria(User.class)
.setFetchMode("permissions", FetchMode.JOIN)
.add( Restrictions.idEq(userId) )
.uniqueResult();
查询性能提升小结 在本小节中介绍了查询性能提升的方法,关键是如何通过优化SQL语句来提升系统的查询性能。查询方法和抓取策略的影响也是通过执行查询方式和SQL语句的多少来改变系统的性能的。这些都属于开发人员所应该掌握的基本技能,避免由于开发不当而导致系统性能的低下。
在性能调整中,除了前面介绍的执行SQL语句的因素外,对于缓存的使用也会影响系统的性能。通常来说,缓存的使用会增加系统查询的性能,而降低系统增加、修改和删除操作的性能(因为要进行缓存的同步处理)。所以,开发人员应该能够正确地使用有效的缓存来提高数据查询的性能,而要避免滥用缓存而导致的系统性能变低。在采用缓存的时候也应该注意调整自己的检索策略和查询方法,这三者配合起来才可以达到最优的性能。
另外,事务的使用策略也会影响到系统的性能。选取正确的事务隔离级别以及使用正确的锁机制来控制数据的并发访问都会影响到系统的性能。
分享到:
相关推荐
统架构师指导下完成详细设计的能力。他们需要理解整个系统的需求,能够将高层设计分解为具体的模块...设计阶段的成果不仅要满足需求,还需要考虑软件的扩展性、可维护性和性能,为后续的实现和测试阶段打下坚实的基础。
3. **详细设计**:在概要设计基础上,详细设计阶段细化每个模块的功能和操作流程,包括界面设计、数据库设计、类和对象设计等。这一阶段产出详细的设计图、伪代码或流程图,为编码提供直接依据。 4. **可行性分析**...
在工程项目的实施过程中,成本控制是一项至关重要的任务,特别是在设计阶段。这个阶段的成本控制直接影响到整个项目的经济效益和最终的成功与否。本文将详细探讨在工程设计阶段如何有效地进行成本控制,以确保项目的...
《工程设计阶段造价控制存在的问题与对策》 工程造价控制是确保建设项目经济效益的核心环节,而在所有建设阶段中,设计阶段的造价控制尤为重要。设计阶段不仅决定了工程的功能性和安全性,更是影响工程造价的关键...
《设计阶段成本优化指南》主要讲述了在建筑设计的早期阶段如何通过各种策略来降低成本并提高项目的经济效益。以下是根据文件内容提炼的关键知识点: 1. 建筑与结构优化: - 在概念设计阶段,成本管理部应与规划...
本文将详细解读在设计阶段如何进行成本控制,以及应考虑的关键因素。 一、规划对施工的成本控制 规划阶段的成本控制是确保项目整体经济效益的基础。规划中要充分考虑施工的各个方面,包括单体设计、地下建筑、施工...
"参考资料-2016绿城设计阶段26项112个成本优化点.zip"是一个关于房地产开发中设计阶段成本控制的压缩包文件,其中包含了一份名为"参考资料-2016绿城设计阶段26项112个成本优化点.pdf"的详细文档。这份资料深入探讨了...
在工程项目的设计阶段,关注的重点涵盖了设计的重要性、设计的特点以及设计过程中可能出现的问题。设计是工程项目的核心,它将理念、计划和规划转化为实际的建设方案,是科学技术转化为生产力的关键环节。设计阶段对...
初步设计和技术设计阶段则需要考虑设计分析评审的时间,以确保工程质量和进度同步。 初步设计和技术设计的周期不仅要考虑设计本身,还包括审批时间。而施工图设计则依据批准的初步设计或技术设计文件进行,同时考虑...
软件工程详细设计阶段-详细设计说明书 在软件工程中,详细设计阶段是软件开发过程中的一个重要阶段。在这个阶段中,将对系统的总体架构和模块设计进行详细的描述,以便在编码阶段可以根据设计直接翻译成具体的程序...
在结构设计阶段,选择功能实现技术;选择器件厂商、具体型号和开发工具;定义系统架构,考虑设计实现的可升级性;分割固定的离散功能模块与可编程模块;分割设计功能是使用软件还是硬件实现;定义设计模块功能和接口...
10. **风险管理**:在设计阶段识别和评估潜在风险,制定应对策略,可以防止因未预见问题引发的成本增加。 总的来说,"房产设计阶段成本优化"涉及多方面的专业知识,包括建筑设计、工程管理、成本控制等,旨在通过...
在不同阶段,这些管理工作的重要性各有侧重,例如方案设计阶段就需要全面考虑各种因素,而施工图设计阶段则更注重细节和实施性。 设计任务的委托通常有设计竞赛和直接委托等方式。设计竞赛是一种常见的选择方案方法...
然而,设计阶段成本控制面临的主要问题包括技术与经济的分离、设计费用计算的不合理以及设计市场竞争机制的不完善。设计和技术的分离使得设计人员在选择材料和结构时缺乏成本意识,可能导致工程造价超出预期。另一...
3. 主动控制:设计阶段的控制工作能使项目团队更为主动,而不是等到施工阶段才被动应对问题。 4. 技术与经济结合:控制工程成本的同时,可以促进技术和经济因素的协调,实现技术进步与经济效益的平衡。 5. 显著的...
设计师应具备成本意识,并在早期就与业主进行积极沟通,以理解质量标准并站在业主的角度思考问题。同行间的交流和竞争也有助于推动成本控制的创新和优化。 设计合同中应明确设计公司应承担的风险,如设计错误、延误...
首先,在设计阶段,需要根据军队的需求和要求,对军品的性能、结构和材料进行设计和优化。这包括对军品的功能、尺寸、材料和外观的设计,以确保军品能够满足军队的需求。 其次,在试制阶段,需要根据设计方案,制造...
此外,设计阶段的团队协作、进度控制和问题解决能力也是评估的重要标准。 总结,软件项目设计阶段是项目成功与否的关键阶段,涵盖了从需求分析到具体实现方案的转化。团队的组织结构、技能要求和管理控制都是确保...
软件测试可以分为四个阶段:需求阶段、设计编码阶段、测试阶段和用户测试阶段。每个阶段都有其特定的职责和目标。 需求阶段 在需求阶段,测试人员需要了解项目需求,包括项目需求规格说明、功能结构及模块划分等。...
《建筑设计阶段工程造价管理方法分析...总之,建筑设计阶段的工程造价管理是决定项目经济效益的关键环节,需综合考虑设计、技术、经济等多方面因素,通过科学的管理和控制措施,确保工程项目的高效、经济与可持续发展。