Hibernate学习笔记(十三)

性能优化策略

1、 注意session.clear()的动用,尤其在不断分页循环的时候

a) 在一个大集合中进行遍历,遍历msg,取出其中的含有敏感字样的对象

b) 另外一种形式的内存泄露 //面试是:Java有内存泄漏吗?(语法级别没有,但写程序时用到了其中的一些资源要注意回收,比如打开了连接一定要注意关闭,打开文件一定要关闭,不然它不能调用本地文件把他关闭,其实就是java调用了cc调用了windows  apic有内存泄露。)

2、 1 + N问题 //本来只要取出Topic,但是因为ManyToOne默认的Fetch类型是eager所以它会把与Topic关联的所有category都取出来,这是典型的面试题,下面是解决方案:

a) 把Fetch设为Lazy,那么后面的查询语句就变成需要的时候才发出

b) BatchSize  设置在category实体类的前面

c) join fetch

3、 list 和 iterate不同之处

a) list取所有

b) Iterate先取ID,等用到的时候再根据ID来取对象

c) session中list第二次发出,仍会到数据库查询

d) iterate第二次,首先找session级缓存

hibernate缓存

一、 Session级缓存(一级缓存)

一级缓存很短和session的生命周期一致,因此也叫session级缓存或事务级缓存

hibernate一级缓存

那些方法支持一级缓存:

* get()

* load()

* iterate(查询实体对象)

如何管理一级缓存:

* session.clear(),session.evict()

如何避免一次性大量的实体数据入库导致内存溢出

flush,再clear

如果数据量特别大,考虑采用jdbc实现,如果jdbc也不能满足要求可以考虑采用数据本身的特定导入工具

二、 二级缓存

二级缓存也称为进程级的缓存,也可称为SessionFactory级的缓存(因为SessionFactory可以管理二级缓存),它与session级缓存不一样,一级缓存只要session关闭缓存就不存在了。而二级缓存则只要进程在二级缓存就可用。二级缓存可以被所有的session共享,二级缓存的生命周期和SessionFactory的生命周期一样,SessionFactory可以管理二级缓存,二级缓存同session级缓存一样,只缓存实体对象,普通属性的查询不会缓存,二级缓存一般使用第三方的产品,如EHCache

1、 二级缓存的配置和使用:

配置二级缓存的配置文件:模板文件位于hibernate\etc目录下(ehcache.xml),将模板存放在ClassPath目录中,一般放在根目录下(src目录下)

<ehcache>
    <!-- 设置当缓存对象益出时,对象保存到磁盘时的保存路径。
            如 d:\xxxx
         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - windows的临时目录 -->
    <diskStore path="java.io.tmpdir"/>

    <!--默认配置/或对某一个类进行管理
        maxInMemory       - 缓存中可以存入的最多个对象数 
        eternal           - true:表示永不失效,false:不是永久有效的。 
        timeToIdleSeconds - 空闲时间,当第一次访问后在空闲时间内没有访问,则对象失效,单位为秒 
        timeToLiveSeconds - 被缓存的对象有效的生命时间,单位为秒 
      overflowToDisk  当缓存中对象数超过核定数(益出时)时,对象是否保存到磁盘上。true:保存;false:不保存
        					如果保存,则保存路径在标签<diskStore>中属性path指定
        -->
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />	
</ehcache>

2、 二级缓存的开启:

Hibernate中二级缓存默认就是开启的,也可以显示的开启

二级缓存是hibernate的配置文件设置如下:

<!-- 开启二级缓存,hibernate默认的二级缓存就是开启的 -->
		<property name="hibernate.cache.use_second_level_cache">true</property>

3、 使用二级缓存

a) xml方式:指定哪些实体类使用二级缓存:

方法一:在实体类映射文件中,使用<cache>来指定那个实体类使用二级缓存,如下:

<cache 
    usage="transactional|read-write|nonstrict-read-write|read-only"  (1)
    region="RegionName"                                              (2)
    include="all|non-lazy"                                           (3)
/>

(1) usage(必须)说明了缓存的策略: transactional、 read-write、 nonstrict-read-write或 read-only。 

(2) region (可选, 默认为类或者集合的名字(class or collection role name)) 指定第二级缓存的区域名(name of the second level cache region) 

(3) include (可选,默认为 all) non-lazy 当属性级延迟抓取打开时, 标记为lazy="true"的实体的属性可能无法被缓存 ,你可以在hibernate.cfg.xml中指定<class-cache>和 <collection-cache> 元素。 

