原作者:费超
内容大纲
副标题
对象关系映射(ORM)是解决面向对象和关系数据库不匹配的规范。简单来说,ORM通过使用描述对象和数据库之间映射的元数据,自动将ja程序中的对象持久化到关系数据库中。
面向对象的概念面向关系的概念类表行(记录)属性表列(字段)
ORM框架在前端和后端领域都可以看到,比如Android的greenDAO,iOS的coreData,Node.js的mongoose这里主要讲解一下Ja中Hibernate容易忽略的重要点。
保存和获取的执行过程
保存并获取
保存:
根据对象找到user类(user.getClass)和对应的映射文件User.hbm.xml,并解析出表名t_user使用内省机制操作user对象,获取其中的属性:id/name解析映射文件,找到属性对应的列名根据主键生成策略,如果是native,此时主键就不出现在SQL语句中,如当前的SQL语句为:insert into t_user(uname) values (?)其中?就对应user.getXxx()方法
获取:
根据对象找到user类(user.getClass)和对应的映射文件User.hbm.xml,并解析出表名t_user解析映射文件,找到<id>元素对应的列名uid,SQL就拼接成功了处理结果集,把一条数据封装成User对象创建User对象根据列名找到对应的属性名,调用user.setXxx()后返回对象
get和load方法之间的区别
首先看看这个简单的测试代码:
get会立即发送select语句,load不会立刻发送,当使用到该对象的非OID属性时才会发送,延迟加载load方法返回的对象永远不为null,即使在数据库中不存在,所以不能使用if-null的方式来判断,而get可以为null,因为load执行的时候没有发送select语句,所以他不知道数据库中有没有对应数据,所以索性返回一个不为null的对象,如果存在,则再把数据设置到对象中去,如果不存在,使用该对象时报错load方法会创建出代理对象,但是代理对象必须在session关闭之前创建出来,否则会报hibernate中最常见的错误,no session,解决办法为Hibernate.initialize(代理对象)
持久对象的生命周期
为什么要关注持久对象的生命周期?那么我们来回忆一下,我们在使用Hibernate时是否遇到过三个问题:
问题一:主键生成策略不同,se操作时发生INSERT语句的时机不同?native:在执行se方法的时候发送INSERT SQLincrement:在提交事务的时候,才发送INSERT SQL问题二:删除对象的时候,没有立刻发生DELETE语句,而是在提交事务的时候发送的。问题三:为什么在事务环境下,通过get方法得到的对象,只要修改了属性值,会发生UPDATE语句。
那么SQL执行的时机又有什么关系呢?和对象的状态。持久对象的状态是什么?是怎么分的?
划分规则:
当前对象是否有OID(该对象在表中对应有一个id值)对象是否被session所管理(对象是否在一级缓存中)
状态描述的特征临时状态/瞬态是刚刚用new语句创建的,还没有持久化。它不在没有oid的会话中。持久状态已保持,并且有oid添加到会话的缓存中。会话中一直保持分离状态。但是,有不在会话中的oid,也有不在会话中删除的oid,在会话的管理下,但是已经计划删除。在进程中,最终效果将被删除。
持久对象的状态。
对象状态摘要
session中的方法只改变对象的状态,不负责发送SQL/默认提交事务时发送SQL,那么前面三个问题就可以解决了。
问题一解答:se方法仅仅是把临时状态的对象转换为持久化状态,本身不负责发送SQL。临时状态的对象没有OID,调用se方法之后,变成持久化状态,就必须有OID。 * native:表示数据库主键的自增长,只有发送SQL,才能获取主键,从而获取OIDincrement:先发送SELECT语句查询id(拥有了OID),不需要发送increment来获取OID问题二解答:delete方法仅仅是改变对象的状态,本身不负责发送SQL。因此按照默认的方式,提交事务的时候发送SQL问题三解答:通过get查询操作得到的对象处于持久化状态(有OID,存在于一级缓存中)。此时,修改了非IOD的属性值,发现一级缓存中的数据和快照区域的数据不同(脏数据),Hibernate就会做比较(一级缓存和快照区),发现不同,就发送UPDATE语句,做数据同步。session的flush方法,负责把一级缓存中的脏数据同步到数据库中去
L2高速缓存
为了理解二级缓存,我们必须知道什么是一级缓存。在介绍L1缓存之前,让我们回顾一下会议。
会议
session对象,通过sessionFactory对象创建而来,包含了connection对象,封装了很多操作方法session不是线程安全的(使用局部变量),所以,session的最大生命周期:一个线程,在web应用当中,一个session的最大生命周期:requestsession中有一个缓存,称为一级缓存。存放当前工作单元加载的对象。在一个session的生命周期之内,连续拿相同类型,相同ID的对象,只需要发送一次SQL
原理如图所示:
一级缓存
虽然一级缓存可以提高性能,但是因为会话的范围有限,提高的性能也非常有限,所以这就引出了二级缓存的概念:
L2高速缓存
在整个应用中,有且只需要一个sessionFactory对象即可生命周期为整个应用的缓存(二级缓存是sessionFactory上的缓存,能提供整个应用中所有的session使用)所有的get,load方法,总是先查一级缓存,再查二级缓存,如果都没有,在去数据库里面查询
想了解Hibernate和Mybatis的缓存对比,可以戳这里Hibernate和Mybatis缓存(http://www . jinshu . com/p/Fe 4d 82 c8 c 97 c)。
事务并发问题
当事务并发时,有两种更新丢失,如下图所示:
第一类丢失更新:A事务撤销时,把已经提交的B事务的更新数据覆盖了。
第一类丢失更新问题
第二类丢失更新:A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。
第二种丢失更新的问题
但是,有两种解决方案,一种叫做悲观锁,另一种叫做乐观锁。
悲观锁:悲观的认为别人每次得到数据都会修改数据,所以自己每次得到数据都会锁,所以别人会一直阻止,直到自己得到锁。选择底层…..求更新。
悲观锁
乐观锁:我是乐观的,每次去取数据都不会被修改,所以不会锁,但是更新的时候会判断这段时间别人有没有更新这个数据,可以用版本号之类的机制。
乐观锁定
要在Hibernate中使用乐观锁定,建议使用版本模式:
最后,我是一个从事开发多年的Ja老程序员。我辞掉了工作,现在正在做自己的Ja个人定制课程。今年年初,我花了一个月的时间整理了一个最适合2019年学习的干货Ja学习产品,可以送给每一个喜欢Ja的伙伴。想要获取,可以关注我的头条号,后台01私信我,免费获取。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。