hibernate 13 优化抓取和高速缓存 | hibernate 实战(第二版) 第13章优化抓取和高速缓存 | 笔记

1定义全局抓取计划 


你的目标是为应用程序中的每个用例找到最好的获取方法方法和抓取策略;同时也要最小化SQL查询次数,以获得最好的性能。
 

HQL常用于对象获取,而不是更新、插入或者删除数据。对象状态同步是持久化管理的工作,而不是开发人员的工作。但是,正如前一章 
中说的,HQL都支持大批量的操作。 



按条件查询(Criteria的用法):
  1. public Item quertItem(String itemName) {  
  2.     Session session = getSessionFactory().openSession();  
  3.       
  4.     Criteria criteria = session.createCriteria(Item.class);  
  5.     criteria.add(Restrictions.like("itemName", itemName));  
  6.       
  7.     Item item = (Item) criteria.uniqueResult();  
  8.       
  9.     return item;  
  10. }  

(criteria是hibernate固有的) 


按示例查询: 
作为Criteria工具的一部分,hibernate支持按示例查询(Query By Example,QBE)
  1. public Item quertItem2(String itemName) {  
  2.     Session session = getSessionFactory().openSession();  
  3.       
  4.     Criteria criteria = session.createCriteria(Item.class);  
  5.       
  6.     Item itemExample = new Item(itemName);  
  7.       
  8.     criteria.add(Example.create(itemExample));  
  9.       
  10.     Item item = (Item) criteria.uniqueResult();  
  11.       
  12.     return item;  
  13. }  

延迟的默认抓取技术: 
Hibernate给所有的实体和集合默认一个延迟的抓取策略。这意味着Hibernate在默认情况下只加载你正在查询的对象。 

load()操作之后,内存中并没有持久化的item对象。甚至加载Item的SQL也没有被执行。Hibernate创建了一个看起来跟真的一样的代理(proxy) 

理解代理: 
代理是在运行时生成的占位符。每当Hibernate返回实体类的实例时,它就检查是否可以返回一个代理来代替,并避免数据库命中。 

JPA中:find()必须命中,getReference(),可能返回一个代理。 

新的延迟策略: 
<set lazy="extra" ...> 
如果调用size()、contains()或者isEmpty(),集合不再被初始化--查询数据库来获取必要的信息。 

禁用代理生成: 
即使是被关联,也会变成立即加载。 
<class lazy="false" ...> 

@org.hibernate.annotations.Proxy(lazy=false) 
public class User{...} 

关联和集合的即时加载: 
(比如当你想从一个脱管状态的实体获取关联对象) 
但是,当你想象一下,当很过千丝万偶的地方都设置了立即加载,那么获取一个对象,会关联获取出很多层关联对象! 
不过你可以尝试使用视图对象,或者JOIN等关联获取方式。 


通过拦截延迟加载: 

Hibernate提供的运行时代理生成,是透明的延迟加载的一种极好的选择。(但是,通过instanceof测试多态的关联不可用,因为代理 
是运行时生成的子类的一个实例。) 
其它持久化工具没有代理的方式,都是使用的拦截。 
通过lazy="no-proxy"告知对这个关联应用拦截。 
(但是没给例子,不知道怎么玩) 


2选择抓取策略  

你的目标是把SQL语句的数量减少到最少,简化SQL语句,以便尽可能地提高查询效率。 
批量预抓(batch fetching)取数据: 
批量抓取经常被称为瞎猜优化(blind-guess optimization) 
<class name="User" batch-size="10" ..> 
也可用于集合 

通过子查询预抓取集合: 
<set fetch="subselect" ..> 
@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.SUBSELECT) 

配置文件:
  1.   <class name="Item" table="ITEM">  
  2.       <id name="itemId" column="ITEM_ID" type="integer">  
  3.           <generator class="native"/>  
  4.       </id>  
  5.       <property name="itemName" type="string" column="ITEM_NAME"/>  
  6.         
  7. <bag name="bids" inverse="true" cascade="save-update" fetch="subselect">  
  8.     <key column="ITEM_ID_M"></key>  
  9.     <one-to-many class="Bid"/>  
  10. </bag>  
  11.           
  12.   </class>  


测试代码:
  1.     private static void select() {  
  2.         Configuration configuration = new Configuration().configure();  
  3.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  4.         Session session = sessionFactory.openSession();  
  5.           
  6.         Item item = (Item) session.get(Item.class1);  
  7.           
  8.         System.out.println("-----");  
  9.           
  10.         Collection<Bid> bids = item.getBids();  
  11.           
  12.         System.out.println("+++++");  
  13.           
  14.         for(Iterator it = bids.iterator();it.hasNext();){  
  15.             Bid bid = (Bid) it.next();  
  16.             System.out.println(bid);  
  17.         }  
  18.           
  19. //      hibernate打印:  
  20. //      Hibernate: select item0_.ITEM_ID as ITEM1_1_0_, item0_.ITEM_NAME as ITEM2_1_0_ from ITEM item0_ where item0_.ITEM_ID=?  
  21. //      -----  
  22. //      +++++  
  23. //      Hibernate: select bids0_.ITEM_ID_M as ITEM3_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.bidMoeny as bidMoeny0_0_, bids0_.ITEM_ID_M as ITEM3_0_0_ from BID bids0_ where bids0_.ITEM_ID_M=?  
  24. //      Bid [bidId=1, bidMoeny=12.0]  
  25. //      Bid [bidId=2, bidMoeny=13.0]  
  26.   
  27. //如果是多条就会变成:  
  28. //Hibernate: select bids0_.ITEM_ID_M as ITEM3_1_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.bidMoeny as bidMoeny0_0_, bids0_.ITEM_ID_M as ITEM3_0_0_ from BID bids0_ where bids0_.ITEM_ID_M in (select item0_.ITEM_ID from ITEM item0_)  
  29.   
  30.           
  31.         session.close();  
  32.           
  33.         sessionFactory.close();  
  34.     }  

