hibernate运行原理_第1页
hibernate运行原理_第2页
hibernate运行原理_第3页
hibernate运行原理_第4页
hibernate运行原理_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、hibernate 简介:hibernate是一个开源框架,它是对象关联关系映射的框架,它对JDBC做了轻量级的封装,而我们java程序员可以使用面向对象的思想来操纵数据库。hibernate核心接口configuration:负责配置并启动hibernate,创建SessionFactorysessionFactory:负责初始化hibernate,创建session对象session:负责被持久化对象CRUD操作Transaction:负责事物相关的操作Query和Criteria接口:负责执行各种数据库查询hibernate工作原理:1.通过Configuration 读取并解析hibe

2、rnate.cfg.xml配置文件2.通过SessionFactory sf = config.buildSessionFactory();/创建SessionFactory4.Session session = sf.openSession();/打开Sesssion5.Transaction tx = session.beginTransaction();/创建并启动事务Transation6.persistent operate操作数据,持久化操作mit();/提交事务8.关闭Session9.关闭SesstionFactory为什么要用hibernate: 1. 对JDBC访问数据库的

3、代码做了封装,大大简化了数据访问层繁琐的重复性代码。2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。4. hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。Hibernate缓存:缓存是介于应用程序和物理数据源之间,其作用是为了降低应用程序对物理数据源访问的频次,从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的

4、时刻或事件会同步缓存和物理数据源的数据。 缓存的介质一般是内存,所以读写速度很快。但如果缓存中存放的数据量非常大时,也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质,还要考虑到管理缓存的并发访问和缓存数据的生命周期。 Hibernate的缓存包括Session的缓存和SessionFactory的缓存,其中SessionFactory的缓存又可以分为两类:内 置缓存和外置缓存。Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存。SessionFactory的内置缓存和 Session的缓存在实现方式上比较相似,前者是SessionFactory对象的一些集

5、合属性包含的数据,后者是指Session的一些集合属性包 含的数据。SessionFactory的内置缓存中存放了映射元数据和预定义SQL语句,映射元数据是映射文件中数据的拷贝,而预定义SQL语句是在 Hibernate初始化阶段根据映射元数据推导出来,SessionFactory的内置缓存是只读的,应用程序不能修改缓存中的映射元数据和预定义 SQL语句,因此SessionFactory不需要进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的插件。在默认 情况下,SessionFactory不会启用这个插件。外置缓存的数据是数据库数据的拷贝,外置缓存的介质可以是

6、内存或者硬盘。 SessionFactory的外置缓存也被称为Hibernate的第二级缓存。Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝,那么它们之间的区别是什么呢?为了理解二者的区别,需要深入理解持久化层的缓存的两个特性:缓存的范围和缓存的并发访问策略。持久化层的缓存的范围缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。1 事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。在此范围下,缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联

7、的的对象形式。 2 进程范围:缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进 程结束时,缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据,所以存放的介质可以是内存或硬盘。缓存内的数据既可以是相互关联的对象形式也可 以是对象的松散数据形式。松散的对象数据形式有点类似于对象的序列化数据,但是对象分解为松散的算法比对象序列化的算法要求更快。3 集群范围:在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的

8、数据通常采用对象的松散数据形式。对大多数应用来说,应该慎重地考虑是否需要使用集群范围的缓存,因为访问的速度不一定会比直接访问数据库数据的速度快多少。 持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据,还可以到进程范围或集群范围的缓存内查询,如果还是没有查到,那么只有到 数据库中查询。事务范围的缓存是持久化层的第一级缓存,通常它是必需的;进程范围或集群范围的缓存是持久化层的第二级缓存,通常是可选的。持久化层的缓存的并发访问策略当多个并发的事务同时访问持久化层的缓存的相同数据时,会引起并发问题,必须采用必要的事务隔离措施。在进程范围或集群范围的缓存,即第二级缓存,会出现并发

9、问题。因此可以设定以下四种类型的并发访问策略,每一种策略对应一种事务隔离级别。事务型:仅仅在受管理环境中适用。它提供了Repeatable Read事务隔离级别。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。读写型:提供了Read Committed事务隔离级别。仅仅在非集群的环境中适用。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读这类的并发问题。 非严格读写型:不保证缓存与数据库中数据的一致性。如果存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量 避免脏读。对于极少被修改,

10、并且允许偶尔脏读的数据,可以采用这种并发访问策略。 只读型:对于从来不会修改的数据,如参考数据,可以使用这种并发访问策略。事务型并发访问策略是事务隔离级别最高,只读型的隔离级别最低。事务隔离级别越高,并发性能就越低。什么样的数据适合存放到第二级缓存中?1、很少被修改的数据 2、不是很重要的数据,允许出现偶尔并发的数据3、不会被并发访问的数据4、参考数据不适合存放到第二级缓存的数据?1、经常被修改的数据2、财务数据,绝对不允许出现并发3、与其他应用共享的数据。Hibernate的二级缓存 如前所述,Hibernate提供了两级缓存,第一级是Session的缓存。由于Session对象的生命周期通

