1. 如何提高hibernate性能
这就意味着 业务层应该载入所有表现层/web层所需的数据,并将这些已实例化完毕的数据返回。通常,应用程序应该 为web层所需的每个集合调用Hibernate.initialize() (这个调用必须发生咱session关闭之前); 或者使用带有FETCH 从句,或FetchMode.JOIN 的Hibernate查询, 事先取得所有的数据集合。如果你在应用中使用了Command模式,代替Session Facade , 那么这项任务将会变得简单的多。 你也可以通过merge() 或lock() 方法,在访问未实例化的集合(或代理)之前, 为先前载入的对象绑定一个新的Session 。 显然,Hibernate将不会,也不应该自动完成这些任务,因为这将引入一个特殊的事务语义。 有时候,你并不需要完全实例化整个大的集合,仅需要了解它的部分信息(例如其大小)、或者集合的部分内容。 你可以使用集合过滤器得到其集合的大小,而不必实例化整个集合: ( (Integer) s.createFilter( collection, "select count(*)" ).list().get(0) ).intValue()这里的createFilter() 方法也可以被用来有效的抓取集合的部分内容,而无需实例化整个集合: s.createFilter( lazyCollection, "").setFirstResult(0).setMaxResults(10).list();20.1.5. 使用批量抓取(Using batch fetching) Hibernate可以充分有效的使用批量抓取,也就是说,如果仅一个访问代理(或集合),那么Hibernate将不载入其他未实例化的代理。 批量抓取是延迟查询抓取的优化方案,你可以在两种批量抓取方案之间进行选择:在类级别和集合级别。 类/实体级别的批量抓取很容易理解。假设你在运行时将需要面对下面的问题:你在一个Session 中载入了25个 Cat 实例,每个Cat 实例都拥有一个引用成员owner , 其指向Person ,而Person 类是代理,同时lazy="true" 。 如果你必须遍历整个cats集合,对每个元素调用getOwner() 方法,Hibernate将会默认的执行25次SELECT 查询, 得到其owner的代理对象。这时,你可以通过在映射文件的Person 属性,显式声明batch-size ,改变其行为: ...随之,Hibernate将只需要执行三次查询,分别为10、10、 5。 你也可以在集合级别定义批量抓取。例如,如果每个Person 都拥有一个延迟载入的Cats 集合, 现在,Sesssion 中载入了10个person对象,遍历person集合将会引起10次SELECT 查询, 每次查询都会调用getCats() 方法。如果你在Person 的映射定义部分,允许对cats 批量抓取, 那么,Hibernate将可以预先抓取整个集合。请看例子: ... 如果整个的batch-size 是3(笔误?),那么Hibernate将会分四次执行SELECT 查询, 按照3、3、3、1的大小分别载入数据。这里的每次载入的数据量还具体依赖于当前Session 中未实例化集合的个数。 如果你的模型中有嵌套的树状结构,例如典型的帐单-原料结构(bill-of-materials pattern),集合的批量抓取是非常有用的。(尽管在更多情况下对树进行读取时,嵌套集合(nested set)或原料路径(materialized path)(××)是更好的解决方法。) 20.1.6. 使用子查询抓取(Using subselect fetching) 假若一个延迟集合或单值代理需要抓取,Hibernate会使用一个subselect重新运行原来的查询,一次性读入所有的实例。这和批量抓取的实现方法是一样的,不会有破碎的加载。 20.1.7. 使用延迟属性抓取(Using lazy property fetching) Hibernate3 对单独的属性支持延迟抓取,这项优化技术也被称为组抓取(fetch groups)。请注意,该技术更多的属于市场特性。在实际应用中,优化行读取比优化列读取更重要。但是,仅载入类的部分属性在某些特定情况下会有用,例如在原有表中拥有几百列数据、数据模型无法改动的情况下。 可以在映射文件中对特定的属性设置lazy ,定义该属性为延迟载入。 属性的延迟载入要求在其代码构建时加入二进制指示指令(bytecode instrumentation),如果你的持久类代码中未含有这些指令, Hibernate将会忽略这些属性的延迟设置,仍然将其直接载入。 你可以在Ant的Task中,进行如下定义,对持久类代码加入“二进制指令。” 还有一种可以优化的方法,它使用HQL或条件查询的投影(projection)特性,可以避免读取非必要的列, 这一点至少对只读事务是非常有用的。它无需在代码构建时“二进制指令”处理,因此是一个更加值得选择的解决方法。 有时你需要在HQL中通过抓取所有属性 ,强行抓取所有内容。 20.2. 二级缓存(The Second Level Cache) Hibernate的Session 在事务级别进行持久化数据的缓存操作。 当然,也有可能分别为每个类(或集合),配置集群、或JVM级别(SessionFactory级别 )的缓存。 你甚至可以为之插入一个集群的缓存。注意,缓存永远不知道其他应用程序对持久化仓库(数据库)可能进行的修改 (即使可以将缓存数据设定为定期失效)。 默认情况下,Hibernate使用EHCache进行JVM级别的缓存(目前,Hibernate已经废弃了对JCS的支持,未来版本中将会去掉它)。 你可以通过设置Hibernate.cache.provider_class 属性,指定其他的缓存策略, 该缓存策略必须实现org.hibernate.cache.CacheProvider 接口。 表20.1. 缓存策略提供商(Cache Providers) CacheProvider classTypeCluster SafeQuery Cache SupportedHashtable (not intended for proction use)org.hibernate.cache.HashtableCacheProvider memory yesEHCacheorg.hibernate.cache.EhCacheProvider memory, disk yesOSCacheorg.hibernate.cache.OSCacheProvider memory, disk yesSwarmCacheorg.hibernate.cache.SwarmCacheProvider clustered (ip multicast)yes (clustered invalidation) JBoss TreeCacheorg.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactionalyes (replication)yes (clock sync req.) 20.2.1. 缓存映射(Cache mappings) 类或者集合映射的“ 元素”可以有下列形式: (1) usage 说明了缓存的策略: transactional 、 read-write 、 nonstrict-read-write 或 read-only 。 另外(首选?), 你可以在hibernate.cfg.xml中指定 和 元素。 这里的usage 属性指明了缓存并发策略(cache concurrency strategy)。 20.2.2. 策略:只读缓存(Strategy: read only) 如果你的应用程序只需读取一个持久化类的实例,而无需对其修改, 那么就可以对其进行只读 缓存。这是最简单,也是实用性最好的方法。甚至在集群中,它也能完美地运作。 ....20.2.3. 策略:读/写缓存(Strategy: read/write) 如果应用程序需要更新数据,那么使用读/写缓存 比较合适。 如果应用程序要求“序列化事务”的隔离级别(serializable transaction isolation level),那么就决不能使用这种缓存策略。 如果在JTA环境中使用缓存,你必须指定Hibernate.transaction.manager_lookup_class 属性的值, 通过它,Hibernate才能知道该应用程序中JTA的TransactionManager 的具体策略。 在其它环境中,你必须保证在Session.close() 、或Session.disconnect() 调用前, 整个事务已经结束。 如果你想在集群环境中使用此策略,你必须保证底层的缓存实现支持锁定(locking)。Hibernate内置的缓存策略并不支持锁定功能。 .... .... 20.2.4. 策略:非严格读/写缓存(Strategy: nonstrict read/write) 如果应用程序只偶尔需要更新数据(也就是说,两个事务同时更新同一记录的情况很不常见),也不需要十分严格的事务隔离, 那么比较适合使用非严格读/写缓存 策略。如果在JTA环境中使用该策略, 你必须为其指定Hibernate.transaction.manager_lookup_class 属性的值, 在其它环境中,你必须保证在Session.close() 、或Session.disconnect() 调用前, 整个事务已经结束。 20.2.5. 策略:事务缓存(transactional) Hibernate的事务缓存 策略提供了全事务的缓存支持, 例如对JBoss TreeCache的支持。这样的缓存只能用于JTA环境中,你必须指定 为其Hibernate.transaction.manager_lookup_class 属性。 没有一种缓存提供商能够支持上列的所有缓存并发策略。下表中列出了各种提供器、及其各自适用的并发策略。 表20.2. 各种缓存提供商对缓存并发策略的支持情况(Cache Concurrency Strategy Support) Cacheread-onlynonstrict-read-writeread-writetransactionalHashtable (not intended for proction use)yesyesyes EHCacheyesyesyes OSCacheyesyesyes SwarmCacheyesyes JBoss TreeCacheyes yes 20.3. 管理缓存(Managing the caches) 无论何时,当你给save() 、update() 或 saveOrUpdate() 方法传递一个对象时,或使用load() 、 get() 、list() 、iterate() 或scroll() 方法获得一个对象时, 该对象都将被加入到Session 的内部缓存中。 当随后flush()方法被调用时,对象的状态会和数据库取得同步。 如果你不希望此同步操作发生,或者你正处理大量对象、需要对有效管理内存时,你可以调用evict() 方法,从一级缓存中去掉这些对象及其集合。 ScrollableResult cats = sess.createQuery("from Cat as cat").scroll(); //a huge result setwhile ( cats.next() ) { Cat cat = (Cat) cats.get(0); doSomethingWithACat(cat); sess.evict(cat);}Session还提供了一个contains() 方法,用来判断某个实例是否处于当前session的缓存中。 如若要把所有的对象从session缓存中彻底清除,则需要调用Session.clear() 。 对于二级缓存来说,在SessionFactory 中定义了许多方法, 清除缓存中实例、整个类、集合实例或者整个集合。 sessionFactory.evict(Cat.class, catId); //evict a particular CatsessionFactory.evict(Cat.class); //evict all CatssessionFactory.evictCollection("Cat.kittens", catId); //evict a particular collection of kittenssessionFactory.evictCollection("Cat.kittens"); //evict all kitten collectionsCacheMode 参数用于控制具体的Session如何与二级缓存进行交互。 CacheMode.NORMAL - 从二级缓存中读、写数据。 CacheMode.GET - 从二级缓存中读取数据,仅在数据更新时对二级缓存写数据。 CacheMode.PUT - 仅向二级缓存写数据,但不从二级缓存中读数据。 CacheMode.REFRESH - 仅向二级缓存写数据,但不从二级缓存中读数据。通过 Hibernate.cache.use_minimal_puts 的设置,强制二级缓存从数据库中读取数据,刷新缓存内容。 如若需要查看二级缓存或查询缓存区域的内容,你可以使用统计(Statistics) API。 Map cacheEntries = sessionFactory.getStatistics() .getSecondLevelCacheStatistics(regionName) .getEntries();此时,你必须手工打开统计选项。可选的,你可以让Hibernate更人工可读的方式维护缓存内容。 Hibernate.generate_statistics truehibernate.cache.use_structured_entries true20.4. 查询缓存(The Query Cache) 查询的结果集也可以被缓存。只有当经常使用同样的参数进行查询时,这才会有些用处。 要使用查询缓存,首先你必须打开它: Hibernate.cache.use_query_cache true该设置将会创建两个缓存区域 - 一个用于保存查询结果集(org.hibernate.cache.StandardQueryCache ); 另一个则用于保存最近查询的一系列表的时间戳(org.hibernate.cache.UpdateTimestampsCache )。 请注意:在查询缓存中,它并不缓存结果集中所包含的实体的确切状态;它只缓存这些实体的标识符属性的值、以及各值类型的结果。 所以查询缓存通常会和二级缓存一起使用。 绝大多数的查询并不能从查询缓存中受益,所以Hibernate默认是不进行查询缓存的。如若需要进行缓存,请调用 Query.setCacheable(true) 方法。这个调用会让查询在执行过程中时先从缓存中查找结果, 并将自己的结果集放到缓存中去。 如果你要对查询缓存的失效政策进行精确的控制,你必须调用Query.setCacheRegion() 方法, 为每个查询指定其命名的缓存区域。 List blogs = sess.createQuery("from Blog blog where blog.blogger = :blogger") .setEntity("blogger", blogger) .setMaxResults(15) .setCacheable(true) .setCacheRegion("frontpages") .list();如果查询需要强行刷新其查询缓存区域,那么你应该调用Query.setCacheMode(CacheMode.REFRESH) 方法。 这对在其他进程中修改底层数据(例如,不通过Hibernate修改数据),或对那些需要选择性更新特定查询结果集的情况特别有用。 这是对SessionFactory.evictQueries() 的更为有效的替代方案,同样可以清除查询缓存区域。 20.5. 理解集合性能(Understanding Collection performance) 前面我们已经对集合进行了足够的讨论。本段中,我们将着重讲述集合在运行时的事宜。 20.5.1. 分类(Taxonomy) Hibernate定义了三种基本类型的集合: 值数据集合 一对多关联 多对多关联 这个分类是区分了不同的表和外键关系类型,但是它没有告诉我们关系模型的所有内容。 要完全理解他们的关系结构和性能特点,我们必须同时考虑“用于Hibernate更新或删除集合行数据的主键的结构”。 因此得到了如下的分类: 有序集合类 集合(sets) 包(bags) 所有的有序集合类(maps, lists, arrays)都拥有一个由 和 组成的主键。 这种情况下集合类的更新是非常高效的――主键已经被有效的索引,因此当Hibernate试图更新或删除一行时,可以迅速找到该行数据。 集合(sets)的主键由 和其他元素字段构成。 对于有些元素类型来说,这很低效,特别是组合元素或者大文本、大二进制字段; 数据库可能无法有效的对复杂的主键进行索引。 另一方面,对于一对多、多对多关联,特别是合成的标识符来说,集合也可以达到同样的高效性能。( 附注:如果你希望SchemaExport 为你的 创建主键, 你必须把所有的字段都声明为not-null="true" 。) 映射定义了代理键,因此它总是可以很高效的被更新。事实上, 拥有着最好的性能表现。 Bag是最差的。因为bag允许重复的元素值,也没有索引字段,因此不可能定义主键。 Hibernate无法判断出重复的行。当这种集合被更改时,Hibernate将会先完整地移除 (通过一个(in a single DELETE ))整个集合,然后再重新创建整个集合。 因此Bag是非常低效的。 请注意:对于一对多关联来说,“主键”很可能并不是数据库表的物理主键。 但就算在此情况下,上面的分类仍然是有用的。(它仍然反映了Hibernate在集合的各数据行中是如何进行“定位”的。) 20.5.2. Lists, maps 和sets用于更新效率最高 根据我们上面的讨论,显然有序集合类型和大多数set都可以在增加、删除、修改元素中拥有最好的性能。 可论证的是对于多对多关联、值数据集合而言,有序集合类比集合(set)有一个好处。因为Set 的内在结构, 如果“改变”了一个元素,Hibernate并不会更新(UPDATE) 这一行。 对于Set 来说,只有在插入(INSERT) 和删除(DELETE) 操作时“改变”才有效。再次强调:这段讨论对“一对多关联”并不适用。 注意到数组无法延迟载入,我们可以得出结论,list, map和idbags是最高效的(非反向)集合类型,set则紧随其后。 在Hibernate中,set应该时最通用的集合类型,这时因为“set”的语义在关系模型中是最自然的。 但是,在设计良好的Hibernate领域模型中,我们通常可以看到更多的集合事实上是带有inverse="true" 的一对多的关联。对于这些关联,更新操作将会在多对一的这一端进行处理。因此对于此类情况,无需考虑其集合的更新性能。 20.5.3. Bag和list是反向集合类中效率最高的 在把bag扔进水沟之前,你必须了解,在一种情况下,bag的性能(包括list)要比set高得多: 对于指明了inverse="true" 的集合类(比如说,标准的双向的一对多关联), 我们可以在未初始化(fetch)包元素的情况下直接向bag或list添加新元素! 这是因为Collection.add() )或者Collection.addAll() 方法 对bag或者List总是返回true(这点与与Set不同)。因此对于下面的相同代码来说,速度会快得多。 Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();20.5.4. 一次性删除(One shot delete) 偶尔的,逐个删除集合类中的元素是相当低效的。Hibernate并没那么笨, 如果你想要把整个集合都删除(比如说调用list.clear()),Hibernate只需要一个DELETE就搞定了。 假设我们在一个长度为20的集合类中新增加了一个元素,然后再删除两个。 Hibernate会安排一条INSERT 语句和两条DELETE 语句(除非集合类是一个bag)。 这当然是显而易见的。 但是,假设我们删除了18个数据,只剩下2个,然后新增3个。则有两种处理方式: 逐一的删除这18个数据,再新增三个; 删除整个集合类(只用一句DELETE语句),然后增加5个数据。 Hibernate还没那么聪明,知道第二种选择可能会比较快。 (也许让Hibernate不这么聪明也是好事,否则可能会引发意外的“数据库触发器”之类的问题。) 幸运的是,你可以强制使用第二种策略。你需要取消原来的整个集合类(解除其引用), 然后再返回一个新的实例化的集合类,只包含需要的元素。有些时候这是非常有用的。 显然,一次性删除并不适用于被映射为inverse="true" 的集合。 20.6. 监测性能(Monitoring performance) 没有监测和性能参数而进行优化是毫无意义的。Hibernate为其内部操作提供了一系列的示意图,因此可以从 每个SessionFactory 抓取其统计数据。 20.6.1. 监测SessionFactory 你可以有两种方式访问SessionFactory 的数据记录,第一种就是自己直接调用 sessionFactory.getStatistics() 方法读取、显示统计 数据。 此外,如果你打开StatisticsService MBean选项,那么Hibernate则可以使用JMX技术 发布其数据记录。你可以让应用中所有的SessionFactory 同时共享一个MBean,也可以每个 SessionFactory分配一个MBean。下面的代码即是其演示代码: // MBean service registration for a specific SessionFactoryHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "myFinancialApp");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationstats.setSessionFactory(sessionFactory); // Bind the stats to a SessionFactoryserver.registerMBean(stats, on); // Register the Mbean on the server// MBean service registration for all SessionFactory'sHashtable tb = new Hashtable();tb.put("type", "statistics");tb.put("sessionFactory", "all");ObjectName on = new ObjectName("hibernate", tb); // MBean object nameStatisticsService stats = new StatisticsService(); // MBean implementationserver.registerMBean(stats, on); // Register the MBean on the serverTODO:仍需要说明的是:在第一个例子中,我们直接得到和使用MBean;而在第二个例子中,在使用MBean之前 我们则需要给出SessionFactory的JNDI名,使用hibernateStatsBean.setSessionFactoryJNDIName("my/JNDI/Name") 得到SessionFactory,然后将MBean保存于其中。 你可以通过以下方法打开或关闭SessionFactory 的监测功能: 在配置期间,将Hibernate.generate_statistics 设置为true 或false ; 在运行期间,则可以可以通过sf.getStatistics().setStatisticsEnabled(true) 或hibernateStatsBean.setStatisticsEnabled(true) 你也可以在程序中调用clear() 方法重置统计数据,调用logSummary() 在日志中记录(info级别)其总结。 20.6.2. 数据记录(Metrics) Hibernate提供了一系列数据记录,其记录的内容包括从最基本的信息到与具体场景的特殊信息。所有的测量值都可以由 Statistics 接口进行访问,主要分为三类: 使用Session 的普通数据记录,例如打开的Session的个数、取得的JDBC的连接数等; 实体、集合、查询、缓存等内容的统一数据记录 和具体实体、集合、查询、缓存相关的详细数据记录 例如:你可以检查缓存的命中成功次数,缓存的命中失败次数,实体、集合和查询的使用概率,查询的平均时间等。请注意 java中时间的近似精度是毫秒。
2. hibernate 查询语句好优化吗
hibernate对单表的控制能力比较优秀,但是对于比较复杂的再加上分页的动态查询,仅仅是查询,没有问题。hibernate的分页也已经针对不同的数据库进行了优化的
比如oracle,hibernate就采用的是oracle最快的分页方式,具体的可以去看OracleDialect源代码
其他的数据库也是
对于特别庞大的数据,hibernate的get和load方法也是不管有多少全查的,对大数据,get或者load是获取一条数据,这里都需要用延迟加载的
批量sql的时候,需要设置batch size,并且关闭二级缓存,同时使用flush来同步数据库,在使用clear来清空session缓存,这样不至于内存溢出,hibernte文档上有这个例子
或者用存储过程,如果你了解hibernate你就会用他的长处,而避免其短处
有很多人认为Hibernate天生效率比较低,确实,在普遍情况下,需要将执行转换为SQL语句的
Hibernate的效率低于直接JDBC存取,然 而,在经过比较好的性能优化之后,Hibernate的性能还是让人相当满意的,特别是应用二级缓存之后,甚至可以获得比较不使用缓存的JDBC更好的性能
通常的Hibernate的优化策略:
1.抓取优化
抓取是指Hibernate如何在关联关系之间进行导航的时候,Hibernate如何获取关联对象的策略,其主要定义了两个方面:如何抓取和何时抓取
1)如何抓取。
Hibernate3主要有两种种抓取方式,分.应用于对象关联实例(many-to-one、one-to-one)和对象关联集合
(set、map等),总共是四种变种
JOIN抓取: 通过在SELECT语句中使用OUTER JOIN来获得对象的关联实例或者关联集合)
SELECT抓取: 另外发送一条SELECT语句来抓取当前对象的关联实体和集合
在我的开发经历中,此处对性能的优化是比较有限的,并不值得过多关注
2)何时抓取
主要分为延迟加载和立即抓取,默认的情况下Hibernate3对对象关联实采用延迟加载,普通属性采用立即抓取,通过延迟加载和采用适当的抓取粒度,与不采用优化相比往往可以将性能提升数倍
立即抓取:当抓取宿主对象时,同时抓取其关联对象和关联集以及属性
延迟加载:当抓取宿主对象时,并不抓取其关联对象,而是当对其对象进行调用时才加载
对于延迟加载,需要注意的时,对延迟对象的使用必须在Session关闭之前进行,Hibernate的
LazyInitalizationException往往就是由于在Session的生命期外使用了延迟加载的对象。当我们进行Web开发时,可以使用 OpenSessionInView模式,当请求开始时打开session,当请求响应结束时才关闭session,不过,在使用 OpenSessionInView模式时,需要注意如果响应时间比较长(业务比较复杂或者客户端是低速网络),将Session资源(也就是数据库的连 接)占用太久的话可以会导致资源耗尽
3)抓取粒度
抓取粒度指的是对象在关联关系之间被导航时一次预先加载的数量,Hibernate程序的性能比较差往往就在于没有对抓取粒度仔细考虑,当加载一个列表并在列表中的每个对象中对其关联进行导航时,往往导致N+1条SQL语句查询。
对该值并没有一个约定俗成的值,根据情况而定,如果被关联表数据比较少,则可以设置地小一
些,3-20,如果比较大则可以设到30-50,注意的时候,并不是越多越好,当其值超过50之后,对性能并没有多大改善但却无谓地消耗内存
2.二级缓存
Hibernate对数据的缓存包括两个级:一级缓存,在Session的级别上进行,主要是对象缓存,以其id为键保存对象,在Session 的生命期间存在;二级缓存,在SessionFactory的级别上进行,有对象缓存和查询缓存,查询缓存以查询条件为键保存查询结果,在 SessionFactory的生命期间存在。默认地,Hibernate只启用一级缓存,通过正确地使用二级缓存,往往可以获得意想不到的性能。
1)对象缓存:
当抓取一个对象之后,Hiberate将其以id为键缓存起来,当下次碰到抓取id相同的对象时,可以使用如下配置
方法1:在缓存对象上配置
<class ...>
<cache useage="read-only/write/...." regions="group" />
</class>
useage表示使用什么类型的缓存,譬如只读缓存、读写缓存等等(具体参见Hibernate参考指南),值得注意的时,有部分缓存在 Hibernate的实现中不支持读写缓存,譬如JBossCache在Hibernate的实现中只是一种只读缓存,具体缓存实现对缓存类型的支持情 况,可以参见org.hibernate.cache包 regions
表示缓存分块,大部分的缓存实现往往对缓存进行分块,该部分是可选的,详细参见各缓存实现
方法2:在hibernate.cfg.xml中配置
<cache class=".." useage=".." regions=".."/>
我认为第二种更好,可以统一管理
2)查询缓存
查询时候将查询结果以查询条件为键保存起来,需要配置如下
A.在hibernate.cfg.xml中配置(启用查询缓存)
<property name="hibernate.cache.use_query_cache">true</property> (前面的属性名可参见常量
org.hibernate.cfg.Enviroment.USE_QUERY_CACHE)
B.程序
query.setCacheable(true);
query.setCacheRegions(...);
需要注意的是,查询缓存与对象缓存要结合更有效,因为查询缓存仅缓存查询结果列表的主键数据一般情况下在开发中,对一些比较稳定而又被频繁引用的数据,譬如数据字典之类的,将其进行二级缓存,对一些查询条件和查询数据变化不频繁而又常常 被使用的查询,将其进行二级缓存。由于二级缓存是放在内存中,而且Hibernate的缓存不是弱引用缓存(WeekReference),所以注意不要 将大块的数据放入其中,否则可能会被内存造成比较大的压力。
3.批量数据操作
当进行大批量数据操作(几万甚至几十几百万)时,需要注意两点,一,批量提交,二,及时清除不需要的一级缓存数据
1)所谓的批量提交,就是不要频繁使用session的flush,每一次进行flush,Hibernate将PO数据于数据库进行同步,对于 海量级数据操作来说是性能灾难(同时提交几千条数据和提交一条数据flush一次性能差别可能会是几十倍的差异)。一般将数据操作放在事务中,当事务提交 时Hibernate自动帮你进行flush操作。
2)及时清除不需要的一级缓存数据:由于Hibernate默认采用一级缓存,而在session的生命期间,所有数据抓取之后会放入一级缓存中,而当数据规模比较庞大时,抓取到内存中的数据会让内存压力非常大,一般分批操作数据,被一次操作之后将一级缓存清除,譬如 session.clear(User.class)
4.杂项
dynamic-insert,dynamic-update,动态插入和动态更新,指的是让Hibernate插入数据时仅插入非空数据,当修改数据时只修改变化的数据,譬如对于
class User
{
id
username
password
}
如果u.id=1, u.username="ayufox",u.password=null,那么如果不设置动态插入,则其sql语句是insert
into users(id, username, password) values (1, 'ayufox', '),如果设置则其sql语句是insert into users(username) valeus('ayufox')
在如上的情况下,如果修改u.password='11',那么如果不设置动态更新,则sql语句为
update users set username='ayufox', password='11' where id = 1,如果设置则为
update user set password='11' where d = 1
设置是在class的映射文件中,如下
<class name="User" table="users" dynamic=insert="true/false" dynamic-update="true/false" ...>
</class>
该设置对性能的提升比较有限
3. 如何优化hibernate
是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数,一般设置为30、50、100。Oracle数据库的JDBC驱动默认的Fetch
Size=15,设置Fetch
Size设置为:30、50,性能会有明显提升,如果继续增大,超出100,性能提升不明显,反而会消耗内存。
即在hibernate配制文件中进行配制:
1 <property name="hibernateProperties">
2 <props>3 <propkey="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
4 <prop key="hibernate.show_sql">false</prop>
5 <!-- Create/update the database tables automatically when the JVMstarts up
6 <prop key="hibernate.hbm2ddl.auto">update</prop> -->
7 <!-- Turn batching off for better error messages underPostgreSQL
8 <prop key="hibernate.jdbc.batch_size">100</prop> -->
9 <prop key="hibernate.jdbc.batch_size">50</prop>
10 </props>
11 </property>Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch
Size越小,读数据库的次数越多,速度越慢。
2、如果是超大的系统,建议生成htm文件。加快页面提升速度。
3、不要把所有的责任推在hibernate上,对代码进行重构,减少对数据库的操作,尽量避免在数据库查询时使用in操作,以及避免递归查询操作,代码质量、系统设计的合理性决定系统性能的高低。
4、 对大数据量查询时,慎用list()或者iterator()返回查询结果,(1).
使用List()返回结果时,Hibernate会所有查询结果初始化为持久化对象,结果集较大时,会占用很多的处理时间。(2).
而使用iterator()返回结果时,在每次调用iterator.next()返回对象并使用对象时,Hibernate才调用查询将对应的对象初始化,对于大数据量时,每调用一次查询都会花费较多的时间。当结果集较大,但是含有较大量相同的数据,或者结果集不是全部都会使用时,使用iterator()才有优势。
5、在一对多、多对一的关系中,使用延迟加载机制,会使不少的对象在使用时方会初始化,这样可使得节省内存空间以及减少数据库的负荷,而且若PO中的集合没有被使用时,就可减少互数据库的交互从而减少处理时间。
6、对含有关联的PO(持久化对象)时,若default-cascade="all"或者
“save-update”,新增PO时,请注意对PO中的集合的赋值操作,因为有可能使得多执行一次update操作。
7、对于大数据量新增、修改、删除操作或者是对大数据量的查询,与数据库的交互次数是决定处理时间的最重要因素,减少交互的次数是提升效率的最好途径,所以在开发过程中,请将show_sql设置为true,深入了解Hibernate的处理过程,尝试不同的方式,可以使得效率提升。尽可能对每个页面的显示,对数据库的操作减少到100----150条以内。越少越好。
以上是在进行struts+hibernate+spring进行项目开发中,对hibernate性能优化的几点心得。
4. 如何优化hibernate
① 制定合理的缓存策略(二级缓存、查询缓存)。
② 采用合理的Session管理机制。
③ 尽量使用延迟加载特性。
④ 设定合理的批处理参数。
⑤ 如果可以,选用UUID作为主键生成器。
⑥ 如果可以,选用基于版本号的乐观锁替代悲观锁。
⑦ 在开发过程中, 开启hibernate.show_sql选项查看生成的SQL,从而了解底层的状况;开发完成后关闭此选项。
⑧ 考虑数据库本身的优化,合理的索引、恰当的数据分区策略等都会对持久层的性能带来可观的提升,但这些需要专业的DBA(数据库管理员)提供支持。
Hibernate的悲观锁和乐观锁机制。
有些业务逻辑在执行过程中要求对数据进行排他性的访问,于是需要通过一些机制保证在此过程中数据被锁住不会被外界修改,这就是所谓的锁机制。
Hibernate支持悲观锁和乐观锁两种锁机制。悲观锁,顾名思义悲观的认为在数据处理过程中极有可能存在修改数据的并发事务(包括本系统的其他事务或来自外部系统的事务),于是将处理的数据设置为锁定状态。悲观锁必须依赖数据库本身的锁机制才能真正保证数据访问的排他性,关于数据库的锁机制和事务隔离级别在《Java面试题大全(上)》中已经讨论过了。乐观锁,顾名思义,对并发事务持乐观态度(认为对数据的并发操作不会经常性的发生),通过更加宽松的锁机制来解决由于悲观锁排他性的数据访问对系统性能造成的严重影响。最常见的乐观锁是通过数据版本标识来实现的,读取数据时获得数据的版本号,更新数据时将此版本号加1,然后和数据库表对应记录的当前版本号进行比较,如果提交的数据版本号大于数据库中此记录的当前版本号则更新数据,否则认为是过期数据无法更新。Hibernate中通过Session的get()和load()方法从数据库中加载对象时可以通过参数指定使用悲观锁;而乐观锁可以通过给实体类加整型的版本字段再通过XML或@Version注解进行配置。
提示:使用乐观锁会增加了一个版本字段,很明显这需要额外的空间来存储这个版本字段,浪费了空间,但是乐观锁会让系统具有更好的并发性,这是对时间的节省。因此乐观锁也是典型的空间换时间的策略。
Hibernate的延迟加载机制。
延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载。Hibernate使用了虚拟代理机制实现延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方,得到的都是虚拟代理,简单的说返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用getter方法时才会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关闭了。
延迟加载与session关闭的矛盾一般可以这样处理:
①
关闭延迟加载特性。这种方式操作起来比较简单,因为Hibernate的延迟加载特性是可以通过映射文件或者注解进行配置的,但这种解决方案存在明显的缺陷。首先,出现"no
session or session was closed"通常说明系统中已经存在主外键关联,如果去掉延迟加载的话,每次查询的开销都会变得很大。
②
在session关闭之前先获取需要查询的数据,可以使用工具方法Hibernate.isInitialized()判断对象是否被加载,如果没有被加载则可以使用Hibernate.initialize()方法加载对象。
③
使用拦截器或过滤器延长Session的生命周期直到视图获得数据。Spring整合Hibernate提供的OpenSessionInViewFilter和OpenSessionInViewInterceptor就是这种做法。
Hibernate的一级缓存、二级缓存和查询缓存
Hibernate的Session提供了一级缓存的功能,默认总是有效的,当应用程序保存持久化实体、修改持久化实体时,Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中,除非显示调用了Session的flush()方法或通过close()方法关闭Session。通过一级缓存,可以减少程序与数据库的交互,从而提高数据库访问性能。
SessionFactory级别的二级缓存是全局性的,所有的Session可以共享这个二级缓存。不过二级缓存默认是关闭的,需要显示开启并指定需要使用哪种二级缓存实现类(可以使用第三方提供的实现)。一旦开启了二级缓存并设置了需要使用二级缓存的实体类,SessionFactory就会缓存访问过的该实体类的每个对象,除非缓存的数据超出了指定的缓存空间。
一级缓存和二级缓存都是对整个实体进行缓存,不会缓存普通属性,如果希望对普通属性进行缓存,可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们的查询结果作为键值对进行缓存,对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的,需要显示开启。
5. 介绍一下hibernate的工作原理,优点以及如何优化
hibernate,通过对jdbc进行封装,对 java类和 关系数据库进行mapping,实现了对关系数据库的面向对象方式的操作,改变了传统的jdbc + sql操作数据的方式,从而使开发人员可以花更多精力进行对象方面的开发
好处呢,并不只是简化了数据库操作,更有价值的是对不同的数据库提供了统一操作的接口,使应用程序的跨数据库开发成为可能
1.读取并解析配置文件。
2.读取并解析映射信息,创建SessionFactory
3.打开Session
4.创建事物Transaction
5.持久化操作
6.提交事务
7.关闭Session
8.关闭SessionFactory
6. hibernate导入大量数据时怎样处理内存中的数据
hibernate插入大数据
1:把数据量分成几块(如1000条为一块)导入,然后清除缓存(否则容易内存溢出)。
7. hibernate如何避免复杂查询,优化大数据,和批量sql语句求答案
------解决方案--------------------------------------------------------
Hibernate 是一个优秀的 ORM 框架,但不是高效的。
大数据量的查询,以及需要使用本地数据库语言对 SQL 进行逐步调优的应用不适合使用 Hibernate
------解决方案--------------------------------------------------------
仅仅是查询,没有问题。hibernate的分页也已经针对不同的数据库进行了优化的
比如oracle,hibernate就采用的是oracle最快的分页方式,具体的可以去看OracleDialect源代码
其他的数据库也是
get或者load是获取一条数据,这里都需要用延迟加载的
批量sql的时候,需要设置batch size,并且关闭二级缓存,同时使用flush来同步数据库,在使用clear来清空session缓存,这样不至于内存溢出
,hibernte文档上有这个例子
或者用存储过程,如果你了解hibernate你就会用他的长处,而避免其短处
------解决方案--------------------------------------------------------
hibernate的查询缓存key是查询语句,但是key对应的value其实是数据库表的主键
当查询被缓存后,再次调用查询的时候,通过key找到对应的id集合,然后一个一个的去class缓存中去load
class里没有,再去数据库中获取。所以这又涉及到类缓存上了,当用hibernate进行数据库的修改和删除等操作的时候,都会更新一二级缓存,所以查询缓存会获取最新的数据
------解决方案--------------------------------------------------------