Hibernate 优化

◆一级缓存:

 

  Session提供和维护,缓存操作的实体对象,以key - value map方式存储

  key就是对象的IDvalue就是对象

 

  连续的大批量数据操作需要考虑内存占用问题

  session的以下方法为我们提供了手段:

  evict()把某个对象从一级缓存中去掉

  clear()全部清除一级缓存,删除和更新大量对象时候需要及时调用此方法释放内存,否则有内存益出的危险

  flush()同步更新缓存中被改变的对象,对象的状态会和数据库取得同步

 

◆二级缓存

  

  SessionFactory提供的缓存机制叫二级缓存,分内置缓存和外置缓存

  内置缓存的内容有:映射数据,预定义的sql语句

  外置缓存默认不启用,除非进行了配置,只有这一部分才叫通常的二级缓存,缓存单个实体

  ID映射到对象的方式缓存),缓存结果集是把相关的查询信息做键,

  对应值是结果集(或者仅仅是ID集合),这些数据在所有session中共享

注意,缓存永远不知道其他应用程序对持久化仓库(数据库)可能进行的修改

  通常以下数据适合使用二级缓存:

  很少被修改,很少并发访问的数据对象

 

  一、二级缓存配置

启用二级缓存要经历以下步骤:

  1。修改hibernate配置文件增加缓存提供者

     <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

<property name="hibernate.cache.use_query_cache">true</property>

 

  2。配置EHCache的基本配置参数,在包的默认路径下,跟hibernate.cfg.xml放一起。

     在类包中建立ehcache.xml 根据ehcache.xsd的约束进行配置

<?xml version="1.0" encoding="UTF-8"?>

<ehcache>

  <diskStore path="Java.io.tmpdir"/>

  <defaultCache

     maxElementsInMemory="10000"

     eternal="false"

     overflowToDisk="true"

     timeToIdleSeconds="300"

     timeToLiveSeconds="180"

     diskPersistent="false"

     />

</ehcache>

defaultCache缓存最大数目,

eternal缓存是否持久,

overflowToDisk是否保存到磁盘,

timeToIdleSeconds当缓存闲置n秒后销毁

timeToLiveSeconds当缓存存活n秒后销毁

 

  3。在需要缓存的实体的映射文件中class标签中进行相应的缓存策略配置

     <cache usage="read-write| read-only "/>

如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。

 

 

二、二级缓存使用。

查询方法与二级缓存有很大关系

load() 对一个实体的查找顺序是:一级缓存à二级缓存à数据库

get()对一个实体的查找顺序是:一级缓存à数据库

list()只能使用二级缓存的查询缓存,不能使用二级缓存的单个对象缓存,除非进行相同的查询,否则无法使用二级缓存。如果缓存中没有这样的查询则直接去数据库查询所有对象

iterator()可以充分使用二级缓存,如果是第一次执行,它会到数据库查询所有满足条件的对象的 ID ,形成几个 ID

合,然后遍历时候再根据ID查找具体对象,因此对于n条记录,它可能进行n+1次查询。但是在根据ID查找对象时

候它可以利用二级缓存的单个对象缓存。

查询的结果集也可以被缓存。只有当经常使用同样的参数进行查询时,这才会有些用处。绝大多数的查询并不能从

查询缓存中受益,所以Hibernate默认是不进行查询缓存的。如若需要进行缓存,请调用 Query.setCacheable(true) 方法。

这个调用会让查询在执行过程中时先从缓存中查找结果, 并将自己的结果集放到缓存中去。

如果查询需要强行刷新其查询缓存区域,那么你应该调用Query.setCacheMode(CacheMode.REFRESH) 方法。 这对

在其他进程中修改底层数据(例如,不通过Hibernate修改数据),或对那些需要选择性更新特定查询结果集的情况特别有用。

三、二级缓存查看。

我们可以检查缓存的命中成功次数,缓存的命中失败次数,实体、集合和查询的使用概率,查询的平均时间等。请注意 Java中时间的近似精度是毫秒。Hibernate的数据精度和具体的JVM有关,在有些平台上其精度甚至只能精确到10秒。

你可以直接使用getter方法得到全局数据记录(例如,和具体的实体、集合、缓存区无关的数据),你也可以在具体查询中通过标记实体名、 HQLSQL语句得到某实体的数据记录。请参考StatisticsEntityStatistics CollectionStatisticsSecondLevelCacheStatistics QueryStatisticsAPI文档以抓取更多信息。下面的代码则是个简单的例子:

Statistics stats = HibernateUtil.sessionFactory.getStatistics();

 

double queryCacheHitCount  = stats.getQueryCacheHitCount();

double queryCacheMissCount = stats.getQueryCacheMissCount();

double queryCacheHitRatio =

  queryCacheHitCount / (queryCacheHitCount + queryCacheMissCount);

 

System.out.println("Query Hit ratio:" + queryCacheHitRatio);

 

EntityStatistics entityStats =

  stats.getEntityStatistics( Cat.class.getName() );

long changes =

        entityStats.getInsertCount()

        + entityStats.getUpdateCount()

        + entityStats.getDeleteCount();

System.out.println (Cat.class.getName() + " changed " + changes + "times"  );

如果你想得到所有实体、集合、查询和缓存区的数据,你可以通过以下方法获得实体、集合、查询和缓存区列表: getQueries()getEntityNames() getCollectionRoleNames() getSecondLevelCacheRegionNames()

 

四、二级缓存管理。

二级缓存来说,在SessionFactory 中定义了许多方法, 清除缓存中实例、整个类、集合实例或者整个集合。

sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class); //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //清除特定集合

kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collections

