使用悲观锁(Pessimistic Locking)解决事务并发问题
悲观锁:正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
Hibernate使用悲观锁十分容易,但实际应用中悲观锁是很少被使用的,因为它大大限制了并发性。
用乐观锁(Optimistic Locking)解决事务并发问题
相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。
乐观锁,大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个"version"字段来实现。
乐观锁的工作原理:读取出数据时,将此版本号一同读出,并在更新数据时比较版本号与数据库表中的版本号,如果等于数据库表中的版本号则予以更新,并递增版本号,如果小于数据库表中的版本号就抛出异常。
Hibernate为乐观锁提供了3中实现:
1. 基于version
配置基于version的乐观锁:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.suxiaolei.hibernate.pojos.People" table="people">
<id name="id" type="string">
<column name="id"></column>
<generator class="uuid"></generator>
</id>
<!-- version标签用于指定表示版本号的字段信息 -->
<version name="version" column="version" type="integer"></version>
<property name="name" column="name" type="string"></property>
</class>
</hibernate-mapping>
2. 基于timestamp
配置基于timestamp的乐观锁:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.suxiaolei.hibernate.pojos.People" table="people">
<id name="id" type="string">
<column name="id"></column>
<generator class="uuid"></generator>
</id>
<!-- timestamp标签用于指定表示版本号的字段信息 -->
<timestamp name="updateDate" column="updateDate"></timestamp>
<property name="name" column="name" type="string"></property>
</class>
</hibernate-mapping>
3. 为遗留项目添加乐观锁 :
遗留项目,由于各种原因无法为原有的数据库添加"version"或"timestamp"字段,这时不可以使用上面两种方式配置乐观锁,Hibernate为这种情况提供了一个"optimisitic-lock"属性,它位于<class>标签上
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.suxiaolei.hibernate.pojos.People" table="people" optimistic-lock="all">
<id name="id" type="string">
<column name="id"></column>
<generator class="uuid"></generator>
</id>
<property name="name" column="name" type="string"></property>
</class>
</hibernate-mapping>
将该属性的值设置为all,让该记录所有的字段都为版本控制信息。
Hibernate n+1的问题:
select语句的数目太多,需要频繁的访问数据库,会影响检索性能.
加载的对象完全是多余的操作,这些多余的对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了两种检索策略:延迟检索策略和迫切左外连接检索策略
1、延迟检索策略能避免多余加载应用程序不需要访问的关联对象,
hibernate3开始已经默认是lazy=true了;lazy=true时不会立刻查询关联对象,只有当需要关联对象(访问其属性)时才会发生查询动作。
2、迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
可以在映射文件中定义连接抓取方式。
<set name=”orders” fetch=”join”>
<key column=”customer_id”>
<one-to-many class="com.hibernate.mappings.Order"/>
</set>
或者使用HQL的LEFT OUTER JOIN.
或者在条件查询中使用setFetchMode(FetchMode.JOIN)如:
Customer ctm = (Customer)session.createCriteria(Customer.class)
.setFetchMode(“Order”.JOIN)
.add(Restrictions.idEq(customer_id));
Hibernate load()方法:
使用load()时如果在session关闭之后再查询此对象,会报异常:could not initialize proxy - no Session。处理办法:在session关闭之前初始化一下查询出来的对象:Hibernate.initialize(user);
使用load()可以提高效率,因为刚开始的时候并没有查询数据库。但很少使用。
Hibernate缓存:
Hibernate缓存包括两大类:
Hibernate一级缓存:事务范围的缓存
Hibernate二级缓存:进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。第二级缓存是可选的,是一个可配置的插件,在默认情况下,SessionFactory不会启用这个插件。
什么样的数据适合存放到第二级缓存中?
1 很少被修改的数据
2 不是很重要的数据,允许出现偶尔并发的数据
3 不会被并发访问的数据
4 常量数据
不适合存放到第二级缓存的数据?
1经常被修改的数据
2 .绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3 与其他应用共享的数据。
hibernate配置文件中相关配置:
<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
<!-- 二级缓存区域名的前缀 -->
<!--<property name="hibernate.cache.region_prefix">h3test</property>-->
<!-- 高速缓存提供程序 -->
<property name="hibernate.cache.region.factory_class">net.sf.ehcache.hibernate.EhCacheRegionFactory</property>
<!-- 指定缓存配置文件位置 -->
<property name="hibernate.cache.provider_configuration_file_resource_path">
ehcache.xml
</property>
<!-- 强制Hibernate以更人性化的格式将数据存入二级缓存 -->
<property name="hibernate.cache.use_structured_entries">true</property>
<!-- Hibernate将收集有助于性能调节的统计数据 -->
<property name="hibernate.generate_statistics">true</property>
启用第三方缓存产品 (可改变,上面eache是hibernate官方默认的第三方缓存产品)
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>