本系列前序文章索引:html
今天老兵哥将介绍经过优化对象关系映射 ORM 框架(Hibernate)等来优化系统性能的方法。程序员
对象-关系映射 ORM(Object/Relation Mapping),是伴随着面向对象软件开发方法的发展而产生的。面向对象的开发方法是当今企业级应用开发环境中的主流方法,关系数据库是企业级应用环境中数据永久存储的主流数据存储系统。对象和关系是业务实体数据的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。内存中的对象之间存在关联和继承关系,而在数据库中,关系数据没法直接表达多对多关联和继承关系。数据库
对象-关系映射 ORM 系统一般以中间件的形式存在,借助描述对象到关系数据库数据的映射元数据,将内存中的对象自动持久化到关系数据库中,其本质就是将数据从一种形式转换到另一种形式。这个转换过程须要额外的开销,天然也就存在许多优化的机会,接下来咱们一块儿来看看如何提高 ORM 框架 Hibernate 的性能。 segmentfault
应用或者 ORM 框架每次执行 SQL 语句都须要跟数据库创建链接,每次创建链接都须要额外开销。若是某个事务内部有循环屡次操做数据库的场景,那么将这些操做聚集在一块儿批量执行,这样就能够下降损耗,具体以下:缓存
使用这种方法时,首先在 Hibernate 的配置文件 hibernate.cfg.xml 中设置批量尺寸属性 hibernate.jdbc.batch_size ,且最好关闭Hibernate的二级缓存以提升效率。session
<hibernate-configuration> <session-factory> <property name="hibernate.jdbc.batch_size">50</property> //设置尺寸 <property name="hibernate.cache.use_second_level_cache">false</property> //关闭缓存 <mapping resource="com/itlaobingge/po/User.hbm.xml" /> </session-factory> </hibernate-configuration>
public class HibernateDemo { public static void main(String args[]) { Session session = HibernateSessionFactory.getSession(); Transaction ts = session.beginTransaction(); for (int i = 0; i < 50; i++) { User user = new User(); user.setPassword(i); session.save(user); if (i%50 == 0) { // 以 50 为一个批次往数据库提交,此值应与配置的批量尺寸一致 session.flush(); // 清空缓存区,释放内存供下批数据使用 session.clear(); } } ts.commit(); HibernateSessionFactory.closeSession(); } }
为了使 Hibernate 的 HQL 直接支持 update 的批量更新语法,咱们须要在 Hibernate 的配置文件 hibernate.cfg.xml 中设置 HQL/SQL 查询翻译器属性 "hibernate.query.factory_class":架构
<hibernate-configuration> ...... <property name="hibernate.query.factory_class"> org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory </property> <mapping resource="com/itlaobingge/po/User.hbm.xml" /> </session-factory> </hibernate-configuration>
public class HibernateDemo { public static void main(String args[]) { Session session = HibernateSessionFactory.getSession(); Transaction ts = session.beginTransaction(); Query query = session.createQuery("update User set password='123456'"); query.executeUpdate(); ts.commit(); HibernateSessionFactory.closeSession(); } }
为了使 Hibernate 的 HQL 直接支持 delete 的批量更新语法,咱们须要在 Hibernate 的配置文件 hibernate.cfg.xml 中设置 HQL/SQL 查询翻译器属性 "hibernate.query.factory_class":并发
<hibernate-configuration> ...... <property name="hibernate.query.factory_class"> org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory </property> <mapping resource="com/itlaobingge/po/User.hbm.xml" /> </session-factory> </hibernate-configuration>
public class HibernateDemo { public static void main(String args[]) { Session session = HibernateSessionFactory.getSession(); Transaction ts = session.beginTransaction(); Query query=session.createQuery("delete User where id < 123"); query.executeUpdate(); ts.commit(); HibernateSessionFactory.closeSession(); } }
抓取策略是指当应用程序须要在对象关联关系间进行导航时,Hibernate 如何获取关联对象的策略,常见的抓取策略有以下几种:app
Hibernate 会区分下列几种状况:框架
定制合理的抓取策略对系统的性能提高有很大的帮助。查询抓取在 N+1 查询的状况下是极其脆弱的,所以咱们可能会要求在映射文件中定义链接抓取(fetch=”join”),可是在映射文件中定义的抓取策略将会产生如下影响:经过 get() 或者 load() 方法获取数据,只有在关联之间进行导航时,才会隐式的取得数据。
条件查询,使用了 subselect 抓取的 HQL 查询,无论使用哪一种抓取策略,定义为非延时的类图会保证装载入内存,这就意味着一条 HQL 查询后紧跟着一系列的查询。一般咱们并不使用映射文件进行抓取策略的定制,更可能是保持其默认值而后在待定事务中适用 HQL 的左链接对其进行重载。
Hibernate 推荐的作法也是最佳实践:把全部对象关联的抓取都设为 lazy,而后在特定事务中进行重载。这种考虑是基于对象之间的关联关系错综复杂,有时候哪怕咱们只是一个简单的查询,也会致使不少关联对象被装载出来,因此在 Hibernate 中,全部对象关联都是 lazy 的。
在 Hibernate 中实施关联抓取,咱们能够定义每次抓取数据的数量,批量地将数据载入内存,减小与数据库交互的次数,在应用程序中能够定义默认的关联抓取数量。Hibernate 提供了两种批量抓取方案:
<class name=”Class” batch-size=”15”>...</class>
<set name=”users” batch-size=”15”>...</set>
缓存能够下降应用程序对物理数据源访问的频次,从而提升应用程序的运行性能。缓存对 Hibernate 来讲也是很重要的,它使用了以下图所示的多级缓存方案:
Hibernate 的二级缓存经过两个步骤设置:第一,你必须决定好使用哪一个并发策略(Transactional、Read-write、Nonstrict-read-write、Read-only);第二,你使用第三方缓存提供者来配置缓存到期时间和物理缓存属性。并发策略,负责保存缓存中的数据项和从缓存中检索它们,如何选择并发策略及配置能够查资料。
查询结果集也能够被缓存,只有在常用一样的参数进行查询时,查询缓存才会有些用处。若是要使用查询缓存,你必须打开它:hibernate.cache.use_query_cache,该设置将会建立两个缓存区域:一个用于保存查询结果集(org.hibernate.cache.StandardQueryCache);另外一个则用于保存最近查询的一系列表的时间戳(org.hibernate.cache.UpdateTimestampsCache)。
在查询缓存中,它并不缓存结果集中所包含的实体的确切状态,它只缓存这些实体的标识符属性的值、以及各值类型的结果,因此查询缓存一般会和二级缓存一块儿使用。绝大多数的查询并不能从查询缓存中受益,因此 Hibernate 默认是不进行查询缓存的。如若须要进行缓存,请调用 Query.setCacheable(true) 方法。这个调用会让查询在执行过程当中时先从缓存中查找结果,并将本身的结果集放到缓存中去。
关注「 IT老兵哥 」,赋能程序人生!近期热评系列《 程序员必须懂的架构师入门课 》: