- 浏览: 10552 次
- 性别:
- 来自: 成都
文章分类
最新评论
I've written multiple articles here at Javalobby on the Hibernate O/R mapping tool, and they are usually met with a large degree of popularity. Hibernate isn't just a popular kid on the block, however; it is actually a very powerful, consistent, and reliable database mapping tool. Mapping between objects in Java to relational databases has many facets that you must be aware of. Hibernate does a particularly good job of making the process simple to start, and providing the facilities to allow it to scale well and meet exceedingly complex mapping demands.
One of the primary concerns of mappings between a database and your Java application is performance. One of the common concerns of people who haven't spent much time working with Hibernate in particular, is that O/R mapping tools will limit your ability to make performance-enhancing changes to particular queries and retrievals. Today I want to discuss two facets of the Hibernate infrastructure that are implemented to handle certain performance concerns - the second-level cache and the query cache.
The Second Level Cache
The second-level cache is called 'second-level' because there is already a cache operating for you in Hibernate for the duration you have a session open. From the Hibernate documentation:
A Hibernate Session is a transaction-level cache of persistent data. It is possible to configure a cluster or JVM-level (SessionFactory-level) cache on a class-by-class and collection-by-collection basis. You may even plug in a clustered cache. Be careful. Caches are never aware of changes made to the persistent store by another application (though they may be configured to regularly expire cached data).
As implied above, this 'second-level' cache exists as long as the session factory is alive. The second-level cache holds on to the 'data' for all properties and associations (and collections if requested) for individual entities that are marked to be cached.
I'm not here to re-hash the details provided clearly by the Hibernate documentation, so for detailed information about selecting a cache provider and configuring Hibernate to use it, look at Section 20.2 of the Hibernate documentation .
Suffice it to say, the important part is the 'cache' element which you add to your mapping file:
<cache usage="transactional|read-write|nonstrict-read-write|read-only" />
For most readers, what I'm describing here is probably nothing new. Bear with me, I'll get to the goodies in a minute.
The Query Cache
The other cache that is available is the query cache. The query cache effectively holds on to the identifiers for an individual query. As described in the documentation:
Note that the query cache does not cache the state of the actual entities in the result set; it caches only identifier values and results of value type. So the query cache should always be used in conjunction with the second-level cache.
Configuration of the query cache is described in Section 20.4 of the Hibernate Documentation .
Once enabled via the configuration of Hibernate, it is simply a matter of calling setCacheable(true) on your Query or Criteria object.
Now, on to the inner workings.
How The Second-Level Cache Works
One of the keys to understanding how the caches can help you is to understand how they work internally (at least conceputally). The second-level cache is typically the more important to understand, particularly when dealing with large, complex object graphs that may be queried and loaded often. The first thing to realize about the second-level cache is that it doesn't cache instances of the object type being cached; instead it caches the individual values for the properties of that object. So, conceptually, for an object like this:
public class Person {
private Person parent;
private Set<Person> children;
public void setParent(Person p) { parent = p; }
public void setChildren(Set<Person> set) { children = set; }
public Set<Person> getChildren() { return children; }
public Person getParent() { return parent; }
}
...with a mapping like this:
<class name="org.javalobby.tnt.hibernate.Person">
<cache usage="read-write"/>
<id name="id" column="id" type="long">
<generator class="identity"/>
</id>
<property name="firstName" type="string"/>
<property name="middleInitial" type="string"/>
<property name="lastName" type="string"/>
<many-to-one name="parent" column="parent_id" class="Person"/>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Person"/>
</set>
</class>
Hibernate will hold on to records for this class conceptually like this:
*-----------------------------------------*
| Person Data Cache |
|-----------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 ] |
*-----------------------------------------*
So, in this case, Hibernate is holding on to 3 strings, and one serializable identifier for the 'many-to-one' parent relationship. Let me reiterate that Hibernate is *not* holding on to actual instances of the objects. Why is this important? Two reasons. One, Hibernate doesn't have to worry that client code (i.e. your code) will manipulate the objects in a way that will disrupt the cache, and two, the relationships and associations do not become 'stale', and are easy to keep up to date as they are simply identifiers. The cache is not a tree of objects, and can instead just be a conceptual map of arrays. I continually use the word conceptual, because Hibernate does much more behind the scenes with this cache but, unless you plan on implementing your own provider, you don't need to worry about it. Hibernate calls this state that the objects are in 'dehydrated', as it is all the important bits of the object, but in a more controllable form for Hibernate. I'll use the terms 'dehydrate' and 'hydrate' to describe the process of converting between this broken apart data into an object of your domain (hydrating being the process of creating an instance of your domain and populating it with this data, dehydrating being the inverse).
Now, the astute of you may have noticed that I have omitted the 'children' association all-together from the cache. This was intentional. Hibernate adds the granularity to allow you to decide which associations should be cached, and which associations should be re-determined during the hydration of an object of the cached type from the second-level cache. This provides control for when associations can potentially be altered by another class that doesn't explicitly cascade the change to the cached class.
This is a very powerful construct to have. The default setting is to not cache associations; and if you are not aware of this, and simply turn on caching quickly without really reading into how caching works in Hibernate, you will add the overhead of managing the cache without adding much of the benefits. After all, the primary benefit of caching is to have complex associations available without having to do subsequent database selects - as I have mentioned before, n+1 database queries can quickly become a serious performance bottleneck.
In this case we're dealing just with our person class, and we know that the association will be managed properly - so let's update our mapping to reflect that we want the 'children' association to be cached.
<set name="children">
<cache usage="read-write"/>
<key column="parent_id"/>
<one-to-many class="Person"/>
Now, here is an updated version of our person data cache:
*-----------------------------------------------------*
| Person Data Cache |
|-----------------------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null , [ 2 , 3 ] ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 , [] ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 , [] ] |
*-----------------------------------------------------*
Once again let me point out that all we are caching is the ID of the relative parts.
So, if we were to load the person with ID '1' from the database (ignoring any joining for the time being) without the cache, we would have these selects issued:
select * from Person where id=1 ; load the person with id 1
select * from Person where parent_id=1 ; load the children of 1 (will return 2, 3)
select * from Person where parent_id=2 ; load any potential children of 2 (will return none)
select * from Person where parent_id=3 ; load any potential children of 3 (will return none)
*With* the cache (assuming it was fully loaded), a direct load will actually issue NO select statements, because it can simply look up based on an identifier. If, however we didn't have the associations cached, we'd have these SQL statements invoked for us:
select * from Person where parent_id=1 ; load the children of 1 (will return 2, 3)
select * from Person where parent_id=2 ; load any potential children of 2 (will return none)
select * from Person where parent_id=3 ; load any potential children of 3 (will return none)
That's nearly the same number of SQL statements as when we didn't use the cache at all! This is why it's important to cache associations whenever possible.
Let's say that we wanted to lookup entries based on a more complex query than directly by ID, such as by name. In this case, Hibernate must still issue an SQL statement to get the base data-set for the query. So, for instance, this code:
Query query = session.createQuery("from Person as p where p.firstName=?");
query.setString(0, "John");
List l = query.list();
... would invoke a single select (assuming our associations were cached).
select * from Person where firstName='John'
This single select will then return '1', and then the cache will be used for all other lookups as we have everything cached. This mandatory single select is where the query cache comes in.
How the Query Cache Works
The query cache is actually much like the association caching described above; it's really little more than a list of identifiers for a particular type (although again, it is much more complex internally). Let's say we performed a query like this:
Query query = session.createQuery("from Person as p where p.parent.id=? and p.firstName=?");
query.setInt(0, Integer.valueOf(1));
query.setString(1, "Joey");
query.setCacheable(true);
List l = query.list();
The query cache works something like this:
*----------------------------------------------------------------------------------------*
| Query Cache |
|----------------------------------------------------------------------------------------|
| ["from Person as p where p.parent.id=? and p.firstName=?", [ 1 , "Joey"] ] -> [ 2 ] ] |
*----------------------------------------------------------------------------------------*
The combination of the query and the values provided as parameters to that query is used as a key, and the value is the list of identifiers for that query. Note that this becomes more complex from an internal perspective as you begin to consider that a query can have an effect of altering associations to objects returned that query; not to mention the fact that a query may not return whole objects, but may in fact only return scalar values (when you have supplied a select clause for instance). That being said, this is a sound and reliable way to think of the query cache conceptually.
If the second-level cache is enabled (which it should be for objects returned via a query cache), then the object of type Person with an id of 2 will be pulled from the cache (if available in the cache), it will then be hydradated, as well as any associations.
I hope that this quick glance into how Hibernate holds on to values gives you some idea as to how information can be cached by Hibernate, and will give you some insight into how you can manage your mappings so that you can squeeze the maximum performance possible out of your application.
原文:http://www.javalobby.org/java/forums/t48846.html
One of the primary concerns of mappings between a database and your Java application is performance. One of the common concerns of people who haven't spent much time working with Hibernate in particular, is that O/R mapping tools will limit your ability to make performance-enhancing changes to particular queries and retrievals. Today I want to discuss two facets of the Hibernate infrastructure that are implemented to handle certain performance concerns - the second-level cache and the query cache.
The Second Level Cache
The second-level cache is called 'second-level' because there is already a cache operating for you in Hibernate for the duration you have a session open. From the Hibernate documentation:
A Hibernate Session is a transaction-level cache of persistent data. It is possible to configure a cluster or JVM-level (SessionFactory-level) cache on a class-by-class and collection-by-collection basis. You may even plug in a clustered cache. Be careful. Caches are never aware of changes made to the persistent store by another application (though they may be configured to regularly expire cached data).
As implied above, this 'second-level' cache exists as long as the session factory is alive. The second-level cache holds on to the 'data' for all properties and associations (and collections if requested) for individual entities that are marked to be cached.
I'm not here to re-hash the details provided clearly by the Hibernate documentation, so for detailed information about selecting a cache provider and configuring Hibernate to use it, look at Section 20.2 of the Hibernate documentation .
Suffice it to say, the important part is the 'cache' element which you add to your mapping file:
<cache usage="transactional|read-write|nonstrict-read-write|read-only" />
For most readers, what I'm describing here is probably nothing new. Bear with me, I'll get to the goodies in a minute.
The Query Cache
The other cache that is available is the query cache. The query cache effectively holds on to the identifiers for an individual query. As described in the documentation:
Note that the query cache does not cache the state of the actual entities in the result set; it caches only identifier values and results of value type. So the query cache should always be used in conjunction with the second-level cache.
Configuration of the query cache is described in Section 20.4 of the Hibernate Documentation .
Once enabled via the configuration of Hibernate, it is simply a matter of calling setCacheable(true) on your Query or Criteria object.
Now, on to the inner workings.
How The Second-Level Cache Works
One of the keys to understanding how the caches can help you is to understand how they work internally (at least conceputally). The second-level cache is typically the more important to understand, particularly when dealing with large, complex object graphs that may be queried and loaded often. The first thing to realize about the second-level cache is that it doesn't cache instances of the object type being cached; instead it caches the individual values for the properties of that object. So, conceptually, for an object like this:
public class Person {
private Person parent;
private Set<Person> children;
public void setParent(Person p) { parent = p; }
public void setChildren(Set<Person> set) { children = set; }
public Set<Person> getChildren() { return children; }
public Person getParent() { return parent; }
}
...with a mapping like this:
<class name="org.javalobby.tnt.hibernate.Person">
<cache usage="read-write"/>
<id name="id" column="id" type="long">
<generator class="identity"/>
</id>
<property name="firstName" type="string"/>
<property name="middleInitial" type="string"/>
<property name="lastName" type="string"/>
<many-to-one name="parent" column="parent_id" class="Person"/>
<set name="children">
<key column="parent_id"/>
<one-to-many class="Person"/>
</set>
</class>
Hibernate will hold on to records for this class conceptually like this:
*-----------------------------------------*
| Person Data Cache |
|-----------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 ] |
*-----------------------------------------*
So, in this case, Hibernate is holding on to 3 strings, and one serializable identifier for the 'many-to-one' parent relationship. Let me reiterate that Hibernate is *not* holding on to actual instances of the objects. Why is this important? Two reasons. One, Hibernate doesn't have to worry that client code (i.e. your code) will manipulate the objects in a way that will disrupt the cache, and two, the relationships and associations do not become 'stale', and are easy to keep up to date as they are simply identifiers. The cache is not a tree of objects, and can instead just be a conceptual map of arrays. I continually use the word conceptual, because Hibernate does much more behind the scenes with this cache but, unless you plan on implementing your own provider, you don't need to worry about it. Hibernate calls this state that the objects are in 'dehydrated', as it is all the important bits of the object, but in a more controllable form for Hibernate. I'll use the terms 'dehydrate' and 'hydrate' to describe the process of converting between this broken apart data into an object of your domain (hydrating being the process of creating an instance of your domain and populating it with this data, dehydrating being the inverse).
Now, the astute of you may have noticed that I have omitted the 'children' association all-together from the cache. This was intentional. Hibernate adds the granularity to allow you to decide which associations should be cached, and which associations should be re-determined during the hydration of an object of the cached type from the second-level cache. This provides control for when associations can potentially be altered by another class that doesn't explicitly cascade the change to the cached class.
This is a very powerful construct to have. The default setting is to not cache associations; and if you are not aware of this, and simply turn on caching quickly without really reading into how caching works in Hibernate, you will add the overhead of managing the cache without adding much of the benefits. After all, the primary benefit of caching is to have complex associations available without having to do subsequent database selects - as I have mentioned before, n+1 database queries can quickly become a serious performance bottleneck.
In this case we're dealing just with our person class, and we know that the association will be managed properly - so let's update our mapping to reflect that we want the 'children' association to be cached.
<set name="children">
<cache usage="read-write"/>
<key column="parent_id"/>
<one-to-many class="Person"/>
Now, here is an updated version of our person data cache:
*-----------------------------------------------------*
| Person Data Cache |
|-----------------------------------------------------|
| 1 -> [ "John" , "Q" , "Public" , null , [ 2 , 3 ] ] |
| 2 -> [ "Joey" , "D" , "Public" , 1 , [] ] |
| 3 -> [ "Sara" , "N" , "Public" , 1 , [] ] |
*-----------------------------------------------------*
Once again let me point out that all we are caching is the ID of the relative parts.
So, if we were to load the person with ID '1' from the database (ignoring any joining for the time being) without the cache, we would have these selects issued:
select * from Person where id=1 ; load the person with id 1
select * from Person where parent_id=1 ; load the children of 1 (will return 2, 3)
select * from Person where parent_id=2 ; load any potential children of 2 (will return none)
select * from Person where parent_id=3 ; load any potential children of 3 (will return none)
*With* the cache (assuming it was fully loaded), a direct load will actually issue NO select statements, because it can simply look up based on an identifier. If, however we didn't have the associations cached, we'd have these SQL statements invoked for us:
select * from Person where parent_id=1 ; load the children of 1 (will return 2, 3)
select * from Person where parent_id=2 ; load any potential children of 2 (will return none)
select * from Person where parent_id=3 ; load any potential children of 3 (will return none)
That's nearly the same number of SQL statements as when we didn't use the cache at all! This is why it's important to cache associations whenever possible.
Let's say that we wanted to lookup entries based on a more complex query than directly by ID, such as by name. In this case, Hibernate must still issue an SQL statement to get the base data-set for the query. So, for instance, this code:
Query query = session.createQuery("from Person as p where p.firstName=?");
query.setString(0, "John");
List l = query.list();
... would invoke a single select (assuming our associations were cached).
select * from Person where firstName='John'
This single select will then return '1', and then the cache will be used for all other lookups as we have everything cached. This mandatory single select is where the query cache comes in.
How the Query Cache Works
The query cache is actually much like the association caching described above; it's really little more than a list of identifiers for a particular type (although again, it is much more complex internally). Let's say we performed a query like this:
Query query = session.createQuery("from Person as p where p.parent.id=? and p.firstName=?");
query.setInt(0, Integer.valueOf(1));
query.setString(1, "Joey");
query.setCacheable(true);
List l = query.list();
The query cache works something like this:
*----------------------------------------------------------------------------------------*
| Query Cache |
|----------------------------------------------------------------------------------------|
| ["from Person as p where p.parent.id=? and p.firstName=?", [ 1 , "Joey"] ] -> [ 2 ] ] |
*----------------------------------------------------------------------------------------*
The combination of the query and the values provided as parameters to that query is used as a key, and the value is the list of identifiers for that query. Note that this becomes more complex from an internal perspective as you begin to consider that a query can have an effect of altering associations to objects returned that query; not to mention the fact that a query may not return whole objects, but may in fact only return scalar values (when you have supplied a select clause for instance). That being said, this is a sound and reliable way to think of the query cache conceptually.
If the second-level cache is enabled (which it should be for objects returned via a query cache), then the object of type Person with an id of 2 will be pulled from the cache (if available in the cache), it will then be hydradated, as well as any associations.
I hope that this quick glance into how Hibernate holds on to values gives you some idea as to how information can be cached by Hibernate, and will give you some insight into how you can manage your mappings so that you can squeeze the maximum performance possible out of your application.
原文:http://www.javalobby.org/java/forums/t48846.html
相关推荐
一级缓存的工作原理是:用户发起查询请求,SqlSession先去缓存中查找,是否有该数据,如果有,读取;如果没有,从数据库中查询,并将查询到的数据放入一级缓存区域,供下次查找使用。但是,当SqlSession执行commit,...
缓存机制分为一级缓存和二级缓存,两者都有助于减少对物理数据库的直接访问,从而提高应用程序的运行效率。 一级缓存是 Hibernate 内置的,默认开启,与 Session 对象关联。它是一个事务范围的缓存,也就是说,每个...
Redis作为二级缓存是数据库系统优化的一个重要策略,特别是在高并发、大数据量的场景下,可以显著提升应用性能。在Mybatis中,二级缓存是一个跨Mapper共享的区域,用于存储查询结果,避免了重复查询数据库,从而降低...
在这个"hibernate二级缓存实例"中,我们将深入探讨二级缓存的原理、配置以及在实际项目中的应用。 首先,我们需要了解一级缓存和二级缓存的区别。一级缓存是Session级别的,每个Session都有自己的一级缓存,用于...
二级缓存的工作流程如下: 1. 当一个SqlSession执行查询时,若二级缓存中存在所需数据,直接返回。 2. 如果数据不在二级缓存中,MyBatis会继续查询数据库并将结果存入一级缓存。 3. 当SqlSession提交事务时,一级...
**hibernate一级缓存、二级缓存和查询缓存** 在Java的持久化框架Hibernate中,缓存机制是提高应用程序性能的关键要素。缓存能够减少数据库的访问次数,提高数据读取速度,并且在一定程度上降低了系统的负载。本文将...
本篇文章将深入探讨Hibernate的二级缓存机制,以及如何进行一级缓存与二级缓存的同步,同时还会介绍二级缓存的配置文件设置。 一级缓存是Hibernate默认提供的缓存,每个SessionFactory实例都有一个一级缓存。当对象...
本篇将深入探讨Hibernate的一级缓存和二级缓存,以及查询缓存的配置和使用。 ### 一级缓存 一级缓存是Hibernate默认提供的缓存,它是Session级别的,每个Hibernate Session都有一个私有的、本地的一级缓存。当我们...
二级缓存是 Mapper 级别的缓存,可以跨 SqlSession 工作。它将数据存储在一个全局区域,多个 SqlSession 可以共享。二级缓存默认是关闭的,需要在 MyBatis 配置文件或 Mapper 映射文件中开启。开启二级缓存后,相同...
Mybatis-plus基于Redis实现二级缓存过程解析 Mybatis-plus是一款基于Java语言的持久层框架,旨在简化数据库交互操作。然而,在高并发、高性能的应用场景中,数据库的查询操作可能会成为性能瓶颈。为了解决这个问题...
本篇文章将深入探讨Hibernate二级缓存的概念、工作原理以及如何在实际项目中设置和使用。 **一、二级缓存概念** 一级缓存是每个Hibernate Session内部的一个内存区域,用于存储Session期间的操作对象。当Session...
本文将详细探讨如何在Spring集成的Hibernate环境中配置二级缓存,以及其背后的原理和实践。 首先,我们需要了解什么是二级缓存。在Hibernate中,一级缓存是每个Session内部的缓存,它自动管理实体的状态,当一个...
本文将深入探讨Hibernate二级缓存的基本概念、工作原理及如何通过示例源码实现。 ### 1. 二级缓存概述 Hibernate的一级缓存是Session级别的,它自动管理对象的状态,提供瞬时、持久化和脱管状态之间的转换。然而,...
本文将详细介绍如何配置Hibernate中的二级缓存,并探讨其工作原理及应用场景。 #### 二、二级缓存简介 二级缓存是Hibernate提供的用于改善读取性能的一种机制。它位于SessionFactory级别,可以被多个Session共享,...
Hibernate二级缓存是一种提高应用程序性能的技术,它将数据存储在SessionFactory级别的缓存中,使得数据可以在不同的Session之间共享。这与一级缓存(Session级别)不同,一级缓存仅存在于单个Session生命周期内,当...
本文将深入探讨MyBatis二级缓存的工作原理、配置方式以及如何在实际开发中有效利用。 一、MyBatis二级缓存简介 1.1 一级缓存与二级缓存的区别 MyBatis的一级缓存是SqlSession级别的,同一个SqlSession内的多次查询...
在本文中,我们将深入探讨如何在Spring Boot 2.1.4.RELEASE项目中结合JPA(Java Persistence API)和Hibernate实现Redis作为二级缓存。首先,我们需要理解这些技术的基本概念。 Spring Boot 是一个用于简化Spring...
在实际应用中,理解一级缓存和二级缓存的工作机制对软件开发人员至关重要。例如,在编写高并发、高性能的代码时,开发者可以利用缓存友好的数据结构和算法来最大化缓存利用率,减少缓存未命中的情况。此外,工具如...
二级缓存的工作流程如下: 1. 当一个 SqlSession 执行完查询后,将结果存入二级缓存。 2. 如果其他 SqlSession 在同一命名空间下执行相同查询,会首先检查二级缓存,找到则直接返回,无需再次执行 SQL。 3. 更新操作...
标题“Hibernate一级缓存和二级缓存”指的是Hibernate框架中的两种缓存机制,它们是提高数据访问性能的关键要素。一级缓存是Session级别的,而二级缓存是SessionFactory级别的,两者在数据库操作中起到了重要的作用...