面试题:hibernate 第二天 快照 session oid 有用
## Hibernate第二天 ##
### 回顾与反馈 ###
Hibernate第一天
1)一种思想 : ORM OM(数据库表与实体类之间的映射) RM
2)一个项目 : CRM 客户关系管理系统 客户公司表
3)一个框架 : Hibernate 简化JDBC 面向对象的数据库操作
注意问题 : Hibernate简化了操作 但是牺牲了SQL的执行性能
线程问题 数据库连接放到线程
程序世界 程序运行期间 进程(一个软件运行期间驻留在内存中的一个程序) 与
进程接收到执行任务的时候 它就会创建一个线程去帮助我完成
这个任务
程序运行期间 内存里有一个进程 进程要做某些事情交给具体的线程
多线程
### 课程安排 ###
1. Hibernate的持久化类和对象标示符
2. Hibernate的一级缓存和对象状态
3. Hibernate的事务控制
4. Hiberante查询对象的API
### 重点内容 ###
1. Hibernate的持久化类和对象标示符
a. 描述Hibernate持久化类及编写规则
- 持久化类 : 持久化 某个java的普通类与表建立映射关系之后
- 规范
- 1) 持久化类需要无参的构造
- 2) 属性都是私有的 提供get set方法
- 3) 持久化类的属性都是包装类型 null int(0) Integer(null)
- 4) 持久化类中必须有一个属性与主键字段对应
- 5) implements Serializable
- JavaBean : Bean(豆角)-->封装思想 Java 玩积木
- Serializable 序列化 java对象-->保存到硬盘里的技术
- OID 表的主键ID --> 程序里被标记成<id>标签里面的属性
- 1) Hibernate用于区分两个对象的唯一途径
- 2) 对象标示符可以帮助session完成查询性能的优化
b. Hibernate中常用的主键生成策略
- 数据库主键
- Grade g_id g_name g_desc
- 一个汉字 2 字节 一个数字 1字节
- 主键要求 唯一
- 自然主键 : g_name(业务中的某个字段拿来当主键) 省了一个字段
- 代理主键(推荐) : 不参与真实业务的字段 只为数据库方便管理的主键
- Hibernate主键自增策略:
- native : 让数据库自己决定主键如何生存适合于能主键自增的数据(mysql sqlserver)
- sequence : 使用序列来生成主键,有的数据库不支持主键自增(Oracle)
- identity : 采用数据库的主键自增来生成主键(mysql,sqlserver,DB2)
- uuid : 根据128位数据算法生成32为的一个字母+数字的字符串用来标识主键
- uuid : 当你一个程序使用两个数据库的时候必须使用UUID作为同名表的主键ID; 数据1 数据2 userinfo (1 2 3 4) (1 2 3 4)
- 整理Hibernate的时候需要注意 : jar包 配置文件
2. Hibernate的一级缓存和对象状态
a. 能够描述Hibernate的一级缓存
- 缓存 : 临时存储硬盘数据到内存中的一项技术;
- 程序运行在内存里 所以当读取数据的时候 内存读内存 内存读硬盘快
- redis(2000)
- Hibernate为了提升查询性能也进行了缓存操作-->Session
- Session的一级缓存 是Hibernate用于进行数据库与程序之间沟通的一个桥;
- 当程序完成持久化操作的时候Hibernate会通过Session把该对象与相对应的数据库中的数据关联到一起;
- 快照机制 : 说明了session不光可以缓存数据库中的数据,还可以管理数据库中的数据,但是要注意快照机制只能简化更新操作;
- 缓存与快照机制保证了数据库的简化及性能提升操作;
b. Hibernate持久化对象的三种状态及特征
- Hibernate管理数据库表数据库的时候有个问题
- Java 类的对象
- Hibernate Session 管理的对象
- Hibernate管理Java对象的时候因为根据数据库数据对应于session管理不同分为三种状态
- 持久化 : 数据库有数据对应 被session管理
- 瞬时对象 : 数据库没有数据对应 session不管理
- 游离状态 : 数据库有数据对应 但是session不管理
- 操作 :
- 1) 添加的一定是瞬时的!
- 2) 更新和修改的一定是持久的!
它是hibernate用于区分两个对象是否是同一个对象的标识。
我们都知道,虚拟机内存区分两个对象看的是内存的地址是否一致。数据库区分两个对象,靠的是表的主键。hibernate负责把内存中的对象持久化到数据库表中,靠的就是对象标识符来区分两个对象是否是同一个。实体类中映射主键的字段就是OID
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
Hibernate的一级缓存有如下特点:
l 当应用程序调用Session接口的save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
l 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
l 当调用Session的close()方法时,Session缓存会被清空。
1.1.1 快照机制:
Hibernate 向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致
1.1 对象的三种状态
1.1.1 对象的状态说明:
了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态的详细介绍如下。
1、 瞬时态(transient)
瞬时态也称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中是孤立存在的,与数据库中的数据无任何关联,仅是一个信息携带的载体。
2、 持久态(persistent)
持久态的对象存在持久化标识OID ,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
3、 脱管态(detached)
脱管态也称离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
1.1.2 学习对象状态我们要明确的:
a、是为了更好的掌握hibernate中操作的方法。
b、区分状态只有两个标识
一是否有OID
二是否和Session建立的关系
临时状态:
没有OID,和Session没有关系。
持久化状态:
有OID,和Session有关系。
脱管状态:
有OID,和Session没有关系。
3. Hibernate的事务控制
a. Hibernate进行事务控制
- 事务四大特性 多线程环境下事务并发问题 脏读 .....
- 事务隔离级别的设置
- 读未提交 最危险 事务操作最快 1
- 读已提交 比较适合 2
- 可重复读 比较笨拙 4
- 串行读 最安全 事务操作最慢 8
如果一旦出现事务隔离级别的设置 肯定涉及到多线程并发操作
JavaSE开发 单线程
b. 线程与session绑定的时候
1) 自定线程类与session绑定
private static ThreadLocal<Session> sessionTL = new ThreadLocal<Session>();
public static Session getSession(){
// 创建方法返回值
Session session = null;
// 1. 放到线程里 2.C3P0
// 当前线程先获取一下
session = sessionTL.get();
if(null == session){
session = factory.openSession();
// 把session放入当前线程
sessionTL.set(session);
}
// 返回
return session;
}
特点 : 查询方法不用加事务 但是编写其来麻烦
2) Hibernate默认绑定
<property name="hibernate.current_session_context_class">thread</property>
public static Session getCurrentSession(){
// 使用hibernat默认的session与线程绑定
return factory.getCurrentSession();
}
特点: 编写简单,但是所有的方法都必须加上事务才可以运行
4. Hiberante查询对象的API
X. 问题:不能查询所有
a. Query对象的作用及常用的方法
- HQL查询 : Hibernate query la...
- HQL查询 : Hibernate还是属于一种SQL查询;保留SQL结构,查询表被替换成类,查询字段被替换成属性;
- query查询 query对象 -->离开session
b. Criteria对象的作用及常用的方法
Criteria 可以离开session独立生存
### 课后总结 ###
作业: Hibernate+servlet完成一张表的CURD 分页
封装: 三层开发
1.1.1 Query中的方法说明:
l list方法:该方法用于查询语句,返回的结果是一个list集合。
l uniqueResult方法:该方法用于查询,返回的结果是一个Object对象。
l setter方法:Query接口中提供了一系列的setter方法用于设置查询语句中的参数,针对不同的数据类型,需要用到不同的setter方法。
l uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
l setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult()方法结合使用,用于限制结果集的范围,以实现分页功能。
Long total = (Long)query.uniqueResult();//返回的是一个唯一的结果集。 只有确定结果集唯一时,才能使用
1.1.1.1 统计查询
/**
* QBC使用聚合函数
* 统计查询
* 涉及的对象:
* Criteria
* 涉及的方法:
* setProjection(Projection p);
* 参数的含义
* Projection:要添加的查询投影
*/
@Test
public void test2(){
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//1.获取对象
Criteria c = s.createCriteria(Customer.class);//from Customer | select * from cst_customer
//2.想办法把select * 变成 select count(*)
// c.setProjection(Projections.rowCount());//select count(*)
c.setProjection(Projections.count("custId"));//select count(cust_id)
//3.获取结果集
// List list = c.list();
// for(Object o : list){
// System.out.println(o);
// }
Long total = (Long)c.uniqueResult();
System.out.println(total);
tx.commit();
}
1.1.1.2 离线查询
/**
* 离线条件查询
* 离线:
* 它是和在线对应的。
* Criteria对象是一个在线对象,它是由一个可用的(活动的)Session对象获取的出来的。
* 当session失效时,就无法再获取该对象了。
* 有一个对象,它也可以用于设置条件,但是获取的时候并不需要Session对象。
* 该对象就叫做离线对象:
* DetachedCriteria对象
* 使用该对象进行的查询就叫做:离线查询
*
* 如何获取该对象
* DetachedCriteria dCriteria = DetachedCriteria.forClass(要查询的实体类字节码);
*
*/
@Test
public void test3(){
//模拟一次web操作: 浏览器发送请求——调用servlet——调用service——调用dao——拿到结果到jsp上展示
List list = servletFindAllCustomer();
for(Object o : list){
System.out.println(o);
}
}
//模拟servlet
public List<Customer> servletFindAllCustomer(){
//离线对象
DetachedCriteria dCriteria = DetachedCriteria.forClass(Customer.class);
//设置条件:和Criteria是一样的
dCriteria.add(Restrictions.like("custName","%集%"));
return serviceFindAllCustomer(dCriteria);
}
public List<Customer> serviceFindAllCustomer(DetachedCriteria dCriteria) {
return daoFindAllCustomer(dCriteria);
}
public List<Customer> daoFindAllCustomer(DetachedCriteria dCriteria) {
Session s = HibernateUtil.getCurrentSession();
Transaction tx = s.beginTransaction();
//把离线对象使用可用Session激活
Criteria c = dCriteria.getExecutableCriteria(s);
List<Customer> list = c.list();
tx.commit();
return list;
}