一、持久化类的编写规范
1、持久化类
持久化类: 与表建立了映射关系的实体类,就可以称之为持久化类。
= java类+配置文件(ex:类.java+类.hbm.xml)
2、Hibernate对持久化类的要求规范:
- 1 需要提供一个无参的构造方法 hibernate是通过无参构造器来实例化对象的
- 2 所有属性都要私有化
- 3 需要对外提供set/get方法
- 4 基本类型(int)和包装类型(integer),建议全部包装类型。默认值不同的,包装类默认值null,基本数据类型默认0
- 5 持久化类不能被final修饰,否则hibernate的优化手段就失效,即延迟加载就会失效变成立即加载
原因:load查询返回的是代理对象,而这个代理对象是持久化类的子类生产的,final修饰后不能再产生子类 - 6 持久化类必须要有标识属性(OID属性) :Hibernate用来判断对象是否是同一个
标识属性(OID属性):就是与表中主键建立映射关系的属性。
作用: 让hibernate来区分对象是否是同一个。如果多个对象的标识属性值相同,hibernate会认为是同一个对象,存储数据会 报错。
ps: 如果一个类与一张表做了映射 我们可以把这个类称为持久化类。
如果一个类中的属性和一张表中的主键字段做了映射,我们可以把这个属性称为标识属性或则OID属性。
只有增删改和主键查询(OID查询),Hibernate会主动生成SQL,其他查询需要自己手写SQL。
标识属性(OID属性)的作用在哪里? 常用于面试题
标识属性: Object Identifie 简称OID属性
作用: hibernate根据IOD属性来区分多个对象是否是同一个对象
ps:每个技术对于多个对象是否为同一个对象的判断凭据不同
虚拟机jvm: 是根据内存地址
如果内存地址相同 多个对象它会认为是同一个
数据库: 是根据主键
如果主键相同 多个对象它会认为是同一个Hibernate:是根据oid
如果oid相同 多个对象它会认为是同一个结论是: Hibernate必须得有一个oid属性,而且这个oid属性还不能相同。一般通过主键生成策略来生成oid属性。
问题: 怎么保证每个对象oid属性的不同?
使用Hibernate提供的一门技术: 主键生成策略
二、主键生成策略
1、区分自然主键和代理主键
- 1、自然主键:创建表的时候,使用的是实体类中的自身属性作为表的主键。
例如: 创建一张人员表,可以用自身的身份证号(唯一不可重复的)作为主键 - 2、代理主键:创建表的时候,使用的不是实体类中的自身属性作为主键,而是创建一个不相关的字段作为主键。
例如: 创建一张人员表,不使用自身的身份证号(唯一不可重复的)作为主键,用另外创建一个id作为主键
***** 尽量使用代理主键。使用自然主键,如果主键需要参与到业务逻辑中,有可能需要改变很多的设计和代码。
2、Hibernate的主键生成策略
主键不应该由用户自己维护,应该统一管理,那么Hibernate就提供了主键统一管理的方式:主键生成策略。
- 1、increment:由Hibernate维护。自动增长,适用于short、int、long类型的主键,使用的是Hibernate中提供一种自动增长方式,不是数据库中的自动增长。
原理:首先使用select max(CUST_ID) from T_CUSTOMER;将这个最大值+1作为下一条记录的主键。
缺点:对于并发访问数据库不适用 - 2、identity:由Hibernate维护。自动增长,适用于short、int、long类型的主键,使用的是数据库自动增长机制,数据库自动解决多线程问题。如:MySql、db2。不是所有数据库都支持自动增长,比如Oracle没有自动增长。
- 3、sequence:由Hibernate维护。序列,适用于short、int、long类型的主键,使用序列方式完成主键的生成。必须是支持序列方式的数据库。如:Oracle、db2
- 4、native:由Hibernate维护。本地策略,根据底层的数据库不同,自动选择使用identity还是sequence。
- 5、uuid:由Hibernate维护。随机字符串,适用于字符串类型的主键。
- 6、assigned:Hibernate不维护主键。开发人员需要手动设置主键。
总结: 如果主键是数值型,选择用native;如果主键是varchar类型,选择用uuid
这里多说一点:increment增长并发访问时会抛出异常:Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: 违反唯一约束条件。
这是因为:因为increment是通过“select max(CUST_ID) from T_CUSTOMER 的值+1” 来获取主键的,这个max(CUST_ID)值有可能同时被多个线程获得,但只能有一个提交,其他的就会报错。由于这个原因,increment这种方式也没人用就是了。
三、持久化类的三种状态及三种状态之间的相互转化(多用于面试)
1、三种状态
- 1、瞬时态: 当前持久化类没有oid属性,没有被session操作管理
- 2、持久态: 当前持久化类有oid属性,有被session操作管理
- 3、托管态(离线态): 当前持久化类有oid属性,没有被session操作管理
2、三种状态的相互转化
1)、瞬时态
①获得
Customer customer = new Customer();
②状态转换
瞬时-->持久:session.save()、session.saveOrUpdate()
瞬时-->托管:customer.setCust_id(1l);
2)、持久态: 只要查询操作得到的对象都是持久态对象
①获得
例:Customer customer = session.get(Customer.class,1l);
只要是涉及到查询方法直接获得持久态对象。
②状态转换
持久-->脱管:session.close()
持久-->瞬时:session.delete()
3)、脱管态
①获得
Customer customer = new Customer();
customer.setCust_id(1l);
②状态转换
脱管-->持久:session.save()、session.saveOrUpdate()
脱管-->瞬时:customer.setCust_id(null);
3、持久态可以自动更新数据库
持久态: 只要是通过查询返回的对象都是持久化类的持久态对象
特点: 持久态对象修改完属性,不需要执行更新操作,就会自动更新数据库数据
自动更新的原因: 一级缓存的存在。
一级缓存存在于session中,本质上是一堆Java集合。生命周期随session销毁被清空。可分为存储区和快照区。
具体原因或原理:我们初次执行查询操作的时候,将从数据库中的数据放到缓存的存储区中一份,快照区中一份。执行更新操作的时候,会修改缓存的存储区,快照区则不修改。执行事务提交的时候,会自动对比两个区的数据,若一致,则不处理,若不一致,Hibernate自动发送update语句来提交。
如何看到存储区和快照区的数据?可debug查看
session-->persistenceContext--->存储区entitiesByKey + 快照区entityEntryContext 的数据变化。
存储区变化图:-----》
快照区一直不变:
四、Hinbernate的一级缓存
1、概述
Hibernate框架本身的性能不是很好,但是Hibernate提供了很多的性能优化的手段,比如,延迟加载和缓存。
Hibernate框架共提供了两个级别的缓存:
一级缓存:称为Session级别的缓存。因为一级缓存的生命周期与session一致的。自带的,不可卸载的。(由Session中一系列的Java集合构成的)。一级缓存实质上就是一堆Java集合。只要session不被销毁,数据就一直存在。session是事务的。
二级缓存:称为SessionFactory级别的缓存。需要进行配置的缓存的插件。默认不开启的。 比如:ehche。现在缓存多用redis技术。
大白话:除了删除,增改查数据库后,都会往一级缓存中留一份
查询先去一级缓存查,查不到;去数据库查,查询以后还会放到一级缓存中一份
但是session只要关闭,一级缓存的数据全部清空
2、证明一级缓存的存在
3、关闭缓存的方法
- 1、session.close() session关闭 一级缓存也销毁
- 2、session.clear() clear可以清空一级缓存的所有数据
- 3、session.evict(对象) 可以清空一级缓存的某一个对象的数据