11、常对应一个数据库事务或者一个应用事 务,因此它的缓存是事务范围的缓存。第一级缓存是必需的,不允许而且事实上也无法比卸除。在第一级缓存中,持久化类的每个实例都具有唯一的OID。 第二级缓存是一个可插拔的的缓存插件,它是由SessionFactory负责管理。由于SessionFactory对象的生命周期和应用程序的整个 过程对应,因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题,因此需要采用适当的并发访问策 略,该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与Hibernate集成。第二级缓存是可选的,可以在每个

12、类或每个集 合的粒度上配置第二级缓存。Hibernate的二级缓存策略的一般过程如下:1) 条件查询的时候,总是发出一条select * from table_name where . (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。 2) 把获得的所有数据对象根据ID放入到第二级缓存中。 3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。 4) 删除、更新、增加数据的时候,同时更新缓存。Hibernate的二级缓存策略,是针对于ID查询的缓

13、存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query缓存。Hibernate的Query缓存策略的过程如下: 1) Hibernate首先根据这些信息组成一个Query Key,Query Key包括条件查询的请求一般信息:SQL, SQL需要的参数,记录范围(起始位置rowStart,最大记录个数maxRows),等。 2) Hibernate根据这个Query Key到Query缓存中查找对应的结果列表。如果存在,那么返回这个结果列表;如果不存在,查询数据库,获取结果列表,把整个结果列表根据Query Key放入到Query缓存中。 3) Query Ke

14、y中的SQL涉及到一些表名,如果这些表的任何数据发生修改、删除、增加等操作,这些相关的Query Key都要从缓存中清空。Hibernate是如何延迟加载?get与load的区别1. 对于Hibernate get方法,Hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查询数据库,数据 库中没有就返回null。这个相对比较简单,也没有太大的争议。主要要说明的一点就是在这个版本(bibernate3.2以上)中get方法也会查找二 级缓存!2. Hibernate load方法加载实体对象的时候,根据映射文件上类级别的lazy属性

15、的配置(默认为true),分情况讨论: (1)若为true,则首先在Session缓存中查找,看看该id对应的对象是否存在,不存在则使用延迟加载,返回实体的代理类对象(该代理类为 实体类的子类,由CGLIB动态生成)。等到具体使用该对象(除获取OID以外)的时候,再查询二级缓存和数据库,若仍没发现符合条件的记录,则会抛出一 个ObjectNotFoundException。(2)若为false,就跟Hibernateget方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。这里get和load有两个重要区别: 如果未能发现符合条件的记录

16、,Hibernate get方法返回null,而load方法会抛出一个ObjectNotFoundException。 load方法可返回没有加载实体数据的代 理类实例,而get方法永远返回有实体数据的对象。(对于load和get方法返回类型:好多书中都说:“get方法永远只返回实体类”,实际上并不正 确,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加 载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数 据库

17、来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。)总之对于get和load的根本区别,一句话,hibernate对于 load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方 法,hibernate一定要获取到真实的数据,否则返回null。学过hibernate的人都可能都知道hibernate有三种状态,transient(瞬时状态),persistent(持久化状态)以及 detached(离线状态),大家伙也许也知道这三者之间的区别,比如瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态

18、就是已经 被保存到数据库中,离线状态就是数据库中有,但是session中不存在该对象。但是大家又是否对hibernate的session的那几个特殊方法一 清二楚呢?或者说大家是否能够一眼就快速看出一个测试用例在反复的调用session的诸如save,update方法后会到底发出多少条SQL语句呢? 本篇随笔将会给你答案,本篇随笔将会以大量的测试用例来掩饰hibernate的这三种状态的转变,相信看完本篇随笔的你会对hibernate的那三种 状态有更深入的理解。好了,废话不多说了,相信大家都知道hibernate的这三种状态的含义,那我们就通过一张图来开始我们的深入hibernate的三种状