这里的usage 属性指明了缓存并发策略(cache concurrency strategy。 

策略:只读缓存(Strategy: read only) 

如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。 

<class name="eg.Immutable" mutable="false">

    <cache usage="read-only"/>

    ....

</class>

策略:/写缓存(Strategy: read/write) 

如果应用程序需要更新数据,那么使用读/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存,你必须指定hibernate.transaction.manager_lookup_class属性的值, 通过它,Hibernate才能知道该应用程序中JTA的TransactionManager的具体策略。 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。 

<class name="eg.Cat" .... >

    <cache usage="read-write"/>

    ....

    <set name="kittens" ... >

        <cache usage="read-write"/>

        ....

    </set>

</class>

策略:非严格读/写缓存(Strategy: nonstrict read/write) 

如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离, 那么比较适合使用非严格读/写缓存策略。如果在JTA环境中使用该策略, 你必须为其指定hibernate.transaction.manager_lookup_class属性的值, 在其它环境中,你必须保证在Session.close()、或Session.disconnect()调用前, 整个事务已经结束。 

策略:事务缓存(transactional) 

Hibernate的事务缓存策略提供了全事务的缓存支持, 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中,你必须指定 为其hibernate.transaction.manager_lookup_class属性。 

没有一种缓存提供商能够支持上列的所有缓存并发策略。

方法二:在hibernate配置文件(hibernate.cfg.xml)使用<class-cache>标签中指定

要求:<class-cache>标签必须放在<maping>标签之后。

<hibernate-configuration>
	<session-factory>	
		…………	
	<mapping resource="csy/model/Classes.hbm.xml"/>
		<mapping resource="csy/model/Student.hbm.xml"/>
		
		<class-cache class="csy.model.Student" usage="read-only"/>
	</session-factory>
</hibernate-configuration>

一般推荐使用方法一。

b) annotation注解

为了优化数据库访问,你可以激活所谓的Hibernate二级缓存.该缓存是可以按每个实体和集合进行配置的.

@org.hibernate.annotations.Cache定义了缓存策略及给定的二级缓存的范围. 此注解适用于根实体(非子实体),还有集合.

4、 二级缓存的管理:

 (1)、 清除指定实体类的所有数据

SessionFactory.evict(Student.class);

(2)、 清除指定实体类的指定对象

SessionFactory.evict(Student.class, 1);//第二个参数是指定对象的ID,就可以清除指定ID的对象

5、 二级缓存的交互

Session session = null;
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			
			//仅向二级缓存读数据,而不向二级缓存写数据
			session.setCacheMode(CacheMode.GET);
			Student student = (Student)session.load(Student.class, 1);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
		
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			
			//发出sql语句,因为session设置了CacheMode为GET,所以二级缓存中没有数据
			Student student = (Student)session.load(Student.class, 1);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}
		
		try {
			session = HibernateUtils.getSession();
			session.beginTransaction();
			
			//只向二级缓存写数据,而不从二级缓存读数据
			session.setCacheMode(CacheMode.PUT);
			
			//会发出查询sql,因为session将CacheMode设置成了PUT
			Student student = (Student)session.load(Student.class, 1);
			System.out.println("student.name=" + student.getName());
			
			session.getTransaction().commit();
		}catch(Exception e) {
			e.printStackTrace();
			session.getTransaction().rollback();
		}finally {
			HibernateUtils.closeSession(session);
		}

CacheMode参数用于控制具体的Session如何与二级缓存进行交互。 

*CacheMode.NORMAL - 从二级缓存中读、写数据。 

*CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。 

*CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。 

CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。

通过 hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

6、 总结

load默认使用二级缓存,iterate默认使用二级缓存

list默认向二级缓存中加数据,但是查询时候不使用

三、 查询缓存

查询缓存,是用于缓存普通属性查询的,当查询实体时缓存实体ID。

默认情况下关闭,需要打开。查询缓存,对list/iterator这样的操作会起作用。

可以使用<property name=”hibernate.cache.use_query_cache”>true</property>来打开查询缓存,默认为关闭。

所谓查询缓存:即让hibernate缓存list、iterator、createQuery等方法的查询结果集。如果没有打开查询缓存,hibernate将只缓存load方法获得的单个持久化对象。

在打开了查询缓存之后,需要注意,调用query.list()操作之前,必须显式调用query.setCachable(true)来标识某个查询使用缓存。