◆批量查询和修改

  一般在hibernate中需要删除一条数据时候需要先执行查询操作,得到需要修改的数据之后再进行删除或者修改。但是这种方式在处理大量数据时候就会出性能问题。Hibernate3中引入了批量更新和删除的语句。

   Stirng hql = "delete User"  然后调用query.executeUpdate()方法。注意大数据量操作对一级缓存的影响,需要及时清楚缓存对象。(参考一级缓存)

 

     关联查询的优化(抓取策略Fetching strategy

 

抓取策略在获取关联对象时的策略,可以在O/R映射时指明,也可以在写hql语句时指定。Hibernate3定义了以下几个根据抓取策略分:

连接抓取(join fetching)使用外连方式取关联数据,如果使用了连接抓取,延迟就失去意义。

查询抓取(select fetching)使用到关联对象时候进行关联查询

根据抓取的时间分:

立即抓取(immediate fetching

延迟集合抓取(lazy collection fetching

延迟代理抓取(lazy proxy )单值关联时,调用关联方法时候提取

延迟属性加载(lazy attribute)关联对象访问时候才执行提取

 

一、延迟加载。

为了提高效率需要设置延迟加载,通常lazy设置可以在两个级别上设置。一个是类级别如:

<class name="com.accp.entity.Stuinfo" table="STUINFO" schema="SCOTT" lazy="true">

就是设置当前Stuinfo类延迟加载,这样的话如果你在session关闭后对Stuinfo实体进行属性获取就会出现延迟初始化异常。当然通常我们是需要非延迟加载这类的就是通常把类级别lazy设置为false(默认false)。

另外一个就是在属性级别设置延迟加载,这个对于关联对象属性或者关联集合属性很有用,对于属性级别设置延迟加载推荐除非必要都需要在在xml中设置lazy = true,或者不写,就是查询当前对象时不自动加载关联集合或者对象。

      对于设置成lazy=true的对象或者集合,如果非要使用也可以,你必须在关闭session之前对其进行初始化,方法是:

 先使用Hibernate.isInitialized(obj) 判断一个对象或者集合是否被初始化了,是返回true,如果没初始化则调用Hibernate.initialize(obj)进行初始化。

 

二、非延迟加载。

如果在检索当前对象时候需要同时使用其关联对象,就要设置非延迟加载,对关联对象的lazy=false,加载关联集合

或者对象,在这个时候抓取策略就应该考虑了。为减少SQL语句提高效率我们尽量使用join fetching,就是让Hibernate生成连接的查询同时检索关联对象。

     有时候Query对象不支持join查询(除非写成left join查询或者使用get/load方法检索单个对象),这样的话对query查询

优化只能使用select fetching 策略,这个时候设置batch-size="n" ,可以提高一些效率。全局在hibernate核心配置中指定批抓取:

       <property name="jdbc.batch_size">5</property>

     也可以使用Criteria查询,使用join查询减少sql语句数量.要使Criteria查询生成left join 语句有2个办法:

          1  xml中的set 或者 many-to-one中指定 fetch = join

          2  调用Criteria方法动态指定 setFetchMode("xxx", FetchMode.JOIN)

             xxx表示要连接的集合或者对象属性

 

    

 

 

u       设置结合配置接点的inverse="true",减少更新操作关联对象时多余的sql语句。

        例如在多对一中的多方增加一个对象,如果同步更新多端集合,保存新增对象时通常会产生2

        sql语句,一个是插入新的对象,一个是更新一方记录。其实后者是没有必要的。这个时候如果

        设置一方集合配置的inverse="true"属性就可以避免多余sql

 

 

u       Hibernate会话管理

   利于ThreadLocal模式管理Session

 

 

在利用Hibernate开发DAO模块时,我们和Session打的交道最多,所以如何合理的管理Session,避免Session的频繁创建和销毁,对于提高系统的性能来说是非常重要的,以往是通过eclipse的插件来自动完成这些代码的,当然效果是不错的,但是总是觉得不爽(没有读懂那些冗长的代码),所以现在打算自己实现Session管理的代码。

 

我们知道Session是由SessionFactory负责创建的,而 SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么 Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个 Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?

 

Session的众多管理方案中,我们今天来认识一种名ThreadLocal模式的解决方案。

 

早在Java1.2推出之时,Java平台中就引入了一个新的支持: java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。ThreadLocal是什么呢?其实 ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用某变量的线程都提供一个该变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。

 

 

u       研究:Query或者Criteria对象以下方法的使用:

1.setCacheable(true)

绝大多数的查询并不能从查询二级缓存中受益,所以Hibernate默认是不进行查询缓存的。如若需要进行缓存,请调用 Query.setCacheable(true)方法。这个调用会让查询在执行过程中时先从缓存中查找结果, 并将自己的结果集放到缓存中去。

2.setCacheMode(null)

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

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

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

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

CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过 hibernate.cache.use_minimal_puts的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。

3.setCacheRegion("")二级缓存的查询缓存相关

4.setFetchSize(5)

是设置resultSet每次向数据库取的行数,在Hibernate2.1.6或者更后版本,Queryscroll() 这个方法将是最好的途径.

注意区别setMaxRows 是设置resultset最多只返回的行数。

5.setLockMode()

用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔 离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定, 或者在一个新的事务启动的时候,重新进行锁定。

Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!类LockMode 定义了Hibernate所需的不同的锁定级别。一个锁定 可以通过以下的机制来设置: Hibernate更新或者插入一行记录的时候,锁定级别自动设置为LockMode.WRITE 当用户显式的使用数据库支持的SQL格式SELECT ... FOR UPDATE 发送SQL的时候,锁定级别设置为LockMode.UPGRADE

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值