19、态之旅吧。1.TestTransientsession = HibernateUtil.openSession(); session.beginTransaction(); User user = new User(); user.setUsername(aaa); user.setPassword(aaa); user.setBorn(new Date(); /* * 以上user就是一个Transient(瞬时状态),此时user并没有被session进行托管,即在session的 * 缓存中还不存在user这个对象,当执行完save方法后,此时user被session托管,并且数据库中存

20、在了该对象 * user就变成了一个Persistent(持久化对象) */ session.save(user); session.getTransaction().commit();此时我们知道hibernate会发出一条insert的语句,执行完save方法后,该user对象就变成了持久化的对象了Hibernate: insert into t_user (born, password, username) values (?, ?, ?)2.TestPersistent01session = HibernateUtil.openSession(); session.beginTrans

21、action(); User user = new User(); user.setUsername(aaa); user.setPassword(aaa); user.setBorn(new Date(); /以上u就是Transient(瞬时状态),表示没有被session管理并且数据库中没有 /执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态 session.save(user); /此时u是持久化状态,已经被session所管理,当在提交时,会把session中的对象和目前的对象进行比较 /如果两个对象中的值不一致就会继续发出相应的sq

22、l语句 user.setPassword(bbb); /此时会发出2条sql,一条用户做插入,一条用来做更新 session.getTransaction().commit();在调用了save方法后,此时user已经是持久化对象了,被保存在了 session缓存当中,这时user又重新修改了属性值,那么在提交事务时,此时hibernate对象就会拿当前这个user对象和保存在 session缓存中的user对象进行比较,如果两个对象相同,则不会发送update语句,否则,如果两个对象不同,则会发出update语句。Hibernate: insert into t_user (born, pa

23、ssword, username) values (?, ?, ?)Hibernate: update t_user set born=?, password=?, username=? where id=?3.TestPersistent02SimpleDateFormat sdf = new SimpleDateFormat(yyyy-MM-dd); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setBorn(new Date(); u.setUserna

24、me(zhangsan); u.setPassword(zhangsan); session.save(u); u.setPassword(222); /该条语句没有意义 session.save(u); u.setPassword(zhangsan111); /没有意义 session.update(u); u.setBorn(sdf.parse(1988-12-22); /没有意义 session.update(u); session.getTransaction().commit();这个时候会发出多少sql语句呢?还是同样的道理,在调用save方法后,u此时已经是持久化对象了,记住一点

25、:如果一个对象以及 是持久化状态了,那么此时对该对象进行各种修改,或者调用多次update、save方法时,hibernate都不会发送sql语句,只有当事物提交的 时候,此时hibernate才会拿当前这个对象与之前保存在session中的持久化对象进行比较,如果不相同就发送一条update的sql语句,否 则就不会发送update语句Hibernate: insert into t_user (born, password, username) values (?, ?, ?)Hibernate: update t_user set born=?, password=?, username

26、=? where id=?4.TestPersistent03SimpleDateFormat sdf = new SimpleDateFormat(yyyy-MM-dd); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setBorn(sdf.parse(1976-2-3); u.setUsername(zhangsan2); u.setPassword(zhangsan2); session.save(u); /* * 以下三条语句没有任何意义 */ ses

27、sion.save(u); session.update(u); session.update(u); u.setUsername(zhangsan3); session.getTransaction().commit();相信这个测试用例,大家应该都知道结果了,没错,此时hibernate也会发出两条sql语句,原理一样的Hibernate: insert into t_user (born, password, username) values (?, ?, ?)Hibernate: update t_user set born=?, password=?, username=? wher

28、e id=?5.TestPersistent04session = HibernateUtil.openSession(); session.beginTransaction(); /此时u是Persistent User u = (User)session.load(User.class, 4); /由于u这个对象和session中的对象不一致,所以会发出sql完成更新 u.setUsername(bbb); session.getTransaction().commit();我们来看看此时会发出多少sql语句呢?同样记住一点:当session调用load、get方法时,此时如果数据库中有该

29、对象,则该对象也变成了一个持久化对象,被session所托管。因此,这个时候如果对对象进行操作,在提交事务时同样会去与session中的持久化对象进行比较,因此这里会发送两条sql语句Hibernate: select user0_.id as id0_0_, user0_.born as born0_0_, user0_.password as password0_0_, user0_.username as username0_0_ from t_user user0_ where user0_.id=?Hibernate: update t_user set born=?, passwo

30、rd=?, username=? where id=?6.TestPersistent05session = HibernateUtil.openSession(); session.beginTransaction(); /此时u是Persistent User u = (User)session.load(User.class, 4); u.setUsername(123); /清空session session.clear(); session.getTransaction().commit();再看这个例子,当我们load出user对象时,此时user是持久化的对象,在session缓

31、存中存在该对象,此时我们在对user进行修改后,然后调用session.clear()方法,这个时候就会将session的缓存对象清空,那么session中就没有了user这个对象,这个时候在提交事务的时候,发现已经session中已经没有该对象了,所以就不会进行任何操作,因此这里只会发送一条select语句Hibernate: select user0_.id as id0_0_, user0_.born as born0_0_, user0_.password as password0_0_, user0_.username as username0_0_ from t_user user

32、0_ where user0_.id=?7.TestDetached01session = HibernateUtil.openSession(); session.beginTransaction(); /此时u是一个离线对象,没有被session托管 User u = new User(); u.setId(4); u.setPassword(hahahaha); /当执行save的时候总是会添加一条数据,此时id就会根据Hibernate所定义的规则来生成 session.save(u); session.getTransaction().commit();我们看到,当调用了u.setI

33、d(4)时,此时u是一个离线的对象,因为数据库中存在id=4的这个对象,但是该对象又没有被session所托 管,所以这个对象就是离线的对象,要使离线对象变成一个持久化的对象,应该调用什么方法呢?我们知道调用save方法,可以将一个对象变成一个持久化对 象,但是,当save一执行的时候,此时hibernate会根据id的生成策略往数据库中再插入一条数据,所以如果调用save方法,此时数据库会发送 一条插入的语句:Hibernate: insert into t_user (born, password, username) values (?, ?, ?)所以对于离线对象,如果要使其变成持久化

34、对象的话,我们不能使用save方法,而应该使用update方法8.TestDetached02SimpleDateFormat sdf = new SimpleDateFormat(yyyy-MM-dd); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setId(5); /完成update之后也会变成持久化状态 session.update(u); u.setBorn(sdf.parse(1998-12-22); u.setPassword(world); u

35、.setUsername(world); /会发出一条sql session.update(u); session.getTransaction().commit();此时我们看到,当调用了update方法以后,此时u已经变成了一个持久化的对象,那么如果此时对u对象进行修改操作后,在事务提交的时候,则会拿该对象和session中刚保存的持久化对象进行比较,如果不同就发一条sql语句Hibernate: update t_user set born=?, password=?, username=? where id=?9.TestDetached03SimpleDateFormat sdf =

36、 new SimpleDateFormat(yyyy-MM-dd); session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setId(5); /完成update之后也会变成持久化状态 session.update(u); u.setBorn(sdf.parse(1998-12-22); u.setPassword(lisi); u.setUsername(lisi); /会抛出异常 u.setId(333); session.getTransaction().comm

37、it();我们看这个例子,前面的操作一样,调用update方法后,user变成了一个持久化对象,在对user进行一些修改后,此时又通过 u.setId(333)方法设置了u的ID,那么这个时候,hibernate会报错,因为我们的u当前已经是一个持久化对象,如果试图修改一个持久化对象的ID的值的话,就会抛出异常,这点要特别注意org.hibernate.HibernateException: identifier of an instance of com.xiaoluo.bean.User was altered from 5 to 33310.TestDetached04session =

38、 HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setId(5); /现在u就是transient对象 session.delete(u); /此时u已经是瞬时对象,不会被session和数据库所管理 u.setPassword(wangwu); session.getTransaction().commit();接着我们来看这个例子,这里在调用了session.delete()方法以后,此时后u就会变成一个瞬时对象,因为此时数据库中已经不存在该对象 了,既然u已经是一个瞬时对象了

39、,那么对u再进行各种修改操作的话,hibernate也不会发送任何的修改语句,因此这里只会 有一条 delete的语句发生:Hibernate: delete from t_user where id=?11.TestDetached05session = HibernateUtil.openSession(); session.beginTransaction(); User u = new User(); u.setId(4); u.setPassword(zhaoliu); /如果u是离线状态就执行update操作,如果是瞬时状态就执行Save操作 /但是注意:该方法并不常用 sessi

40、on.saveOrUpdate(u); session.getTransaction().commit();这里我们来看看 saveOrUpdate这个方法,这个方法其实是一个偷懒的方法,如果对象是一个离线对象,那么在执行这个方法后,其实是调用了update方法,如果对象是一个瞬时对象,则会调用save方法,记住:如果对象设置了ID值,例如u.setId(4),那么该对象会被假设当作一个离线对象,此时就会执行update操作。Hibernate: update t_user set born=?, password=?, username=? where id=?如果此时我将u.setId(4

41、)这句话注释掉,那么此时u就是一个瞬时的对象,那么此时就会执行save操作,就会发送一条insert语句Hibernate: insert into t_user (born, password, username) values (?, ?, ?)12.TestDetached06session = HibernateUtil.openSession(); session.beginTransaction(); /u1已经是持久化状态 User u1 = (User)session.load(User.class, 3); System.out.println(u1.getUsername(); /u2是离线状态 User u2 = new User(); u2.setId(3); u2.setPassword(123456789); /此时u2将会变成持久化状态,在session的缓存中就存在了两份同样的对象,在session中不能存在两份拷贝,否则会抛出异常 session.saveOrUpdate(u2);我们再来看一下这个例子,此时我们的u1已经是持久化的对象了,保存在session缓存中,u2通过调

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论