通过联结即时抓取: 
(只会查询出来Item存在Bid的数据,因为使用的是 left outer join) 
<set fetch="join" ..> 

@org.hibernate.annotations.Fetch(org.hibernate.annotations.FetchMode.JOIN) 

配置文件:
  1.   <class name="Item" table="ITEM">  
  2.       <id name="itemId" column="ITEM_ID" type="integer">  
  3.           <generator class="native"/>  
  4.       </id>  
  5.       <property name="itemName" type="string" column="ITEM_NAME"/>  
  6.         
  7. <set name="bids" inverse="true" cascade="save-update" fetch="join">  
  8.     <key column="ITEM_ID_M"></key>  
  9.     <one-to-many class="Bid"/>  
  10. </set>  
  11.           
  12.   </class>  

测试代码: 
很奇怪,不解,难道我这个版本的hibernate有bug?把session.close();放在获取之前就会报no session的错误,那么肯定就是采用的懒加载的方式 
  1.     /** 
  2.      * 前提是我Item有三条数据,两条没有Bid 
  3.      */  
  4.     private static void select() {  
  5.         Configuration configuration = new Configuration().configure();  
  6.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  7.         Session session = sessionFactory.openSession();  
  8.           
  9.         Query query = session.createQuery("select o from Item o");  
  10.         List<Item> items = query.list();  
  11.           
  12.         System.out.println(items.size());  
  13.           
  14.         for(int i=0;i<items.size();i++){  
  15.             Item item = items.get(i);  
  16.             System.out.println(item.getBids());  
  17.         }  
  18. //      后台打印:  
  19. //      Hibernate: select item0_.ITEM_ID as ITEM1_1_, item0_.ITEM_NAME as ITEM2_1_ from ITEM item0_  
  20. //      3  
  21. //      Hibernate: select bids0_.ITEM_ID_M as ITEM3_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.bidMoeny as bidMoeny0_0_, bids0_.ITEM_ID_M as ITEM3_0_0_ from BID bids0_ where bids0_.ITEM_ID_M=?  
  22. //      [Bid [bidId=2, bidMoeny=12.0], Bid [bidId=1, bidMoeny=13.0]]  
  23. //      Hibernate: select bids0_.ITEM_ID_M as ITEM3_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.bidMoeny as bidMoeny0_0_, bids0_.ITEM_ID_M as ITEM3_0_0_ from BID bids0_ where bids0_.ITEM_ID_M=?  
  24. //      []  
  25. //      Hibernate: select bids0_.ITEM_ID_M as ITEM3_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.bidMoeny as bidMoeny0_0_, bids0_.ITEM_ID_M as ITEM3_0_0_ from BID bids0_ where bids0_.ITEM_ID_M=?  
  26. //      []  
  27.           
  28.           
  29. //      很奇怪,不解,难道我这个版本的hibernate有bug?把session.close();放在获取之前就会报no session的错误,那么肯定就是采用的懒加载的方式  
  30.   
  31.           
  32.           
  33.         session.close();  
  34.           
  35.         sessionFactory.close();  
  36.     }  
  37.       
换个版本(hibernate-distribution-3.6.4.Final): 
还是这样。 
突然明白过来,他在你指定的HQL中,如何生效呢?呵呵 
  1.     /** 
  2.      * 前提是我Item有三条数据,两条没有Bid 
  3.      */  
  4.     private static void select2() {  
  5.         Configuration configuration = new Configuration().configure();  
  6.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  7.         Session session = sessionFactory.openSession();  
  8.           
  9.         Item item = (Item) session.get(Item.class1);  
  10. //      打印:  
  11. //      Hibernate: select item0_.ITEM_ID as ITEM1_1_1_, item0_.ITEM_NAME as ITEM2_1_1_, bids1_.ITEM_ID_M as ITEM3_1_3_, bids1_.BID_ID as BID1_3_, bids1_.BID_ID as BID1_0_0_, bids1_.bidMoeny as bidMoeny0_0_, bids1_.ITEM_ID_M as ITEM3_0_0_ from ITEM item0_ left outer join BID bids1_ on item0_.ITEM_ID=bids1_.ITEM_ID_M where item0_.ITEM_ID=?  
  12.   
  13.           
  14.         session.close();  
  15.           
  16.         sessionFactory.close();  
  17.     }  
  18.       

