`

hibernate二级缓存实战之EhCacheProvider

阅读更多

转自:http://septem.iteye.com/blog/575130   附件为本实例源码

 

通过这篇文章纪录hibernate二级缓存的一些使用经历,利用几个test case,从代码角度说明二级缓存在使用过程中一些需要注意的问题

使用到的Model类有两个,Author, Book, 两者之间为一对多的关系

Java代码 复制代码
  1. @Entity  
  2. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)   
  3. public class Author {   
  4.        
  5.     private Long id;   
  6.     private String name;   
  7.        
  8.     private Set<Book> books = new HashSet<Book>();   
  9.         // getter setter methods omitted   
  10. }  
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Author {
	
	private Long id;
	private String name;
	
	private Set<Book> books = new HashSet<Book>();
        // getter setter methods omitted
}


Java代码 复制代码
  1. @Entity  
  2. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)   
  3. public class Book {   
  4.        
  5.     private Long id;   
  6.     private String title;   
  7.        
  8.     private Author author;   
  9.         // getter setter methods omitted   
  10. }  
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Book {
	
	private Long id;
	private String title;
	
	private Author author;
        // getter setter methods omitted
}



主要的测试类为TestHibernateSecondLevelCache.java

Java代码 复制代码
  1. public class TestHibernateSecondLevelCache {   
  2.        
  3.     protected Logger logger = LoggerFactory.getLogger(getClass());   
  4.        
  5.     private static SessionFactory sessionFactory;   
  6.        
  7.     @BeforeClass  
  8.     public static void setUpSessionFactory(){   
  9.         sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();   
  10.     }   
  11.        
  12.     @After  
  13.     public void clearSecondLevelCache(){   
  14.         logger.info("clear second level cache");   
  15.         sessionFactory.evict(Author.class);   
  16.         sessionFactory.evict(Book.class);   
  17.                 sessionFactory.getStatistics().clear();   
  18.     }   
  19.   
  20.     private Session openSession(){   
  21.         return sessionFactory.openSession();   
  22.     }   
  23.        
  24.     private Statistics getStatistics(){   
  25.         return sessionFactory.getStatistics();   
  26.     }   
  27. }  
public class TestHibernateSecondLevelCache {
	
	protected Logger logger = LoggerFactory.getLogger(getClass());
	
	private static SessionFactory sessionFactory;
	
	@BeforeClass
	public static void setUpSessionFactory(){
		sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
	}
	
	@After
	public void clearSecondLevelCache(){
		logger.info("clear second level cache");
		sessionFactory.evict(Author.class);
		sessionFactory.evict(Book.class);
                sessionFactory.getStatistics().clear();
	}

    private Session openSession(){
		return sessionFactory.openSession();
	}
	
	private Statistics getStatistics(){
		return sessionFactory.getStatistics();
	}
}



方法setUpSessionFactory用于创建Hibernate SessionFactory,因为创建Session Factory是个相对比较耗时的操作,因此加上Junit4的@BeforeClass annotation,表示该Session Factory只会创建一次,被所有的test case共享.而clearSecondLevelCache方法会在每个test case结束时调用,用于清空二级缓存,防止前一个test case的結果影响后一个test case

测试使用的hibernate-core版本为:3.3.2.GA, hibernate-annotations版本为:3.4.0.GA,测试的数据库为hsqldb内存数据库

一. session.get()

先来看一下session.get是否会查找二级缓存

Java代码 复制代码
  1.       
  2.    @Test  
  3. public void testSessionGetCache(){   
  4.     Author author = createAuthor();   
  5.        
  6.     assertGetMissCache(Author.class, author.getId());   
  7.     assertGetHitCache(Author.class, author.getId());   
  8.        
  9.     updateAuthor(author);   
  10.        
  11.     assertGetMissCache(Author.class, author.getId());   
  12. }   
  13.           
  14.    private Author createAuthor(){   
  15.     Session session = openSession();   
  16.     Author author = new Author();   
  17.     author.setName("septem");   
  18.     session.save(author);   
  19.     session.close();   
  20.     return author;   
  21. }   
  22.   
  23. @SuppressWarnings("unchecked")   
  24. private void assertGetMissCache(Class clazz, Serializable id){   
  25.     Statistics stat = getStatistics();   
  26.     long missCount = stat.getSecondLevelCacheMissCount();   
  27.     Session session = openSession();   
  28.     session.get(clazz, id);   
  29.     session.close();   
  30.     assertEquals(missCount + 1, stat.getSecondLevelCacheMissCount());   
  31. }   
  32.   
  33. @SuppressWarnings("unchecked")   
  34. private void assertGetHitCache(Class clazz, Serializable id){   
  35.     Statistics stat = getStatistics();   
  36.     long hitCount = stat.getSecondLevelCacheHitCount();   
  37.     Session session = openSession();   
  38.     session.get(clazz, id);   
  39.     session.close();   
  40.     assertEquals(hitCount + 1, stat.getSecondLevelCacheHitCount());   
  41. }   
  42.   
  43.    private void updateAuthor(Author author){   
  44.     author.setName("new_name");   
  45.     Session session = openSession();   
  46.     session.update(author);   
  47.     session.flush();   
  48.     session.close();   
  49. }  
    
    @Test
	public void testSessionGetCache(){
		Author author = createAuthor();
		
		assertGetMissCache(Author.class, author.getId());
		assertGetHitCache(Author.class, author.getId());
		
		updateAuthor(author);
		
		assertGetMissCache(Author.class, author.getId());
	}
        
    private Author createAuthor(){
		Session session = openSession();
		Author author = new Author();
		author.setName("septem");
		session.save(author);
		session.close();
		return author;
	}
	
	@SuppressWarnings("unchecked")
	private void assertGetMissCache(Class clazz, Serializable id){
		Statistics stat = getStatistics();
		long missCount = stat.getSecondLevelCacheMissCount();
		Session session = openSession();
		session.get(clazz, id);
		session.close();
		assertEquals(missCount + 1, stat.getSecondLevelCacheMissCount());
	}
	
	@SuppressWarnings("unchecked")
	private void assertGetHitCache(Class clazz, Serializable id){
		Statistics stat = getStatistics();
		long hitCount = stat.getSecondLevelCacheHitCount();
		Session session = openSession();
		session.get(clazz, id);
		session.close();
		assertEquals(hitCount + 1, stat.getSecondLevelCacheHitCount());
	}

    private void updateAuthor(Author author){
		author.setName("new_name");
		Session session = openSession();
		session.update(author);
		session.flush();
		session.close();
	}


testSessionGetCache首先通过createAuthor创建一个author对象,然后在assertGetMissCache里面通过author.id使用get方法查出之前创建的author,因为这是每一次调用get方法,所以hibernate从数据库取回author对象,并将它存入二级缓存.测试結果通过hibernate statistics统计信息里的second level cache miss count来判断这次的get查询未命中缓存

接着assertGetHitCache用同一个id通过get方法获取author对象,因为这个id的对象之前已存入二级缓存,所以这次操作命中缓存

最后通过updateAuthor更新之前的author对象,hibernate会自动将该对象从二级缓存中清除,因此第三次调用get方法时没有命中缓存

总结 : session.get方法会先中二级缓存中通过id做为key查找相应的对象,如果不存在,再发送SQL语句到数据库中查询

二. session.load()

第二步试一下session.load方法

Java代码 复制代码
  1. @Test  
  2.     public void testSessionLoadCache(){   
  3.         Author author = createAuthor();   
  4.            
  5.         assertLoadMissCache(Author.class, author.getId());   
  6.         assertLoadHitCache(Author.class, author.getId());   
  7.            
  8.         updateAuthor(author);   
  9.            
  10.         assertLoadMissCache(Author.class, author.getId());   
  11.     }   
  12.   
  13.         @SuppressWarnings("unchecked")   
  14.     private void assertLoadMissCache(Class clazz, Serializable id){   
  15.         Statistics stat = getStatistics();   
  16.         long missCount = stat.getSecondLevelCacheMissCount();   
  17.         Session session = openSession();   
  18.         Author author = (Author) session.load(clazz, id);   
  19.         author.getName();   
  20.         session.close();   
  21.         assertEquals(missCount + 1, stat.getSecondLevelCacheMissCount());   
  22.     }   
  23.        
  24.     @SuppressWarnings("unchecked")   
  25.     private void assertLoadHitCache(Class clazz, Serializable id){   
  26.         Statistics stat = getStatistics();   
  27.         long hitCount = stat.getSecondLevelCacheHitCount();   
  28.         Session session = openSession();   
  29.         session.load(clazz, id);   
  30.         Author author = (Author) session.load(clazz, id);   
  31.         author.getName();   
  32.         session.close();   
  33.         assertEquals(hitCount + 1, stat.getSecondLevelCacheHitCount());   
  34.     }  
@Test
	public void testSessionLoadCache(){
		Author author = createAuthor();
		
		assertLoadMissCache(Author.class, author.getId());
		assertLoadHitCache(Author.class, author.getId());
		
		updateAuthor(author);
		
		assertLoadMissCache(Author.class, author.getId());
	}

        @SuppressWarnings("unchecked")
	private void assertLoadMissCache(Class clazz, Serializable id){
		Statistics stat = getStatistics();
		long missCount = stat.getSecondLevelCacheMissCount();
		Session session = openSession();
		Author author = (Author) session.load(clazz, id);
		author.getName();
		session.close();
		assertEquals(missCount + 1, stat.getSecondLevelCacheMissCount());
	}
	
	@SuppressWarnings("unchecked")
	private void assertLoadHitCache(Class clazz, Serializable id){
		Statistics stat = getStatistics();
		long hitCount = stat.getSecondLevelCacheHitCount();
		Session session = openSession();
		session.load(clazz, id);
		Author author = (Author) session.load(clazz, id);
		author.getName();
		session.close();
		assertEquals(hitCount + 1, stat.getSecondLevelCacheHitCount());
	}



同样的結果,每一次通过id load未命中缓存,第二次通过相同的id调用load方法命中缓存,而更新过author对象后,缓存失效,第三次查询通过数据库获取author

有一点跟get方法不同:

Java代码 复制代码
  1. Author author = (Author) session.load(clazz, id);   
  2.         author.getName();  
Author author = (Author) session.load(clazz, id);
		author.getName();



总结: 调用load方法的时候,hibernate一开始并没有查询二级缓存或是数据库,而是先返回一个代理对象,该对象只包含id,只有显示调用对象的非id属性时,比如author.getName(),hibernate才会去二级缓存查找,如果没命中缓存再去数据库找,数据库还找不到则抛异常.load方法会尽量推迟对象的查找工作,这是它跟get方法最大的区别.

这两者的测试用例如下:

Java代码 复制代码
  1. @Test(expected=ObjectNotFoundException.class)   
  2.     public void testSessionLoadNonexistAuthor(){   
  3.         Session session = openSession();   
  4.         Author author = (Author) session.load(Author.class, -1L);   
  5.                 assertEquals(Long.valueOf(-1), author.getId());   
  6.         author.getName();   
  7.         session.close();   
  8.     }   
  9.        
  10.     @Test  
  11.     public void testSessionGetNonexistAuthor(){   
  12.         Session session = openSession();   
  13.         Author author = (Author) session.get(Author.class, -1L);   
  14.         session.close();   
  15.         assertNull(author);   
  16.     }  
@Test(expected=ObjectNotFoundException.class)
	public void testSessionLoadNonexistAuthor(){
		Session session = openSession();
		Author author = (Author) session.load(Author.class, -1L);
                assertEquals(Long.valueOf(-1), author.getId());
		author.getName();
		session.close();
	}
	
	@Test
	public void testSessionGetNonexistAuthor(){
		Session session = openSession();
		Author author = (Author) session.get(Author.class, -1L);
        session.close();
		assertNull(author);
	}



三. session.createQuery().list()

Java代码 复制代码
  1. @SuppressWarnings("unchecked")   
  2.     @Test  
  3.     public void testSessionList(){   
  4.         Author author = createAuthor();   
  5.         createAuthor();   
  6.            
  7.         Session session = openSession();   
  8.         //hit database to select authors and populate the cache   
  9.         List<Author> authors = session.createQuery("from Author").list();   
  10.         session.close();   
  11.            
  12.         assertEquals(authors.size(), getStatistics().getSecondLevelCachePutCount());   
  13.            
  14.         Session session2 = openSession();   
  15.         //hit database again to select authors   
  16.         session2.createQuery("from Author").list();   
  17.         session2.close();   
  18.            
  19.         assertEquals(authors.size(), getStatistics().getSecondLevelCachePutCount());   
  20.            
  21.         assertGetHitCache(Author.class, author.getId());   
  22.     }  
@SuppressWarnings("unchecked")
	@Test
	public void testSessionList(){
		Author author = createAuthor();
		createAuthor();
		
		Session session = openSession();
		//hit database to select authors and populate the cache
		List<Author> authors = session.createQuery("from Author").list();
		session.close();
		
		assertEquals(authors.size(), getStatistics().getSecondLevelCachePutCount());
		
		Session session2 = openSession();
		//hit database again to select authors
		session2.createQuery("from Author").list();
		session2.close();
		
		assertEquals(authors.size(), getStatistics().getSecondLevelCachePutCount());
		
		assertGetHitCache(Author.class, author.getId());
	}



首先创建2个author对象,使用HQL : "from Author"调用list方法,这时hibernate直接从数据库查询所有的author对象,并没有从缓存中查询,但是通过list方法查出的所有author对象会存入二级缓存,这点通过getStatistics().getSecondLevelCachePutCount()可以看出来

接着再调用list方法一次,因为此时还没找开查询缓存,list方法重新从数据查了一次.因为第一次查询已将所有的author存入缓存,所以再调用get方法时会命中缓存,assertGetHitCache通过

总结: list方法不会从二级缓存中查找,但它从数据库中查找出来的对象会被存入cache


四. session.createQuery().iterate()

Java代码 复制代码
  1. @SuppressWarnings("unchecked")   
  2.     @Test  
  3.     public void testSessionIterate(){   
  4.         Author author = createAuthor();   
  5.         createAuthor();   
  6.            
  7.         int authorCount = 0;   
  8.            
  9.         Session session = openSession();   
  10.                 //hit database to get ids for all author   
  11.         Iterator<Author>  it = session.createQuery("from Author").iterate();   
  12.         while(it.hasNext()){   
  13.             Author a = it.next();   
  14.             a.getName();   
  15.             authorCount++;   
  16.         }   
  17.         session.close();   
  18.         assertEquals(authorCount, getStatistics().getEntityLoadCount());   
  19.         assertGetHitCache(Author.class, author.getId());   
  20.     }  
@SuppressWarnings("unchecked")
	@Test
	public void testSessionIterate(){
		Author author = createAuthor();
		createAuthor();
		
		int authorCount = 0;
		
		Session session = openSession();
                //hit database to get ids for all author
		Iterator<Author>  it = session.createQuery("from Author").iterate();
		while(it.hasNext()){
			Author a = it.next();
			a.getName();
			authorCount++;
		}
		session.close();
		assertEquals(authorCount, getStatistics().getEntityLoadCount());
		assertGetHitCache(Author.class, author.getId());
	}



先创建2个author对象, 通过HQL: "from Author"调用iterate方法,此时hibernate并没有查author对象,而是先从数据库查出所有author的id,控制台会输入类似以下的SQL:

Sql代码 复制代码
  1. select id from author  
select id from author



在对iterator里面遍历的时候,会根据id一个一个地从先中缓存中查找author,没找到再访问数据库


总结: iterate方法使用的是典型的N+1次查询,先从数据库查询出所有对象的ID,再根据ID一个一个地从二级缓存查找,二级缓存找不到再查询数据库


五. association cache

hibernate支持对关联进行缓存,先在Book.java加上books集合的缓存配置

Java代码 复制代码
  1. @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)   
  2.     @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)   
  3.     public Set<Book> getBooks() {   
  4.         return books;   
  5.     }  
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
	@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
	public Set<Book> getBooks() {
		return books;
	}



测试用例如下:

Java代码 复制代码
  1. @Test  
  2.     public void testAssociationCache(){   
  3.         Author author = createAuthorWith3Books();   
  4.         assertGetBooksForAuthorMissCache(author, 1);   
  5.         assertGetBooksForAuthorHitCache(author, 4);   
  6.         updateOneBookForAuthor(author);   
  7.         assertGetBooksForAuthorMissCache(author, 1);   
  8.         addNewBookForAuthor(author);   
  9.         assertGetBooksForAuthorMissCache(author, 1);   
  10.     }   
  11.   
  12.     private Author createAuthorWith3Books(){   
  13.         Session session = openSession();   
  14.            
  15.         Author author = new Author();   
  16.         author.setName("septem");   
  17.            
  18.         Book book1 = new Book();   
  19.         book1.setTitle("book1");   
  20.         book1.setAuthor(author);   
  21.            
  22.         Book book2 = new Book();   
  23.         book2.setTitle("book2");   
  24.         book2.setAuthor(author);   
  25.            
  26.         Book book3 = new Book();   
  27.         book3.setTitle("book3");   
  28.         book3.setAuthor(author);   
  29.            
  30.         author.getBooks().add(book1);   
  31.         author.getBooks().add(book2);   
  32.         author.getBooks().add(book3);   
  33.            
  34.         session.save(book1);   
  35.         session.save(book2);   
  36.         session.save(book3);   
  37.            
  38.         session.close();   
  39.         return author;   
  40.     }   
  41.   
  42.     private void assertGetBooksForAuthorMissCache(Author author, long miss){   
  43.         Session session = openSession();   
  44.         Author a = (Author) session.get(Author.class, author.getId());   
  45.         long missCount = getStatistics().getSecondLevelCacheMissCount();   
  46.         a.getBooks().size();   
  47.         session.close();   
  48.         assertEquals(missCount + miss, getStatistics().getSecondLevelCacheMissCount());   
  49.     }   
  50.        
  51.     private void assertGetBooksForAuthorHitCache(Author author, long hit){   
  52.         Session session = openSession();   
  53.         Author a = (Author) session.get(Author.class, author.getId());   
  54.         long hitCount = getStatistics().getSecondLevelCacheHitCount();   
  55.         a.getBooks().size();   
  56.         session.close();   
  57.         assertEquals(hitCount + hit, getStatistics().getSecondLevelCacheHitCount());   
  58.     }   
  59.        
  60.     private void updateOneBookForAuthor(Author author){   
  61.         Session session = openSession();   
  62.            
  63.         Author a = (Author) session.get(Author.class, author.getId());   
  64.         Book book = (Book) session.get(Book.class, a.getBooks().iterator().next().getId());   
  65.         book.setTitle("new_title");   
  66.         session.flush();   
  67.            
  68.         session.close();   
  69.     }   
  70.        
  71.     private void addNewBookForAuthor(Author author){   
  72.         Session session = openSession();   
  73.            
  74.         Author a = (Author) session.get(Author.class, author.getId());   
  75.         Book book = new Book();   
  76.         book.setTitle("new_book");   
  77.         book.setAuthor(a);   
  78.         a.getBooks().add(book);   
  79.         session.save(book);   
  80.         session.update(a);   
  81.         session.flush();   
  82.         session.close();   
  83.     }  
@Test
	public void testAssociationCache(){
		Author author = createAuthorWith3Books();
		assertGetBooksForAuthorMissCache(author, 1);
		assertGetBooksForAuthorHitCache(author, 4);
		updateOneBookForAuthor(author);
		assertGetBooksForAuthorMissCache(author, 1);
		addNewBookForAuthor(author);
		assertGetBooksForAuthorMissCache(author, 1);
	}

    private Author createAuthorWith3Books(){
		Session session = openSession();
		
		Author author = new Author();
		author.setName("septem");
		
		Book book1 = new Book();
		book1.setTitle("book1");
		book1.setAuthor(author);
		
		Book book2 = new Book();
		book2.setTitle("book2");
		book2.setAuthor(author);
		
		Book book3 = new Book();
		book3.setTitle("book3");
		book3.setAuthor(author);
		
		author.getBooks().add(book1);
		author.getBooks().add(book2);
		author.getBooks().add(book3);
		
		session.save(book1);
		session.save(book2);
		session.save(book3);
		
		session.close();
		return author;
	}

    private void assertGetBooksForAuthorMissCache(Author author, long miss){
		Session session = openSession();
		Author a = (Author) session.get(Author.class, author.getId());
		long missCount = getStatistics().getSecondLevelCacheMissCount();
		a.getBooks().size();
		session.close();
		assertEquals(missCount + miss, getStatistics().getSecondLevelCacheMissCount());
	}
	
	private void assertGetBooksForAuthorHitCache(Author author, long hit){
		Session session = openSession();
		Author a = (Author) session.get(Author.class, author.getId());
		long hitCount = getStatistics().getSecondLevelCacheHitCount();
		a.getBooks().size();
		session.close();
		assertEquals(hitCount + hit, getStatistics().getSecondLevelCacheHitCount());
	}
	
	private void updateOneBookForAuthor(Author author){
		Session session = openSession();
		
		Author a = (Author) session.get(Author.class, author.getId());
		Book book = (Book) session.get(Book.class, a.getBooks().iterator().next().getId());
		book.setTitle("new_title");
		session.flush();
		
		session.close();
	}
	
	private void addNewBookForAuthor(Author author){
		Session session = openSession();
		
		Author a = (Author) session.get(Author.class, author.getId());
		Book book = new Book();
		book.setTitle("new_book");
		book.setAuthor(a);
		a.getBooks().add(book);
		session.save(book);
		session.update(a);
		session.flush();
		session.close();
	}



先创建一个author,为该author添加3个book对象.在assertGetBooksForAuthorMissCache通过author.getBooks访问关联的book集合,因为延迟加载的关系,此时并没有查询缓存也没有查询数据库,在调用a.getBooks().size()也就是访问book集合的元素时,hibernate先中缓存中查找,没有发现关联缓存,重新数据库查询,生成的SQL类似如下:

Sql代码 复制代码
  1. select * from book where author_id = ?  
select * from book where author_id = ?



此时statistics的missCount只增加了1,因为调用author.getBooks没有命中缓存.hibernate从数据库查询出books后,将books关联以及三个book对象都存入二级缓存.

关联的缓存是以什么样的形式存在呢?注意关联缓存没有保存books集合本身,而是保存所有book的id,假设3个book对象的id分别为1, 2, 3,则author缓存的格式类似于如下:

Java代码 复制代码
  1. *---------------------------------*   
  2. |        Author Data Cache        |   
  3. |---------------------------------|   
  4. 1 -> [ "septem" , [ 123 ] ] |   
  5. *---------------------------------*  
*---------------------------------*
|        Author Data Cache        |
|---------------------------------|
| 1 -> [ "septem" , [ 1, 2, 3 ] ] |
*---------------------------------*



第二步执行assertGetBooksForAuthorHitCache(author, 4)的时候,我们看到hitCount增加了4.因为第二次调用author.getBooks的时候,命中了关联缓存,从缓存中取回3个id,又分别用id一个一个地从二级缓存中取回3个book对象,一共命中缓存4次

接着通过updateOneBookForAuthor(author)更新了其中的一个book对象,假设更新的是id为1的book.接着的assertGetBooksForAuthorMissCache(author, 1)方法里面missCount又增加了1.book虽然更新了,但是author.getBooks还是能命中缓存,因为book id列表还是[ 1, 2, 3 ].从缓存中取回book id列表,通过book id查找book的时候,因为id为1的book已经更新过了,它的二级缓存失效了,重新去数据库取,此时missCount增加了1,而id为2,3的book还是从二级缓存中找到的.这个方法hibernate会生成类似如下的SQL:

Sql代码 复制代码
  1. select * from book where id = 1  
select * from book where id = 1



更新其中的一个book对象不会造成关联缓存的失效,但如果更新了集合id列表的话,缓存就会失效.先通过addNewBookForAuthor为author增加一个books对象,此时books集合里面一共有4个book对象,最后的assertGetBooksForAuthorMissCache(author, 1)我们可以看到缓存失效,missCount增加了1.此时同第一次调用author.getBooks一样,hibernate生成类似如下的SQL

Sql代码 复制代码
  1. select * from book where author_id = ?  
select * from book where author_id = ?




总结: 关联缓存保存的是集合的id列表,而不是集合本身,关联命中缓存的时候,会根据id一个一个地先从二级缓存查找,找不到再查询数据库.更新集合中的某个对象不会造成关联缓存失效,只有改变集合的id列表才会造成缓存失效


五. 查询缓存query cache

在hibernate.cfg.xml里面加上以下配置开启查询缓存:

Xml代码 复制代码
  1. <property name="hibernate.cache.use_query_cache">true</property>  
<property name="hibernate.cache.use_query_cache">true</property>


测试用例如下:

Java代码 复制代码
  1. @Test  
  2.     public void testQueryCache(){   
  3.         createAuthor();   
  4.         createAuthor();   
  5.            
  6.         assertQueryMissCache();   
  7.            
  8.         assertQueryHitCache();   
  9.            
  10.         createAuthor();   
  11.         assertQueryMissCache();   
  12.     }  
@Test
	public void testQueryCache(){
		createAuthor();
		createAuthor();
		
		assertQueryMissCache();
		
		assertQueryHitCache();
		
		createAuthor();
		assertQueryMissCache();
	}



先做准备工作,创建两个author对象,假设它们的id分别为1,2.assertQueryMissCache里面第一次调用list方法,注意调用list前必须setCacheable(true)才会使用查询缓存,此时未命中查询缓存,hibernate从数据库查询Author对象,将此次查询存入查询缓存,同时会将查询到的author对象存入二级缓存

查询缓存并不保存查询结果集,而只是保存结果集的id,它的结构类似以下数据:

Java代码 复制代码
  1. *---------------------------------------------------------------*   
  2. |                         Query Cache                           |   
  3. |---------------------------------------------------------------|   
  4. | [ ["from Author where name = ?", [ "septem"] ] -> [  12 ] ] |   
  5. *---------------------------------------------------------------*  
*---------------------------------------------------------------*
|                         Query Cache                           |
|---------------------------------------------------------------|
| [ ["from Author where name = ?", [ "septem"] ] -> [  1, 2 ] ] |
*---------------------------------------------------------------*


注意缓存的key与HQL,参数以及分页参数有关

再调用assertQueryHitCache()用同样的HQL与参数重新查询Author此时会命中查询缓存,并根据结果集id一个一个地查询author对象,因为author对象之前已存入二级缓存,所以这次查询也会命中二级缓存

查询缓存的失效比较特殊,只要查询涉及的任何一张表的数据发生变化,缓存就会失效.比如我们再创建一个Author对象,此时Author表发生了变化,原来的查询缓存就失效了


总结: 查询缓存的key与HQL,查询参数以及分布参数有关,而且一旦查询涉及到的任何一张表的数据发生了变化,缓存就失效了,所以在生产环境中命中率较低.查询缓存保存的是结果集的id列表,而不是结果集本身,命中缓存的时候,会根据id一个一个地先从二级缓存查找,找不到再查询数据库.


涉及到的所有代码保存在google code上

Java代码 复制代码
  1. svn checkout http://hibernate-cache-testcase.googlecode.com/svn/trunk/ hibernate-cache-testcase  
分享到:
评论

相关推荐

    hibernate 二级缓存详解

    配置Ehcache作为二级缓存提供商,需要在Hibernate的配置文件中设置`hibernate.cache.provider_class`为`net.sf.hibernate.cache.EhCacheProvider`。如果启用查询缓存,还需添加`hibernate.cache.use_query_cache=...

    hibernate二级缓存

    在深入探讨Hibernate二级缓存之前,我们首先需要理解Hibernate缓存机制的基本概念。Hibernate作为一款优秀的对象关系映射(ORM)框架,它提供了一种在Java应用与数据库之间进行对象到关系表转换的方式,简化了数据...

    Hibernate二级缓存技术

    ### Hibernate二级缓存技术详解 #### 一、概述 Hibernate 是一个开源的对象关系映射(ORM)框架,它简化了Java应用与关系型数据库之间的交互。为了提高性能和减少数据库的访问频率,Hibernate 提供了一级缓存和二...

    Hibernate二级缓存配置详解

    本文将深入探讨Hibernate的二级缓存,包括其事务范围、进程范围和集群范围的配置,特别是关注进程范围内的EhCacheProvider以及查询缓存的配置。 缓存的基本原理在于减少直接对持久性数据源的读写操作,通过在内存中...

    Hibernate二级缓存攻略

    为了启用Ehcache作为二级缓存提供者,我们需要在Hibernate配置文件中设置`hibernate.cache.provider_class`为`net.sf.hibernate.cache.EhCacheProvider`。同时,如果要开启查询缓存,还需设置`hibernate.cache.use_...

    hibernate4.0使用二级缓存jar包

    ehcache 二级缓存 配置使用的jar包 配置如下: &lt;!-- 启用二级缓存 --&gt; &lt;property name="hibernate.cache.use_second_level_cache"&gt;true &lt;!-- 查询的二级缓存配置 --&gt; &lt;property name="hibernate....

    Hibernatehibernate二级缓存.pdf

    在本文档中,我们探讨了Hibernate框架中的二级缓存机制。二级缓存是Hibernate提供的一种优化数据库访问性能的策略,它允许将数据存储在进程级别的缓存中,以便多个Session能够共享这些数据,从而减少对数据库的直接...

    Hibernate_二级缓存 实验心得,手册

    ### Hibernate 二级缓存实验心得与手册 #### 一、二级缓存概述 在学习Hibernate的过程中,二级缓存是一个非常重要的概念。与一级缓存不同的是,一级缓存默认为每个`Session`开启,用于存储当前`Session`内的实体...

    hibernate配置二三级缓存

    ### Hibernate配置二级与三级缓存详解 在Java开发领域中,Hibernate作为一种流行的ORM(对象关系映射)框架,被广泛应用于数据库操作。为了提高应用程序的性能,Hibernate支持多种级别的缓存机制,其中最为常见的是...

    hibernate对二级缓存的理解

    《深入理解Hibernate二级缓存机制》 在Java的持久化框架Hibernate中,缓存机制扮演着重要的角色,尤其是在处理大量数据时,它可以显著提高应用程序的性能。然而,许多人对Hibernate的二级缓存可能存在误解,因此...

    二级缓存配置

    其中,Hibernate作为一款流行的Java持久层框架,提供了多种级别的缓存支持,包括一级缓存(Session级别)和二级缓存(SessionFactory级别)。本文将详细介绍如何配置Hibernate中的二级缓存,并探讨其工作原理及应用...

    二级缓存详解

    在Hibernate3中,二级缓存是一个重要的特性,它可以帮助提高应用程序性能,减少对数据库的直接访问。以下是关于"二级缓存详解"的详细知识: 二级缓存是指在SessionFactory级别上维护的数据缓存,它不同于一级缓存...

    java中hibernate二级缓存详解

    为了在 Hibernate 中使用二级缓存,需要配置 EhCacheProvider 和指定缓存的类方法。配置步骤如下: 1. 拷贝 ehcache-1.5.0.jar 到当前工程的 lib 目录下,依赖 backport-util-concurrent 和 commons-logging。 2. ...

    Hibernate的缓存应用

    根据不同的应用场景,Hibernate提供了不同级别的缓存支持,包括一级缓存(Session级别的缓存)和二级缓存(SessionFactory级别的缓存)。这两种缓存机制各有侧重,下面将详细介绍。 #### 三、一级缓存:Session级别...

    详解Hibernate注解方式的二级缓存

    其中,二级缓存是Hibernate缓存策略中的一部分,用来提升应用程序处理大量数据时的性能。在Hibernate框架中,使用注解方式来配置和实现二级缓存是常见的做法。接下来将详细讲解Hibernate注解方式的二级缓存及其使用...

    hibernate缓存策略

    Hibernate的缓存机制分为两个层次:**一级缓存**和**二级缓存**。一级缓存是由Hibernate框架自动管理和维护的,它存在于每个Session的生命周期内;而二级缓存则需要开发者手动配置和管理,主要用于在不同Session之间...

Global site tag (gtag.js) - Google Analytics