Hibernate 中的一级缓存
此 Hibernate教程 分为4部分,是本人看视频总结的,有视频里老师总结的,也有自己总结的,和一些本人的看法。如果错误,感谢大家的指正。视频是 Java Web 一整套的,如果有人需要我可以发给他,我的 QQ: 1666563579
若有转载请指明出处: http://blog.csdn.net/hi_bigguy
目录
1。 Hibernate 初体验
2。api 的介绍
一. Hibernate 初体验
1。 实现的效果:通过面向对象的思维方式实现数据库的操作
@Test
public void test01(){
// Configuration conf = new Configuration();
// 一定不要写成这样,否则会 org.hibernate.HibernateException: 'hibernate.dialect' must be set when no Connection available
Configuration conf = new Configuration().configure(); // 这种方式比较特殊,牢记
SessionFactory sessionFactory = conf.buildSessionFactory();
Session session = sessionFactory.openSession();
Student s = new Student();
s.setName("HB");
Transaction tran = session.getTransaction(); // 得到事务
tran.begin(); // 开启事务
session.save(s); // 会打印 sql 语句将s对象的数据作为一行插入到数据库中
tran.commit(); // 提交事务
session.close(); // session 和 sessionFactory 必须关闭
sessionFactory.close();
/*
* 注意细节 : 如果插入或者是更新数据的时候,发现打印了 相应的 sql 语句,但是数据库并没有得到相应的数据,考虑是否开启事务,并提交
*/
}
想想如果通过上面的代码就可以把数据插入到数据库中是不是很方便呢,Hibernate 是基于面向对象的思维操作数据库,所以直接用 session.save(obj) 就可以插入数据
需要说明的是 本人使用的 hibernate 是 3.6.10版本的
**这是我Demo 的目录结构
2。 下面是对上面操作的配置:
首先是配置最核心的配置文件 hibernate.cfg.xml,这个文件在 hibernate-distribution-3.6.10.Final\project\etc 文件夹下
- hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory name="foo">
<!-- 以下四个是配置连接数据库必须的 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">admin</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hibernate</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- hbm2ddl.auto : 生成表结构的策略配置
update (最常用的取值):如果当前数据库不存在表结构,会自动创建表结构
如果才在该表结构,并且与实体一致,则不做修改
如果存在,表结构与实体不一致,则会修改表结构,但是会保留原来的表结构,只是新加列
create (很少) : 无论是否存在表结构,每次启动 Hibernate(项目运行)都会创建表结构
ceate-drop(极少):无论是否存在表结构,每次启动Hibernate 都会创建表结构,但是Hibernate 运行接受后,会自动删除表结构
validate(很少) : 不会自动创建表结构,也不会自动维护表结构,只是校验表结构,当表结构与实体配置不一致的时候,会抛出异常
-->
<property name="hbm2ddl.auto">update</property>
<mapping resource="com/domain/Student.hbm.xml"/> <!-- 一定要将类的配置文件引入 -->
</session-factory>
</hibernate-configuration>
2。Student.java
package com.domain;
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + "]";
}
/*
注意:一定要实现 get(),set()方法
*/
}
需要为这个类在同包下配置一文件,文件的名字有要求,“类名.hbm.xml”
3。 Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.domain">
<class name="Student" table="t_student">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
</class>
</hibernate-mapping>
需要注意的是 上面的 dtd 文件是在
4。 测试代码在开头已经给出
二。api 的介绍
- Configuration 对象
“`
@Test
public void test02(){
// 默认加载路径是在 classpath( src 目录) 下,名字为 hibernate.cfg.xml 文件
Configuration conf = new Configuration().configure();
/*
* 如果不符合加载规则可以有如下两种加载方式 :
* 1.1. 通过File
* Configuration conf = new Configuration().configure(File arg);
*
* 1.2. 通过路径加载
* Configuration conf = new Configuration().configure(path);
*/
/*
* 2.1 还可以用 Configuration 对象加载映射文件(Student.hbm.xml) ,但是不推荐,麻烦,并且需要加载多次
* 推荐在 hiberntae.cfg.xml 使用 mapping 引入配置文件
*
* 2.2 ORM 映射文件的规范
* 1. orm 文件名与实体名相同
* 2. 与实体类在同包下
*
*
*/
conf.addClass(Student.class); // 相当于在 hibernate.cfg.xml 文件中引入 student.hbm.xml 一样
SessionFactory sessionFactory = conf.buildSessionFactory();
Session session = sessionFactory.openSession();
// 只是测试,这里不写代码也可以,只要不报错,说明没有问题
sessionFactory.close(```````````````````
;
session.close();
}
“`
(1)、快照是数据的副本
(2)、快照属于一级缓存
(3)、快照是在堆内存中的
(4)、快照的作用:保证数据一致性
当执行`session.getTransaction().commit()时,Hibernate同时会清理session的一级缓存(flush),也就是将堆内存中的数据与快照中的数据进行对比,如果不一致,则会执行同步(update)操作,若相同,则不执行update。
举个栗子
数据库中有一条数据:
测试代码:
public void testGet(){
//获取session 对象
Session session = HbnUtils.getSession();
//开启事务
session.beginTransaction();
try {
//执行get操作
Student student = session.get(Student.class, 1);
student.setName("王五");
System.out.println(student);
//提交事务
session.getTransaction().commit();
} catch (Exception e) {
//回滚事务
session.getTransaction().rollback();
e.printStackTrace();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
日志信息
select
student0_.t_id as t_id1_0_0_,
student0_.t_name as t_name2_0_0_,
student0_.t_age as t_age3_0_0_,
student0_.t_score as t_score4_0_0_
from
t_student student0_
where
student0_.t_id=?
Student [id=1, name=王五, age=20, score=89.9]
Hibernate:
update
t_student
set
t_name=?,
t_age=?,
t_score=?
where
t_id=?
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
分析
我们都知道,执行了get方法之后,DB中的数据就加载到session缓存中来了,而执行student.setName("王五")本来应该只是改变了session缓存(堆内存)中的数据,为什么数据库的数据也改变了。
Student student = session.get(Student.class, 1);`
再来解剖一下
1)、将数据从DB中取出来
2)、将数据转变成对象,并存入堆内存中
3)、将对象的id放入session缓存map的key中,将对象的引用放入session缓存map的 value中,这就纳入session管理了
4)、将对象的详情放入到“快照”中
当执行了`session.getTransaction().commit();时,Hibernate为了保证数据的一致性,Hibernate会清理session的一级缓存(flush),也就是将堆内存中的数据(已经纳入session管理的数据)与快照中的数据进行对比,如果不一致,则会执行同步(update)操作,若相同,则不执行update。
由于在commit()前,我们执行了student.setName("王五");
导致堆内存中是数据与快照中的数据不一致,所以它执行了update,以便保证数据的一致性。
为什么需要快照
通过上面的分析知道了快照的根本作用是保证数据一致性,保证数据一致的另一种做法是,commit之前把堆内存中的数据直接与数据库中的对应记录进行对比,显而易见这样的效率是灰常低下的,而采用快照技术,因为快照是一定和数据库中记录一致的,快照也在堆内存中,所以速度不是一般的快。