Hibernate --- 优化方案

一、HQL优化

1.1 使用参数绑定

(1) 是让数据库一次解析SQL,对后续的重复请求可以使用生产好的执行计划,这样做可以节省CPU时间和内存。
(2) 避免SQL注入


1.2 尽量少使用NOT

如果where子句中包含not关键字,那么执行时该字段的索引失效。


1.3 尽量使用where来替换having

Having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销


1.4 减少对表的查询

在含有子查询的HQL中,尽量减少对表的查询,降低开销


1.5 使用表的别名

在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。


1.6 避免like的特殊形式

如果like以一个"%"或"_"开始即前模糊,则该字段的索引不起作用。但是非常遗憾的是,对于这种问题并没有额外的解决方法,只能通过改变索引字段的形式变相的解决。


1.7 避免使用distinct

指定distinct会导致在结果中删除重复的行。这会对处理时间造成一定的影响,因此在不要求或允许冗余时,应避免使用distinct。



二、一级缓存

一级缓存是hibernate自带的,不受用户干预,其生命周期和session的生命周期一致,当前session一旦关闭,一级缓存就会消失,因此,一级缓存也叫session缓存或者事务级缓存,一级缓存只存储实体对象,不会缓存一般的对象属性,即:当获得对象后,就将该对象缓存起来,如果在同一个session中再去获取这个对象时,它会先判断缓存中有没有这个对象的ID,如果有,就直接从缓存中取出,否则,则去访问数据库,取了以后同时会将这个对象缓存起来。


一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出,这是需要我们手动清除一级缓存:

  • evict(Object obj):从缓存中清除指定的持久化对象。
  • clear():清空缓存中所有持久化对象。
  • flush(): 进行清理缓存(此时缓存中的数据并不丢失)的操作,让缓存和数据库同步 执行一些列sql语句,但不提交事务。
  • commit():先调用flush() 方法,然后提交事务. 则意味着提交事务意味着对数据库操作永久保存下来。

三、二级缓存

二级缓存也称为进程缓存或者sessionFactory级的缓存,它可以被所有的session共享,二级缓存的生命周期和sessionFactory的生命周期一致,二级缓存也是只存储实体对象。

Hibernate二级缓存分类:

  • 内置缓存:Hibernate自带的,不可卸载,通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放置到SessionFactory的缓存中。该内置缓存是只读的。
  • 外置缓存:通常说的二级缓存也就是外置缓存,在默认情况下SessionFactory不会启用这个缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或者硬盘。