查询缓存的生命周期:当前关联的表发生修改,那么查询缓存生命周期结束

注意查询缓存依赖于二级缓存,因为使用查询缓存需要打开二级缓存

查询缓存的配置和使用:

hibernate.cfg.xml文件中启用查询缓存

*在程序中必须手动启用查询缓存

例如:

session = HibernateUtils.getSession();
			session.beginTransaction();
			Query query = session.createQuery("select s.name from Student s");
			//启用查询查询缓存
			query.setCacheable(true);
			
			List names = query.list(); 
			for (Iterator iter=names.iterator();iter.hasNext(); ) {
				String name = (String)iter.next();
				System.out.println(name);
			}
			System.out.println("-------------------------------------");
			query = session.createQuery("select s.name from Student s");
			//启用查询查询缓存
			query.setCacheable(true);
			
			//没有发出查询sql,因为启用了查询缓存
			names = query.list(); 
			for (Iterator iter=names.iterator();iter.hasNext(); ) {
				String name = (String)iter.next();
				System.out.println(name);
			}
	session.getTransaction().commit();

注:查询缓存的生命周期与session无关。查询缓存只对query.list()起作用,query.iterate不起作用,也就是query.iterate不使用

四、 缓存算法

1、 LRU(least recently used)、LFU(least frequently used)、FIFO(first in first out)

2.ehcache.xml中设置memeoryStoreEvictionPolicy = "LRU"

事务并发处理

一、 数据库的隔离级别:并发性作用。

1、 Read Uncommited(未提交读):没有提交就可以读取到数据(发出了Insert,但没有commit就可以读取到。)很少用

2、 Read Commited(提交读):只有提交后才可以读,常用,

3、 Repeatable Read(可重复读):mysql默认级别, 必需提交才能见到,读取数据时数据被锁住。

4、 Serialiazble(序列化读):最高隔离级别,串型的,你操作完了,我才可以操作,并发性特别不好,

脏读:没有提交就可以读取到数据称为脏读

不可重复读:再重复读一次,数据与你上的不一样。称不可重复读。

幻读:在查询某一条件的数据,开始查询的后,别人又加入或删除些数据,再读取时与原来的数据不一样了。

*使用select @@tx_isolation来查看Mysql数据库的隔离级别

*使用set transaction isolation level 隔离级别名称;来修改Mysql数据库隔离级别

例如:修改为未提交读:set transaction isolation level read uncommitted;

 hibernate悲观锁、乐观锁

Hibernate谈到悲观锁、乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差

一、 悲观锁

悲观锁:具有排他性(我锁住当前数据后,别人看到不此数据)悲观锁一般由数据机制来做到的。

如果需要使用悲观锁,肯定在加载数据时就要锁住,通常采用数据库的for update语句。

Hibernate使用Load进行悲观锁加载。

Session.load(Class arg0, Serializable arg1, LockMode arg2) throws HibernateException

LockMode:悲观锁模式(一般使用LockMode.UPGRADE)

session = HibernateUtils.getSession();
			tx = session.beginTransaction();
			Inventory inv = (Inventory)session.load(Inventory.class, 1, LockMode.UPGRADE);
			System.out.println(inv.getItemName());
			inv.setQuantity(inv.getQuantity()-200);
			
			session.update(inv);
			tx.commit();

注:如果使用悲观锁,那么lazy(懒加载无效)

二、 乐观锁

乐观锁:不是锁,是一种冲突检测机制。

乐观锁的并发性较好,因为我改的时候,别人随边修改。

乐观锁的实现方式:常用的是版本的方式(每个数据表中有一个版本字段version,某一个用户更新数据后,版本号+1,另一个用户修改后再+1,当用户更新发现数据库当前版本号与读取数据时版本号不一致(等于小于数据库当前版本号),则更新不了。Hibernate使用乐观锁需要在映射文件中配置项才可生效。

<hibernate-mapping>
<!-- 映射实体类时,需要加入一个开启乐观锁的属性 optimistic-lock="version" 共有好几种方式:
	- none	- version	- dirty	- all
	同时需要在主键映射后面映射版本号字段
	-->
<class name="csy.model.Inventory" optimistic-lock="version">
		<id name="itemNo">
			<generator class="native"/>
		</id>
		<version name="version"/><!—必需配置在主键映射后面 -->
		<property name="itemName"/>
		<property name="quantity"/>
	</class>
</hibernate-mapping>

乐观锁在存储数据时不用关心





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值