一、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中主要有三种检索策略:
- 立即检索策略
- 延迟检索策略
- 左外连接检索策略
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(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属性来指定,指定关联查询数量,以减少批量检索的数据数目。