Hibernate二级缓存支持的缓存插件:

  • EHCache: 可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持
  • OpenSymphony`:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持
  • SwarmCache:可作为集群范围内的缓存,但不支持Hibernate的查询缓存
  • JBossCache:可作为集群范围内的缓存,支持Hibernate的查询缓存

hibernate.cfg.xml中开启二级缓存配置:

<!-- 开启二级缓存 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>
		<!-- 开启查询缓存 -->
		<property name="hibernate.cache.use_query_cache">true</property>
		<!-- 配置二级缓存技术提供者 -->
		<property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>


四、检索策略

Hibernate的Session在加载一个Java对象时,可以将与这个对象相关联的其他Java对象都加载到缓存中,以便程序及时调用。但有些情况下,我们不需要加载太多无用的对象到缓存中,一来这样会撑爆内存,二来增加了访问数据库的次数。所以为了合理的使用缓存,Hibernate提供了几种检索策略来供用户选择。


在Hibernate中主要有三种检索策略:

  • 立即检索策略
  • 延迟检索策略
  • 左外连接检索策略


5.1 立即检索策略

采用立即检索策略,会将被检索的对象,以及和这个对象关联的一对多对象都加载到缓存中。Session的get方法就使用的立即检索策略。

优点:频繁使用的关联对象能够被加载到缓存中。

缺点:1、占用内存。2、Select语句过多。


5.2 延迟检索策略

采用延迟检索策略,就不会加载关联对象的内容。直到第一次调用关联对象时,才去加载关联对象。在不涉及关联类操作时,延迟检索策略只适用于Session的load方法。涉及关联类操作时,延迟检索策略也能够适用于get,list等操作。

延迟加载配置:

<!-- 
	name属性它是实体类的全名 
	table 表的名称 
	catalog 数据库名称
	lazy 是否延迟加载
-->
<class name="Customer" table="t_customer" catalog="hibernateTest" lazy="true">
...
</class>

延迟加载检索策略分为两种:
  • 类级别检索
  • 关联级别检索

在类级别操作时, 延迟检索策略,只加载类的OID不加载类的其他属性,只用当第一次访问其他属性时,才回访问数据库去加载内容。

在关联级别操作时,延迟检索策略,只加载类本身,不加载关联类,直到第一次调用关联对象时,才去加载关联对象

程序模式都是用延迟加载策略。如果需要指定使用延迟加载策略。在配置文件中设置<class>的lazy=true,<set>的lazy=true或extra(增强延迟)<many-to-one>的lazy=proxy和no-proxy。

优点:由程序决定加载哪些类和内容,避免了大量无用的sql语句和内存消耗。
缺点:在Session关闭后,就不能访问关联类对象了。 需要确保在Session.close方法前,调用关联对象。


OneToMany中,<set>设置的fetch与lazy可以使用下面注解来描述:
@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT) 
	@LazyCollection(LazyCollectionOption.TRUE)
	private Set<Order> orders = new HashSet<Order>();
等价配置:
<set name="orders" inverse="true" cascade="save-update" lazy="true" fetch="select">

ManyToOne或OneToOne中,使用注解配置fetch与lazy如下:

@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "c_customer_id")
	@Cascade(CascadeType.SAVE_UPDATE)
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT)
	@LazyToOne(LazyToOneOption.FALSE)
	private  Customer c;
等价配置:

<many-to-one fetch="select" cascade="save-update" lazy=false name="c" class="com.hibernate.domain.Customer" column="c_customer_id">


5.2.1 OneToMany中set上的fetch与lazy主要用于设置关联的集合信息的抓取策略:

fetch可取值有:

  • select 生成多条简单的查询sql(默认值)
  • join 采用迫切左外连接
  • subselect 将生成子查询的sql

lazy可取值有:

  • true 坚持检索(默认值)
  • false 立即检索
  • extra 加强延迟检索


fetch和lazy的组合使用情况:

(1) 第一种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT) 
	@LazyCollection(LazyCollectionOption.TRUE)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
会首先查询客户信息,当需要订单信息时,才会关联查询订单信息


(2) 第二种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT) 
	@LazyCollection(LazyCollectionOption.FALSE)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
当查询客户信息时,就会将订单信息也查询,也就是说订单信息没有进行延迟

(3) 第三种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT) 
	@LazyCollection(LazyCollectionOption.EXTRA)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
当查询客户信息时,不会查询订单信息,当需要订单的个数时,也不会查询订单信息,只会通过count来统计订单个数。

当我们使用size()、contains()或isEmpty()方法时不会查询订单信息


(4) 第四种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.JOIN) 
	@LazyCollection(LazyCollectionOption.FALSE)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
如果fetch选中的是join方案,那么lazy会失效,生成的SQL将采用的是迫切左外连接


(5) 第五种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SUBSELECT) 
	@LazyCollection(LazyCollectionOption.TRUE)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
会生成子查询,但是我们在查询订单时采用的是延迟加载


(6) 第六种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SUBSELECT) 
	@LazyCollection(LazyCollectionOption.FALSE)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
会生成子查询,在查询客户信息时,就会将订单信息也查询出来

(7) 第七种组合

@OneToMany(targetEntity = Order.class, mappedBy = "c") 
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SUBSELECT) 
	@LazyCollection(LazyCollectionOption.EXTRA)
	@BatchSize(size=3)
	private Set<Order> orders = new HashSet<Order>();
在查询订单时,只会根据情况来确定是否要订单信息


5.2.2 ManyToOne中的fetch与lazy主要用于设置关联的集合信息的抓取策略:

fetch可取值:

  • select 默认值,代表发送一条或多条简单的select语句
  • join 发送一条迫切左外连接

lazy可取值:

  • false 不采用延迟加载
  • proxy 默认值,是否采用延迟,需要另一方的类级别延迟策略来决定
  • no-proxy 

fetch和lazy的组合使用情况:

(1) 第一种组合

@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "c_customer_id")
	@Cascade(CascadeType.SAVE_UPDATE)
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT)
	@LazyToOne(LazyToOneOption.PROXY)
	private  Customer c;
注意:Customer的类级别延迟策略:

@Proxy(lazy = true)
public class Customer {
...
}

当我们执行时,会首先发送一条sql只查询订单信息,客户信息会延迟,只有真正需要客户信息时,才会发送sql来查询客户信息


(2) 第二种情况

@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "c_customer_id")
	@Cascade(CascadeType.SAVE_UPDATE)
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT)
	@LazyToOne(LazyToOneOption.PROXY)
	private  Customer c;
注意:Customer的类级别延迟策略:

@Proxy(lazy = false)
public class Customer {
...
}


(3) 第三种情况

@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "c_customer_id")
	@Cascade(CascadeType.SAVE_UPDATE)
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.SELECT)
	@LazyToOne(LazyToOneOption.FALSE)
当查询订单时,不会对客户信息进行延迟,立即查询客户信息


(4) 第四种组合

@ManyToOne(targetEntity = Customer.class)
	@JoinColumn(name = "c_customer_id")
	@Cascade(CascadeType.SAVE_UPDATE)
	//可以替代配置文件中,在set上设置的fetch和lazy
	@Fetch(FetchMode.JOIN)
	@LazyToOne(LazyToOneOption.FALSE)
	private  Customer c;
如果fetch值为join,那么lazy失效。会发送一条迫切左外连接来查询,也就立即查询。


5.3 左外连接检索策略

采用左外连接检索,能够使用Sql的外连接查询,将需要加载的关联对象加载在缓存中。
<set>fetch设置为join,<many-to-one>的fetch设置为 join

优点:1.对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象。2.使用了外连接,select语句数目少。
缺点:1.可能会加载应用程序不需要访问的对象,白白浪费许多内存空间。2.复杂的数据库表连接也会影响检索性能。

batch-size属性:
无论是立即检索还是延迟检索,都可以指定关联查询的数量,这就需要使用batch-size属性来指定,指定关联查询数量,以减少批量检索的数据数目。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值