点击上方"java全栈技术"关注,每天学习一个java知识点,喜欢的也可以关注微信公众号"ITeye"
引题
对象关系映射(Object Relational Mapping,简称ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的规范,简单的说,ORM是通过使用描述对象和数据库之间映射的元数据,将java程序中的对象自动持久化到关系数据库中.
ORM框架在前后端领域都能看到它的影子,比如Android的greenDAO,比如iOS的coreData,比如Node.js的mongoose,这里主要讲解Java中Hibernate我们比较容易忽略和重要的点
save和get的执行流程
save:
根据对象找到user类(user.getClass)和对应的映射文件User.hbm.xml,并解析出表名t_user使用内省机制操作user对象,获取其中的属性:id/name,解析映射文件,找到属性对应的列名根据主键生成策略,如果是native,此时主键就不出现在SQL语句中,如当前的SQL语句为:insert into t_user(uname) values (?)其中?就对应user.getXxx()方法get:
根据对象找到user类(user.getClass)和对应的映射文件User.hbm.xml,并解析出表名t_user解析映射文件,找到元素对应的列名uid,SQL就拼接成功了处理结果集,把一条数据封装成User对象创建User对象根据列名找到对应的属性名,调用user.setXxx()后返回对象get和load方法的区别
先看这段简单的测试代码
持久化对象的生命周期
为什么需要关注持久化对象的生命周期?那我们来回忆使用Hibernate中是否遇到的三个问题
问题一: 主键生成策略不同,save操作时发生INSERT语句的时机不同?native: 在执行save方法的时候发送INSERT SQL,increment: 在提交事务的时候,才发送INSERT SQL问题二: 删除对象的时候,没有立刻发生DELETE语句,而是在提交事务的时候发送的.问题三: 为什么在事务环境下,通过get方法得到的对象,只要修改了属性值,会发生UPDATE语句.那么SQL的执行时机和什么有关系呢?和对象的状态有关系.那持久化对象的状态有哪一些?怎么划分的?
划分的规则:
对象状态的总结
session中的方法仅仅只是改变对象的状态,不负责发送SQL/默认情况下事务提交的时候发送SQL,那么之前是三个问题就可以迎刃而解了.
问题一解答: :save方法仅仅是把临时状态的对象转换为持久化状态,本身不负责发送SQL.临时状态的对象没有OID,调用save方法之后,变成持久化状态,就必须有OID. * native: 表示数据库主键的自增长,只有发送SQL,才能获取主键,从而获取OID。increment: 先发送SELECT语句查询id(拥有了OID),不需要发送increment来获取OID.问题二解答: delete方法仅仅是改变对象的状态,本身不负责发送SQL.因此按照默认的方式,提交事务的时候发送SQL问题三解答: 通过get查询操作得到的对象处于持久化状态(有OID,存在于一级缓存中).此时,修改了非IOD的属性值,发现一级缓存中的数据和快照区域的数据不同(脏数据),Hibernate就会做比较(一级缓存和快照区),发现不同,就发送UPDATE语句,做数据同步.session的flush方法,负责把一级缓存中的脏数据同步到数据库中去.二级缓存
要了解二级缓存,我们就必须知道一级缓存是什么.介绍一级缓存之前,我们先回顾一下Session
session
session对象,通过sessionFactory对象创建而来,包含了connection对象,封装了很多操作方法.session不是线程安全的(使用局部变量),所以,session的最大生命周期:一个线程,在web应用当中,一个session的最大生命周期:requestsession中有一个缓存,称为一级缓存。存放当前工作单元加载的对象.在一个session的生命周期之内,连续拿相同类型,相同ID的对象,只需要发送一次SQL原理如图:
虽然一级缓存可以提高性能,但是由于session的作用域有限,因此,提高的性能也是非常有限的.所以这就引出了二级缓存的概念
二级缓存
在整个应用中,有且只需要一个sessionFactory对象即可生命周期为整个应用的缓存(二级缓存是sessionFactory上的缓存,能提供整个应用中所有的session使用)所有的get,load方法,总是先查一级缓存,再查二级缓存,如果都没有,在去数据库里面查询事务并发问题
事务并发时,会产生两类丢失更新问题,如图
第一类丢失更新: A事务撤销时,把已经提交的B事务的更新数据覆盖了.然而解决的办法有两个,一个称之为悲观锁,一个称之为乐观锁
悲观锁(Pessimistic Lock): 悲观地认为每次自己去拿数据的时候别人会修改数据,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。底层采用的就是SELECT ..... FOR UPDATE
乐观锁(Optimistic Lock): 乐观地认为每次去拿数据的时候别人不会修改数据,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。
在hibernate中使用乐观锁,推荐使用version方式:
链接:https://www.jianshu.com/p/44f641fd8363