JPA:
  1. @Entity  
  2. public class Item implements Serializable {  
  3.     @Id  
  4.     @GeneratedValue  
  5.     @Column(name="ITEM_ID")  
  6.     private Integer itemId;  
  7.     @Column(name="ITEM_NAME")  
  8.     private String itemName;  
  9.       
  10.     @OneToMany(mappedBy="item",cascade=CascadeType.ALL)  
  11.     @Fetch(value=FetchMode.JOIN)  
  12.     private Set<Bid> bids = new HashSet<Bid>();  
  13.   
  14. .....  

调用: 
离职执行了查询集合数据
  1.     private static void select() {  
  2.         EntityManagerFactory factory = Persistence.createEntityManagerFactory("partner4java");  
  3.         EntityManager em = factory.createEntityManager();  
  4.           
  5.         Query query = em.createQuery("select o from Item o ");  
  6.         List<Item> items = query.getResultList();  
  7.           
  8. //      后台打印:  
  9. //      Hibernate: select item0_.ITEM_ID as ITEM1_1_, item0_.ITEM_NAME as ITEM2_1_ from Item item0_  
  10. //      Hibernate: select bids0_.ITEM_ID as ITEM3_1_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.BID_MONEY as BID2_0_0_, bids0_.ITEM_ID as ITEM3_0_0_ from Bid bids0_ where bids0_.ITEM_ID=?  
  11. //      Hibernate: select bids0_.ITEM_ID as ITEM3_1_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.BID_MONEY as BID2_0_0_, bids0_.ITEM_ID as ITEM3_0_0_ from Bid bids0_ where bids0_.ITEM_ID=?  
  12.   
  13.           
  14.         em.close();  
  15.         factory.close();  
  16.     }     
  17.   
  18.       
  19.   
  20.     private static void select2() {  
  21.         EntityManagerFactory factory = Persistence.createEntityManagerFactory("partner4java");  
  22.         EntityManager em = factory.createEntityManager();  
  23.           
  24.         Item item = em.find(Item.class1);  
  25.         System.out.println("----");  
  26.         System.out.println(item.getBids());  
  27.           
  28. //      后台打印:  
  29. //      Hibernate: select item0_.ITEM_ID as ITEM1_0_1_, item0_.ITEM_NAME as ITEM2_0_1_, bids1_.ITEM_ID as ITEM3_0_3_, bids1_.BID_ID as BID1_3_, bids1_.BID_ID as BID1_1_0_, bids1_.BID_MONEY as BID2_1_0_, bids1_.ITEM_ID as ITEM3_1_0_ from Item item0_ left outer join Bid bids1_ on item0_.ITEM_ID=bids1_.ITEM_ID where item0_.ITEM_ID=?  
  30. //      ----  
  31. //      [Bid [bidId=1, bidMoeny=12.0], Bid [bidId=2, bidMoeny=13.0]]  
  32.           
  33.         //但是,我们是三条数据,一条Item没有Bid,所以出问题了  
  34.           
  35.         em.close();  
  36.         factory.close();  
  37.     }  
  38.   
  39.       
上面这样就生效了。看来fetch方式如此不堪。 
总结:  
  1. JPA使用下面代码测试(前提是有三条Item,一条没有Bid数据):  
  2.     private static void select() {  
  3.         EntityManagerFactory factory = Persistence.createEntityManagerFactory("partner4java");  
  4.         EntityManager em = factory.createEntityManager();  
  5.           
  6.         Query query = em.createQuery("select o from Item o ");  
  7.         List<Item> items = query.getResultList();  
  8.           
  9.         em.close();  
  10.         factory.close();  
  11.     }  
  12.       
join: 
1、无论使用hibernate和jpa使用HQL或者QL语句,是不生效的。 
2、区别在于JPA: 
@OneToMany(mappedBy="item",cascade=CascadeType.ALL,fetch=FetchType.LAZY) 
@Fetch(value=FetchMode.JOIN) 
后台打印: 
Hibernate: select item0_.ITEM_ID as ITEM1_0_, item0_.ITEM_NAME as ITEM2_0_ from Item item0_ 
Hibernate: select bids0_.ITEM_ID as ITEM3_0_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.BID_MONEY as BID2_1_0_, bids0_.ITEM_ID as ITEM3_1_0_ from Bid bids0_ where bids0_.ITEM_ID=? 
Hibernate: select bids0_.ITEM_ID as ITEM3_0_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.BID_MONEY as BID2_1_0_, bids0_.ITEM_ID as ITEM3_1_0_ from Bid bids0_ where bids0_.ITEM_ID=? 
Hibernate: select bids0_.ITEM_ID as ITEM3_0_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_1_0_, bids0_.BID_MONEY as BID2_1_0_, bids0_.ITEM_ID as ITEM3_1_0_ from Bid bids0_ where bids0_.ITEM_ID=? 
像这样,无论,JPA中你是否指明了懒加载,只要生命了fetch=join方式,就会变成立即加载(我认为是这个版本的bug)。 

Hibernate不会,hibernate始终就是默认就是把返回代理放在最优先考虑的方式。 
3、如果使用非HQL或QL的查询方式,如get之类的,JOIN就会出现问题,因为使用的left outer join方式,就只会查询出有关联数据的Item。 


SUBSELECT: 
1、JPA和hibernate是相同的: 
@OneToMany(mappedBy="item",cascade=CascadeType.ALL,fetch=FetchType.LAZY) 
@Fetch(value=FetchMode.SUBSELECT) 
就是,默认是懒加载模式 
后台打印: 
Hibernate: select item0_.ITEM_ID as ITEM1_1_, item0_.ITEM_NAME as ITEM2_1_ from Item item0_ 

2、但是,如果修改为: 
@OneToMany(mappedBy="item",cascade=CascadeType.ALL,fetch=FetchType.EAGER) 
@Fetch(value=FetchMode.SUBSELECT) 
后台就会打印: 
Hibernate: select item0_.ITEM_ID as ITEM1_1_, item0_.ITEM_NAME as ITEM2_1_ from Item item0_ 
Hibernate: select bids0_.ITEM_ID as ITEM3_1_1_, bids0_.BID_ID as BID1_1_, bids0_.BID_ID as BID1_0_0_, bids0_.BID_MONEY as BID2_0_0_, bids0_.ITEM_ID as ITEM3_0_0_ from Bid bids0_ where bids0_.ITEM_ID in (select item0_.ITEM_ID from Item item0_) 
就是JPA中QL语句也会生效SUBSELECT设置。Hibernate也会生效。 

所以,综上所述,当时使用HQL或QL查询的时候,而且需要获取脱管对象的关联对象时,建议使用SUBSELECT方式,但是,还需要额外指明即时加载。且,JPA的JOIN方式的QL查询还存在bug。 

但是,我们如果想在第一个select的HQL方式中也用join该如何呢? 

3高速缓存基本原理  
事务范围高速缓存--添加到当前的工作单元,它可能是一个数据库事务,甚至是一个对话。他只有在工作单元运行时才是有效的,才能被使用。每个工作单元都有自己的高速缓存。这个高速缓存中的数据不会被并发访问。 
过程范围高速缓存--在许多(可能并发的)工作单元或者事务之间共享。这意味着过程范围高速缓存中的数据被并发运行的线程访问,显然隐含和事务隔离性。 
集群范围高速缓存--在同一台机器的多个线程之间或者一个集群中的多台机器上共享。这里,网络通信是一个值得考虑的关键点。 
1、高速缓存和对象同一性 
2、高速缓存和并发 
3、高速缓存和事务隔离性 


完全的ORM解决方案让你单独给每个类配置二级高速缓存。高速缓存的好的备选类是表示一下数据的类: 
很少改变的数据;不重要的数据(如内容管理数据);应用程序固有的而非共享的数据。 
对于二级高速缓存来说,不好的备选类: 
经常被更新的数据;财务数据;通过遗留应用程序共享的数据。 

许多应用程序都包含大量下面类型的数据: 
少量的实例;被另一个类或者几个类的许多实例引用的每一个实例;很少更新的实例。 
这种数据有时称作引用数据(reference data)。 

Hibernate高速缓存架构: 
1、Hibernate二级高速缓存 
范围:从特定的SessionFactory或者EntityManager所有持久化上下文共享同一个二级高速缓存。 
二级高速缓存是否被启用;Hibernate并发策略;高速缓存过期策略;高速缓存的物理格式。 

(如果你有更新比读取更经常数据,就不要启用二级高速缓存) 

2、内建的并发策略 
事务隔离级别: 
事务--只可用于托管环境,如有必要,他还保证完全的事务隔离知道可重复读取。给主要用于读取的数据使用这种策略,因为在这种数据中,防止并发事务中的废弃数据最为关键,极少数情况下用于更新。 
读/写--这种策略利用时间戳机制,维护读取提交隔离,并且只在非集群环境中可用。还是给主要用于读取数据使用这种策略,因为在这种数据中防止并发事务中废弃数据最为关键,极少数情况下用于更新。 
非严格读/写--不提供高速缓存和数据库之间的一致性保证。如果有可能并发访问相同的实体,你应该配置一个足够短的超时期限。否则,则可能从高速缓存中读取废弃的数据。如果数据几乎不变,并且废弃的数据不可能是关键的关注点,那就使用这种策略。 
只读--并发策略适合于从不改变的数据。他只用于引用数据。 

3、选择高速缓存提供程序 
见图:高速缓存并发策略支持.jpg 

4高速缓存实战  

首先,考虑每个实体类和集合,并找出哪种高速缓存并发策略可能适合。在给本地的和集群的高速缓存选择好高速缓存提供程序之后,讲编写他们的配置文件。 

步骤一:选择并发控制策略: 
Hibernate一把设置:
  1. <class name="Category" table="CATEGORY">  
  2.     <!-- 缓存配置;读/写并发策略 -->  
  3.     <cache usage="read-write"/>  
  4.   
  5.     <id name="categoryId" column="CATEGORY_ID" type="long">  
  6.         <generator class="native"/>  
  7.     </id>  
  8.       
  9.     <property name="categoryName" type="string" column="CATEGORY_NAME"/>  
  10.       
  11. </class>  

JPA配置:
  1. /** 
  2. * 分类 
  3. * @author partner4java 
  4. * 
  5. */  
  6. @Entity  
  7. @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)  
  8. public class Category implements Serializable {  
  9.     @Id  
  10.     @GeneratedValue  
  11.     @Column(name="CATEGORY_ID")  
  12.     private Long categoryId;  
  13.     @Column(name="CATEGORY_NAME")  
  14.     private String categoryName;  
  15. ...  

步骤二:设置hibernate.cfg.xml 
选择EHCache作为二级高速缓存
  1. <!-- Enable the second-level cache  -->  
  2. <property name="cache.use_second_level_cache">true</property>  
  3. <!--  老版本  
  4. <property name="cache.provider_class">  
  5.     net.sf.ehcache.hibernate.EhCacheProvider  
  6. </property>  
  7. -->  
  8.   <property name="cache.region.factory_class">  
  9.            net.sf.ehcache.hibernate.EhCacheRegionFactory</property>  
  10. <property name="net.sf.ehcache.configurationResourceName">  
  11.     ehcache.xml  
  12. </property>  
  13.      
步骤三:配置ehcache.xml
  1. <ehcache>  
  2.    <defaultCache  
  3.       maxElementsInMemory="10"  
  4.       eternal="false"  
  5.       timeToIdleSeconds="120"  
  6.       timeToLiveSeconds="120"  
  7.       overflowToDisk="false">  
  8.     </defaultCache>  
  9.     <cache  
  10.         name="quick.update"  
  11.         maxElementsInMemory="10"  
  12.         eternal="false"  
  13.         timeToIdleSeconds="10"  
  14.         timeToLiveSeconds="10"  
  15.         overflowToDisk="false"  
  16.     />  
  17.       
  18.   <!-- <terracottaConfig url="localhost:9510"/> -->    
  19. </ehcache>  
eternal="false",不会永恒,配置超时时间。(以妙为单位) 
控制二级高速缓存: 
配置开启: 
<property name="cache.use_second_level_cache">true</property> 
移除单个元素: 
SessionFactory.evict(Category.class, new Long(1)); 
移除对象: 
SessionFactory.evict("auction.model.Category"); 
(二级高速缓存的清除是非事物的) 

交互方式: 
CacheMode.NORMAL--默认的行为。 
CacheMode.IGNORE--Hibernate从来不与二级高速缓存交互,除了更新发生时被高速缓存的项目失效之外。 
CacheMode.PUT--Hibernate从来不从二级高速缓存中读取项目,但是当它从数据库中读取项目时,会把项目添加到高速缓存。 
CacheMode.REFRESH--Hibernate从来不从二级高速缓存中读取项目,但是当他从数据库中读取项目时,会把项目添加到高速缓存。在这种模式下,hibernate.cache.use_minimal_puts的作用被忽略,一遍在一个复制的集群高速缓存中强制高速缓存的刷新。 

测试调用: 

  1.     private static void test1() {  
  2.         Configuration configuration = new Configuration().configure();  
  3.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  4.         Session session = sessionFactory.openSession();  
  5.         Transaction tr = session.beginTransaction();  
  6.           
  7.         Category category = new Category("hell");  
  8. //      session.save(category);  
  9.         session.persist(category);  
  10.           
  11.         System.out.println(category);  
  12.           
  13.         tr.commit();  
  14.         session.close();  
  15.           
  16.         try {  
  17.             //期间改变数据库的数据(删除此条数据)  
  18.             Thread.sleep(30*1000);  
  19.         } catch (InterruptedException e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.           
  23.         Session session2 = sessionFactory.openSession();  
  24.           
  25.         Category category2 = (Category) session2.get(Category.class, category.getCategoryId());  
  26.         System.out.println(category2);  
  27.         //打印null,证明,我即使配置了<cache usage="read-write"/>,通过session的save或者persist还是不会把新的实体保存到二级缓存,那么就搞不清到底是怎么回事了?  
  28.           
  29.         session2.close();  
  30.         sessionFactory.close();  
  31.     }  
  32.       
  33.       
  34.     private static void test2() {  
  35.         Configuration configuration = new Configuration().configure();  
  36.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  37.         Session session = sessionFactory.openSession();  
  38.         Transaction tr = session.beginTransaction();  
  39.           
  40.         Category category = (Category) session.get(Category.class, 2L);  
  41.           
  42.         System.out.println(category);  
  43.           
  44.         tr.commit();  
  45.         session.close();  
  46.           
  47.         try {  
  48.             //期间改变数据库的数据(删除此条数据)  
  49.             Thread.sleep(30*1000);  
  50.         } catch (InterruptedException e) {  
  51.             e.printStackTrace();  
  52.         }  
  53.           
  54.         Session session2 = sessionFactory.openSession();  
  55.           
  56.         Category category2 = (Category) session2.get(Category.class, 2L);  
  57.         System.out.println(category2);  
  58.         //正常打印,且没执行sql,获取二级缓存数据,但是如果配置<cache usage="read-write" region="quick.update"/>,10秒清楚缓存,那么就会超时,导致,打印sql和执行sql  
  59.           
  60.         session2.close();  
  61.         sessionFactory.close();  
  62.     }  
  63.       
  64.       
  65.       
  66.       
  67.       
  68.     private static void test3() {  
  69.         Configuration configuration = new Configuration().configure();  
  70.         SessionFactory sessionFactory = configuration.buildSessionFactory();  
  71.         Session session = sessionFactory.openSession();  
  72.         Transaction tr = session.beginTransaction();  
  73.           
  74.         Category category = (Category) session.get(Category.class, 5L);  
  75.           
  76.         System.out.println(category);  
  77.           
  78.         category.setCategoryName("hello world22");  
  79.           
  80.         tr.commit();  
  81.         session.close();  
  82.           
  83.         Session session2 = sessionFactory.openSession();  
  84.           
  85.         Category category2 = (Category) session2.get(Category.class, 5L);  
  86.         System.out.println(category2);  
  87.         //ok,没有执行查询,且更新生效。但如如果改成,<cache usage="read-only"/>,后台报错: Can't write to a readonly object  
  88.           
  89.         session2.close();  
  90.         sessionFactory.close();  
  91.     }  
  92.       
  93.       
  94. //  总结:  
  95. //  17.read-only  
  96. //  Caches data that is never updated.  
  97. //  17.nonstrict-read-write  
  98. //  Caches data that is sometimes updated without ever locking the cache. If concurrent access to an item  
  99. //  is possible, this concurrency strategy makes no guarantee that the item returned from the cache is the  
  100. //  latest version available in the database. Configure your cache timeout accordingly!  
  101. //  17.read-write  
  102. //  Caches data that is sometimes updated while maintaining the semantics of "read committed" isolation  
  103. //  level. If the database is set to "repeatable read", this concurrency strategy almost maintains the  
  104. //  semantics. Repeatable read isolation is compromised in the case of concurrent writes.  
  105.       


与test1()方法对应,曾经有人给过一个demo:
  1.     //save() 方法保存一个对象到数据库中, 同时也会缓存该对象  
  2.     private static void test2() {  
  3.         Session session = HibernateUtil.getSessionFactory().openSession();  
  4.         Transaction tx = session.beginTransaction();  
  5.           
  6.         User user = new User();  
  7.         user.setAge(21);  
  8.         user.setName("name_21");  
  9.           
  10.         Serializable userId = session.save(user);  
  11.           
  12.         session.get(User.class, userId);  
  13.           
  14.         tx.commit();  
  15.         session.close();  
  16.     }  
  17. //那么你看看那这个demo是否正确呢?我以为这个demo是不对的,因为是同一个session当然,不会执行第二条查询sql,不然你把缓存去掉试试。  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第一部分 从Hibernate和EJB 3.0开始  第1章 理解对象/关系持久化    1.1 什么是持久化     1.1.1 关系数据库     1.1.2 理解SQL     1.1.3 在Java中使用SQL     1.1.4 面向对象应用程序中的持久化    1.2 范式不匹配     1.2.1 粒度问题     1.2.2 子类型问题     1.2.3 同一性问题     1.2.4 与关联相关的问题     1.2.5 数据导航的问题     1.2.6 不匹配的代价    1.3 持久层和其他层 显示全部信息第一部分 从Hibernate和EJB 3.0开始  第1章 理解对象/关系持久化    1.1 什么是持久化     1.1.1 关系数据库     1.1.2 理解SQL     1.1.3 在Java中使用SQL     1.1.4 面向对象应用程序中的持久化    1.2 范式不匹配     1.2.1 粒度问题     1.2.2 子类型问题     1.2.3 同一性问题     1.2.4 与关联相关的问题     1.2.5 数据导航的问题     1.2.6 不匹配的代价    1.3 持久层和其他层     1.3.1 分层架构     1.3.2 用SQL/JDBC手工编写持久层     1.3.3 使用序列化     1.3.4 面向对象的数据库系统     1.3.5 其他选项    1.4 ORM     1.4.1 什么是ORM     1.4.2 一般的ORM问题     1.4.3 为什么选择ORM     1.4.4 Hibernate、EJB 3和JPA简介    1.5 小结   第2章 启动项目    2.1 启动Hibernate项目     2.1.1 选择开发过程     2.1.2 建立项目     2.1.3 Hibernate配置和启动     2.1.4 运行和测试应用程序    2.2 启动Java Persistence项目     2.2.1 使用Hibernate Annotations     2.2.2 使用Hibernate EntityManager     2.2.3 引入EJB组件     2.2.4 切换到Hibernate接口    2.3 反向工程遗留数据库     2.3.1 创建数据库配置     2.3.2 定制反向工程     2.3.3 生成Java源代码    2.4 与Java EE服务整合     2.4.1 与JTA整合     2.4.2 JNDI绑定的SessionFactory     2.4.3 JMX服务部署    2.5 小结   第3章 领域模型和元数据    3.1 CaveatEmptor应用程序     3.1.1 分析业务领域     3.1.2 CaveatEmptor领域模型    3.2 实现领域模型     3.2.1 处理关注点渗漏     3.2.2 透明和自动持久化     3.2.3 编写POJO和持久化实体类     3.2.4 实现POJO关联     3.2.5 把逻辑添加到访问方法    3.3 ORM元数据     3.3.1 XML中的元数据     3.3.2 基于注解的元数据     3.3.3 使用XDoclet     3.3.4 处理全局的元数据     3.3.5 运行时操作元数据    3.4 其他实体表示法     3.4.1 创建动态的应用程序     3.4.2 表示XML中的数据    3.5 小结  第二部分 映射概念和策略  第4章 映射持久化类    4.1 理解实体和值类型     4.1.1 细粒度的领域模型     4.1.2 定义概念     4.1.3 识别实体和值类型    4.2 映射带有同一性的实体     4.2.1 理解Java同一性和等同性     4.2.2 处理数据库同一性     4.2.3 数据库主键    4.3 类映射选项     4.3.1 动态的SQL生成     4.3.2 使实体不可变     4.3.3 给查询命名实体     4.3.4 声明包名称     4.3.5 用引号把SQL标识符括起来     4.3.6 实现命名约定    4.4 细粒度的模型和映射     4.4.1 映射基础属性     4.4.2 映射组件    4.5 小结   第5章 继承和定制类型    5.1 映射类继承     5.1.1 每个带有隐式多态的具体类一张表     5.1.2 每个带有联合的具体类一张表     5.1.3 每个类层次结构一张表     5.1.4 每个子类一张表     5.1.5 混合继承策略     5.1.6 选择策略    5.2 Hibernate类型系统     5.2.1 概述实体和值类型     5.2.2 内建的映射类型     5.2.3 使用映射类型    5.3 创建定制的映射类型     5.3.1 考虑定制的映射类型     5.3.2 扩展点     5.3.3 定制映射类型的案例     5.3.4 创建UserType     5.3.5 创建CompositeUserType     5.3.6 参数化定制类型     5.3.7 映射枚举    5.4 小结   第6章 映射集合和实体关联    6.1 值类型的set、bag、list和map     6.1.1 选择集合接口     6.1.2 映射set     6.1.3 映射标识符bag     6.1.4 映射list     6.1.5 映射map     6.1.6 排序集合和有序集合  6.2 组件的集合     6.2.1 编写组件类     6.2.2 映射集合     6.2.3 启用双向导航     6.2.4 避免非空列    6.3 用注解映射集合     6.3.1 基本的集合映射     6.3.2 排序集合和有序集合     6.3.3 映射嵌入式对象的集合    6.4 映射父/子关系     6.4.1 多样性     6.4.2 最简单的可能关联     6.4.3 使关联双向     6.4.4 级联对象状态    6.5 小结   第7章 高级实体关联映射    7.1 单值的实体关联     7.1.1 共享的主键关联     7.1.2 一对一的外键关联     7.1.3 用联结表映射    7.2 多值的实体关联     7.2.1 一对多关联     7.2.2 多对多关联     7.2.3 把列添加到联结表     7.2.4 映射map    7.3 多态关联     7.3.1 多态的多对一关联     7.3.2 多态集合     7.3.3 对联合的多态关联     7.3.4 每个具体类一张多态表    7.4 小结   第8章 遗留数据库和定制SQL    8.1 整合遗留数据库     8.1.1 处理主键     8.1.2 带有公式的任意联结条件     8.1.3 联结任意的表     8.1.4 使用触发器    8.2 定制SQL     8.2.1 编写定制CRUD语句     8.2.2 整合存储过程和函数    8.3 改进Schema DDL     8.3.1 定制SQL名称和数据类型     8.3.2 确保数据一致性     8.3.3 添加领域约束和列约束     8.3.4 表级约束     8.3.5 数据库约束     8.3.6 创建索引     8.3.7 添加辅助的DDL    8.4 小结  第三部分 会话对象处理  第9章 使用对象    9.1 持久化生命周期     9.1.1 对象状态     9.1.2 持久化上下文    9.2 对象同一性和等同性     9.2.1 引入对话     9.2.2 对象同一性的范围     9.2.3 脱管对象的同一性     9.2.4 扩展持久化上下文    9.3 Hibernate接口     9.3.1 保存和加载对象     9.3.2 使用脱管对象     9.3.3 管理持久化上下文    9.4 JPA     9.4.1 保存和加载对象     9.4.2 使用脱管的实体实例    9.5 在EJB组件中使用Java Persistence     9.5.1 注入EntityManager     9.5.2 查找EntityManager     9.5.3 访问EntityManagerFactory    9.6 小结   第10章 事务和并发    10.1 事务本质     10.1.1 数据库和系统事务     10.1.2 Hibernate应用程序中的事务     10.1.3 使用Java Persistence的事务    10.2 控制并发访问     10.2.1 理解数据库级并发     10.2.2 乐观并发控制     10.2.3 获得额外的隔离性保证    10.3 非事务数据访问     10.3.1 揭开自动提交的神秘面纱     10.3.2 使用Hibernate非事务地工作     10.3.3 使用JTA的可选事务    10.4 小结   第11章 实现对话    11.1 传播Hibernate Session     11.1.1 Session传播的用例     11.1.2 通过线程局部传播     11.1.3 利用JTA传播     11.1.4 利用EJB传播    11.2 利用Hibernate的对话     11.2.1 提供对话保证     11.2.2 利用脱管对象的对话     11.2.3 给对话扩展Session    11.3 使用JPA的对话     11.3.1 Java SE中的持久化上下文传播     11.3.2 在对话中合并脱管对象     11.3.3 在Java SE中扩展持久化上下文    11.4 使用EJB 3.0的对话     11.4.1 使用EJB的上下文传播     11.4.2 利用EJB扩展持久化上下文    11.5 小结   第12章 有效修改对象    12.1 传播性持久化     12.1.1 按可到达性持久化     12.1.2 把级联应用到关联     12.1.3 使用传播性状态     12.1.4 利用JPA的传播性关联    12.2 大批量和批量操作     12.2.1 使用HQL和JPA QL的大批量语句     12.2.2 利用批量处理     12.2.3 使用无状态的会话    12.3 数据过滤和拦截     12.3.1 动态数据过滤     12.3.2 拦截Hibernate事件     12.3.3 内核事件系统     12.3.4 实体监听器和回调    12.4 小结   第13章 优化抓取高速缓存    13.1 定义全局抓取计划     13.1.1 对象获取选项     13.1.2 延迟的默认抓取计划     13.1.3 理解代理     13.1.4 禁用代理生成     13.1.5 关联和集合的即时加载     13.1.6 通过拦截延迟加载    13.2 选择抓取策略     13.2.1 批量预抓取数据     13.2.2 通过子查询预抓取集合     13.2.3 通过联结即时抓取     13.2.4 给二级表优化抓取     13.2.5 优化指导方针    13.3 高速缓存基本原理     13.3.1 高速缓存策略和范围     13.3.2 Hibernate高速缓存架构    13.4 高速缓存实践     13.4.1 选择并发控制策略     13.4.2 理解高速缓存区域     13.4.3 设置本地的高速缓存提供程序     13.4.4 设置重复的高速缓存     13.4.5 控制二级高速缓存    13.5 小结   第14章 利用HQL和JPA QL查询    14.1 创建和运行查询     14.1.1 准备查询     14.1.2 执行查询     14.1.3 使用具名查询    14.2 基本的HQL和JPA QL查询     14.2.1 选择     14.2.2 限制     14.2.3 投影    14.3 联结、报表查询和子查询     14.3.1 联结关系和关联     14.3.2 报表查询     14.3.3 利用子查询    14.4 小结   第15章 高级查询选项    15.1 利用条件和示例查询     15.1.1 基本的条件查询     15.1.2 联结和动态抓取     15.1.3 投影和报表查询     15.1.4 按示例查询    15.2 利用原生的SQL查询     15.2.1 自动的结果集处理     15.2.2 获取标量值     15.2.3 Java Persistence中的原生SQL    15.3 过滤集合    15.4 高速缓存查询结果     15.4.1 启用查询结果高速缓存     15.4.2 理解查询高速缓存     15.4.3 什么时候使用查询高速缓存     15.4.4 自然标识符高速缓存查找    15.5 小结   第16章 创建和测试分层的应用程序    16.1 Web应用程序中的Hibernate     16.1.1 用例简介     16.1.2 编写控制器     16.1.3 OSIV模式     16.1.4 设计巧妙的领域模型    16.2 创建持久层     16.2.1 泛型的数据访问对象模式     16.2.2 实现泛型CRUD接口     16.2.3 实现实体DAO     16.2.4 利用数据访问对象    16.3 命令模式简介     16.3.1 基础接口     16.3.2 执行命令对象     16.3.3 命令模式的变形    16.4 利用EJB 3.0设计应用程序     16.4.1 利用有状态的bean实现会话     16.4.2 利用EJB编写DAO     16.4.3 利用依赖注入    16.5 测试     16.5.1 理解不同种类的测试     16.5.2 TestNG简介     16.5.3 测试持久层     16.5.4 考虑性能基准    16.6 小结   第17章 JBoss Seam简介    17.1 Java EE 5.0编程模型     17.1.1 JSF详解     17.1.2 EJB 3.0详解     17.1.3 用JSF和EJB 3.0编写Web应用程序     17.1.4 分析应用程序    17.2 用Seam改善应用程序     17.2.1 配置Seam     17.2.2 将页面绑定到有状态的Seam组件     17.2.3 分析Seam应用程序    17.3 理解上下文组件     17.3.1 编写登录页面     17.3.2 创建组件     17.3.3 给上下文变量起别名     17.3.4 完成登录/注销特性    17.4 验证用户输入     17.4.1 Hibernate Validator简介     17.4.2 创建注册页面     17.4.3 用Seam实现国际化    17.5 利用Seam简化持久化     17.5.1 实现对话     17.5.2 让Seam管理持久化上下文    17.6 小结  附录A SQL基础知识  附录B 映射快速参考
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值