Hibernate对象状态
瞬时(transient):由new操作符创建,且尚未Hibernate Session关联。瞬时对象不会被持久化到数据库,也不会被赋予持久化标识。
持久(persistent):持久化的实例在数据库中有对应的记录,并拥有一个持久化标识。
持久化的实例可能是刚被保存,或刚被加载的,无论哪一种,它都只存在于相关联的Session作用范围内。这点很重要。Hibernate会检测处于持久化状态的对象的任何变动,在当前操作单元执行完毕时,将对对象数据与数据库同步。
脱管(detached):在与持久化对象关联的Session关闭后,对象就变成脱管状态。
Hibernate的第一级缓存(Session缓存)
当调用Session的save(),update(),saveOrUpdate(),load()或get()方法,以及调用Query接口的list(),iterator()或filter()方法时,如果缓存中没有相应的对象,Hibernate便会将对象添加到Session缓存中。
比如:
Transaction transaction = session.beginTransaction(); Feeling feeling = (Feeling) session.get(Feeling.class, 1); System.out.println(feeling); System.out.println(feeling.getFeelingComments()); Feeling feeling1 = (Feeling) session.get(Feeling.class, 1); transaction.commit();
Hibernate: select feeling0_.feeling_id as feeling1_1_0_, feeling0_.content as content1_0_, feeling0_.id as id1_0_ from feeling feeling0_ where feeling0_.feeling_id =? [email protected] Hibernate: select feelingcom0_.feeling_id as feeling3_1_1_, feelingcom0_.feelingComment_id as feelingC1_1_, feelingcom0_.feelingComment_id as feelingC1_2_0_, feelingcom0_.content as content2_0_, feelingcom0_.feeling_id as feeling3_2_0_, feelingcom0_.id as id2_0_ from feeling_comment feelingcom0_ where feelingcom0_.feeling_id=? [[email protected], [email protected], [email protected], [email protected]]
可以看到第二次的get()并没有生成sql.
可以使用1.evict()从缓存中清除指定的持久化对象。2.clear()清除所有。。。。多数情况下,不提倡通过上面两个方法管理缓存。
当Session缓存中对象的属性发生变化时,Session并不会立即清理缓存并执行相关的sql,使数据库同步,而只是在特点的时间点才清理缓存。这使得Session可以把几条相关的sql合并成一条,提升性能。
上面的特点的时间点包括:
1.调用Transaction的commit()方法。这时清理缓存,然后向数据库提交事务。这样做是为了最大限度减少数据库的访问和尽可能的缩短当前事务对数据库中相关资源的锁定时间。
2.执行一些查询操作。这样做是为了保证查询返回的结果是即时,正确的。
3.显式调用flush().注意,flush()不会提交事务,数据还有可能rollback.
对于flush()最常用的用法当然是批量操作了。比如批量插入100000条数据。
for(int i=1;i<100000;i++){ Customer c=new Customer(); session.save(c); if(i%20==0){ session.flush(); session.clear(); } }
设置hibernate.jdbc.batch_size=20。一定数量(20)时强制刷出,使缓存与数据库同步(数据写入数据库),然后清空所有缓存,避免内存溢出。
load()和get()
http://www.cnblogs.com/binjoo/articles/1621254.html
注意两者的运行流程:get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。
对load(),如果只是Feeling feeling = (Feeling) session.load(Feeling.class, 1);是不会发出sql的,只有像System.out.println(feeling);这样使用了feeling才会发出。
load()只是生成类的动态代理,并没有将数据库的数据加载到相应的类中,所以适合在数据更新或为表插入外键数据时使用,
如hibernate查漏补缺1中的那个好友分类例子中,插入新加的好友并把他加入到已经存在的分类中。
Friend_Category fc = new Friend_Category(); fc.setCategory_name("已经存在的好友分类"); User_Friend_pk pk = new User_Friend_pk(); pk.setFriend_name(“新好友”); pk.setName("TheViper"); User_Friend uf = new User_Friend(); uf.setUser_friend_pk(pk); Friend_Category fc1 = (Friend_Category) session.load( Friend_Category.class, 1); uf.setFriend_categorys(fc1); // uf.setSort("好基友"); session.saveOrUpdate(fc1); session.save(uf);
注意,这时对load的Friend_Category要用saveOrUpdate(),否则会出现异常 org.hibernate.PersistentObjectException: uninitialized proxy passed to save()。
很多人都在用Spring的HibernateTemplate模板。它有个不好的地方,比如:
Feeling f = hibernateTemplate.load(Feeling.class, 1); System.out.println(f);
返回异常 org.hibernate.LazyInitializationException: could not initialize proxy - no Session
原因在于HibernateTemplate和Spring的其他dao模板如JdbcTemplate一样,走的是下面的流程:
1.准备资源->2.启动->3.数据访问->4.返回数据->5.提交/回滚事务->6.处理异常关闭资源
其中的3,4是我们写的,其他的都是模板帮我们做的。也就是说,每次用了HibernateTemplate的方法后,HibernateTemplate都会自己关闭资源,防止数据库连接,内存泄漏,而关闭的资源中就包括了Hibernate的Session.Session,自然就找不到Session缓存和类的动态代理(load()返回的)了。
Cascade
在<set>,<one-to-one>,<many-to-one>(个人觉得在many-to-one里面很没用)中使用。
Hibernate日志
Hibernate第二级缓存
进程或集群范围内的缓存,SessionFactory级别,而第一级缓存是事务范围的缓存。第二级缓存中存放的是对象的散装数据。
第二级缓存的4种并发访问策略,每一种策略对应一种事务隔离级别。
- 事务型(transactional):仅在受管理的环境中适应。提供repeatable read事务隔离级别,适用于经常访问但很少被修改的数据,可防止脏读和不可重复读这类并发问题。
- 读写型(read-write):read committed事务隔离级别,仅在非集群的环境中适用。适用于经常访问但很少被修改的数据,防止脏读。
- 非严格读写型(nonstrict-read-write):不保证缓存与数据库中数据的一致性。如果两个事务访问缓存中的相同数据,则必须为该数据一个很短的数据过期时间,以避免脏读。适用于极少被修改且允许偶尔脏读的数据这种情况。
- 只读型(read-only):适用于从来不会修改的数据。
transactional隔离级别最高,并发性能最低;read-only则反之。
缓存插件可分为进程范围内缓存(EHCache,OSCache),集群范围内缓存(SwarmCache,JbossCache).其中SwarmCache不支持查询缓存。
配置hibernate.cfg.xml
<property name="hibernate.cache.use_second_level_cache">true</property> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
Hibernate允许在类和集合上配置二级缓存,在<class>和<set>上都有<cache>子元素.
用法:<cache usage="transactional|read-write|nonstrict-read-write|read-only" region="regionName" include="all|non-lazy"/>
usage:指定并发访问策略。region:指定二级缓存的区域名字,默认为类或集合的名字。include:指定当缓存一个对象时,会不会缓存它的映射为延迟加载的属性。默认all.
ehcache配置
配置<cache>的时候有一点要注意,在<set>下配置了<cache>的话还需要在<set>的另一边的<class>下也配置<cache>才会有效。
管理二级缓存
sessionFactory.evict(Category.class,new Long(1));//清除二级缓存中id为1的Category对象
sessionFactory.evict(”mypack.Category“);//清除二级缓存中Category类的所有对象
sessionFactory.evictCollection(”mypack.Category.items“);//清除二级缓存中Category类的所有对象的items集合
Session与二级缓存的交互模式:
用org.hibernate.CacheMode的5个静态常量表示。
- CacheMode.NORMAL:正常模式(默认),Session会向二级缓存中读取和写入数据。
- CacheMode.IGNORE:忽略模式,Session不会向二级缓存读取,也不会写入数据。
- CacheMode.GET:Session对二级缓存只读不写。
- CacheMode.PUT:Session对二级缓存只写不对。
- CacheMode.REFRESH:刷新模式,Session不会从二级缓存中读取数据,但会向其中写入从数据库读取的数据。和PUT的区别在于REFRESH会忽略配置文件中的hibernate.cache.use_minimal_puts属性,强制刷新二级缓存中的所有数据。
用法:session.setCacheMode(CacheMode.IGNORE);