hibernate笔记
2020-05-29 14:49:58 1 举报
AI智能生成
hibernate
作者其他创作
大纲/内容
初识hibernate
1. 前言
(1)ssh框架体系结构
分支主题
SSH框架集,是现在大多数软件设计过程中都会使用的一种框架集。而这种框架是基于MVC的开发,且MVC模式已经成为现代J2EE开发中的一种常用模式,且受到越来越多JSP、PHP等开发者的欢迎。MVC模式,分别是模型(Model)、视图(View)、控制器(Controller)。
应用程序被分割成这三大部分之后,各自处理自己的任务。视图层通过提取用户的输入信息,提交到控制器之后,控制器根据某种选择来决定这个请求交由给模型层来处理,模型层根据业务逻辑的代码处理用户请求并返回数据,并最终用视图层展示给用户。
而前面提到的SSH框架集,就是很好的对应了MVC模式的开发使用。这种Struts+Hibernate+Spring的三大框架整合,契合着MVC模式的三层对象。其中Struts/springMVC对应着前台的控制层,而Spring则负责实体bean的业务逻辑处理,至于Hibernate/ Mybatis则是负责数据库的交接以及使用Dao接口来完成操作。
(2)持久化
分支主题
(3)持久化层
JDBC就是持久化机制
将数据程序直接保存成文本文件也是持久化进制的一种实现
在分层结构中,Dao层(数据访问层)也被称为持久化层
(4)持久化完成的操作
将对象保存到关系型数据库中
将关系型数据库中的数据读取出来以对象的形式封装
(5)ORM(Object Relational Mapping,对象关系映射)
作用
就是在关系型数据库和对象之间做了一个映射。从对象(Object)映射到关系(Relation),再从关系映射到对象。
总结
编写程序的时候,以面向对象的方式处理数据
保存数据的时候,却以关系型数据库的方式存储
2. Hibernate简介
Hibernate是一个基于元数据的轻量级的ORM框架
1,元数据(Meta Data):data about data(数据的数据),也就是说描述一个对象数据,相当于这个对象的上下文环境。
2,轻量级:占用资源少,没有侵入性。(其实我认为这只是相对而言,如果和Mybatis相比,它到成重量级的了)。
3,ORM:(Object Relation Mapping)对象关系数据库的映射
这是Hibernate框架的重点,也就是说将我们程序中的实体(bean,这里也叫POJO)和数据库中的表进行映射。java类型和sql类型映射,面向对象设计和关系型数据库设计的映射,从而我们只需要将更多的精力放到业务中,而不是sql数据库方面。
4,POJO:(Plain Ordinary Java Object),无格式的普通java对象,也就是上边说的实体,和数据库做映射的简单类。只是在这里提出了一个新的名词而已。
总而言之,Hibernate就是将我们的数据库表和程序的POJO类进行映射,数据的操作进行了封装,使我们不用把数据库弄得非常精通,我们会面向对象编程就可以了,这样大大提高了我们的编程效率,而且对个人的知识要求也降低了。(Hibernate的设计目的是这样的,可是我觉的要想更好的应用Hibernate,更好的掌握开发的来龙去脉,这是要求我们要有更高的能力了,知其然知其所以然了)
3.Hibernate优点缺点
优点
1、更加对象化 --
以对象化的思维操作数据库,我们只需要操作对象就可以了,开发更加对象化。
2、移植性
因为Hibernate做了持久层的封装,你就不知道数据库,你写的所有的代码都具有可复用性。
3、Hibernate是一个没有侵入性的框架,没有侵入性的框架我们称为轻量级框架。
对比Struts的Action和ActionForm,都需要继承,离不开Struts。Hibernate不需要继承任何类,不需要实现任何接口。这样的对象叫POJO对象。
4、Hibernate代码测试方便。
5、提高效率,提高生产力。
缺点
1、使用数据库特性的语句,将很难调优
2、对大批量数据更新存在问题
3、系统中存在大量的攻击查询功能
4.Hibernate与mybatis的区别和应用场景
应用场景
hibernate
标准的ORM(对象关系映射) 框架;
不要用写sql, sql 自动语句生成;
使用Hibernate 对sql 进行优化,修改比较困难
应用场景: 试用需求,变化固定中小型项目;ERP,ORM,OA
mybatis
程序员自己编写sql, sql 修改,优化比较自由。
mybatis 是一个不完全的ORM 框架(部分), mybatis 存在映射关系(输入,输出映射)
应用场景: 除了hibernate 的场景,主要应用需求项目较多的场景,互联网项目; 敏捷开发。
当希望对象的持久化对应用程序完全透明是,不适合使用mybatis
当数据库有移植需求或需要支持多种数据库时,不适合使用mybatis
映射关系描述
hibernate
对数据库结构提供了较为完整的封装,hibernate 的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行。
只需定义好了POJO 到数据库表的映射关系,即可通过提供的方法完成持久层操作。
不需要对SQL 的熟练掌握, 会根据制定的存储逻辑,自动生成对应的SQL 并调用JDBC 接口加以执行。
mybatis
POJO SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。 相对“O/R”而言,iBATIS 是一种“Sql Mapping”的ORM实现。
数据库的无关性
hibernate 的 具体数据只需要利用xml 进行关联; HQL 语句与数据库无关
mybatis 更接近sql ,因此对数据库的依赖较多。
hibernate
Hibernate的DAO层开发比MyBatis简单,Mybatis需要维护SQL和结果映射。
Hibernate对对象的维护和缓存要比MyBatis好,对增删改查的对象的维护要方便。
Hibernate数据库移植性很好,MyBatis的数据库移植性不好,不同的数据库需要写不同SQL。
Hibernate有更好的二级缓存机制,可以使用第三方缓存。MyBatis本身提供的缓存机制不佳
mybatis
MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
MyBatis容易掌握,而Hibernate门槛较高。
总结
Hibernate的ORM实现更加完善
完全面向对象,开发者无需关注SQL的生成
使用自由度不如直接使用SQL语句的MyBatis
使用Hibernate
一、环境搭建
分支主题
(1)与之间的项目一样,创建一个maven项目,可以参考之前的项目搭建笔记
(2)导入hibernate需要依赖的Jar包
POM.XML配置文件如下
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--日志包-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
<scope>compile</scope>
</dependency>
<!--数据库连接包-->
<!-- https://mvnrepository.com/artifact/com.oracle/ojdbc6 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4.0-atlassian-hosted</version>
<scope>compile</scope>
</dependency>
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.version}</version>
</dependency>
(3)添加hibernate的文件
选择Facets,后选择+号,再选择hibernate
选中后,选中当前项目,点击OK
ok后再选择+号,进行添加
选择版本号,这里建议使用推荐的4.x版本的就行
点击ok后,这时候项目下的resouces下会多出来一个hibernate.cfg.xml的文件。
(4)对hibernate的文件进行设置,主要是数据库的关联,以及与bean的关联操作
连接Orable数据库时,先写个测试类,测试连接
public class OracleJdbc {
@Test
public void testOracle() {
Connection conn = null;// 创建一个数据库连接
PreparedStatement ps = null;// 创建预编译语句对象,用这个不用Statement
ResultSet resultSet = null;// 创建一个结果集对象
try {
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序,不用DriverManager
String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl";// Oracle地址
String user = "scott";// 数据库用户名
String password = "123456";// 数据库密码
conn = DriverManager.getConnection(url, user, password);// 获取连接
String sql = "select * from emp ";// 预编译语句,“?”代表参数
ps = conn.prepareStatement(sql);// 实例化预编译语句
//ps.setString(1, "小李");// 设置参数,前面的1表示第一个问号(第二个问号就用2)
resultSet = ps.executeQuery();// 执行查询
while (resultSet.next()) {// 当结果集不为空时
System.out.println(resultSet.getString("ename"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 从小到大释放. resultSet < Statement < Connection
// 释放时调用close方法即可. 如果其中一个对象的关闭 出现了异常. 也要保证其他的对象关闭方法被调用.
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (ps != null) {
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
}
容易出现的错误如下
ORA-28000: the account is locked - 账户已经被锁定
解决:通过system用户登录修改
alter user SCOTT account unlock;
ORA-28001: the password has expired - 密码过期
解决:通过system用户登录修改
alter user SCOTT identified by 123456;
修改如下:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!--<property name="connection.url"/>
<property name="connection.driver_class"/>-->
<!-- <property name="connection.username"/> -->
<!-- <property name="connection.password"/> -->
<!-- DB schema will be updated if needed -->
<!-- <property name="hbm2ddl.auto">update</property> -->
<!-- Oracle用户名 -->
<property name="hibernate.connection.username">scott</property>
<!-- Oracle密码 -->
<property name="hibernate.connection.password">123456</property>
<!-- Oracle驱动 -->
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<!-- Oracle连接URL -->
<!--建议:Oracle的url最好是这样写上,如果要连接远程Oracle,只需要把127.0.0.0.1的改为远程Oracle地址即可-->
<property name="hibernate.connection.url">jdbc:oracle:thin:@127.0.0.1:1521:orcl</property>
<!-- 数据库方言 -->
<!--<property name="dialect">org.hibernate.dialect.MySQLDialect</property>-->
<!--// 5.0之后使用的数据库方言-->
<!--<property name="dialect">org.hibernate.dialect.MySQL5Dialect</property>-->
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<!-- 显示sql语句 -->
<property name="show_sql">true</property>
<!-- 格式化sql语句 -->
<property name="format_sql">true</property>
<!-- 根据需要创建数据库 (这里的操作是,如果数据库中已经有这个表,就将这个表删除掉,重新创建表格)-->
<!--<property name="hbm2ddl.auto">create</property>-->
<!--添加刚刚生成的映射类-->
<!-- <mapping class="com.hibernatedemo.dao.TbAreaEntity"></mapping>-->
</session-factory>
</hibernate-configuration>
(5)使用intellij idea的database数据库进行连接数据库。
填写账号,密码,ip地址,数据库名等
(6)生成持久化对象
persistence
数据库标识xml文件
generate persistence mapping - by database schema
(7)在hibernate.cfg.xml中进行类配置
<!--添加刚刚生成的映射类-->
<mapping class="com.hibernate.entity.EmpEntity"></mapping>
(8)创建TestEmp测试类,并进行测试。
/**
* description: TestEmp <br>
* date: 2019/8/19 3:29 <br>
* author: GHL <br>
* version: 1.0 <br>
*/
public class TestEmp {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
// 测试开始
@Before
public void init(){
//创建会话工厂
Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
// 开启会话
session =sessionFactory.openSession();
//开启事物
transaction = session.beginTransaction();
}
// 测试结束
@After
public void destory(){
//关闭事物
transaction.commit();
//关闭对话
session.close();
// 关闭会话工厂
sessionFactory.close();
}
// 下面是测试用例
@Test
public void testStudent(){
EmpEntity emp = new EmpEntity();
emp.setEmpno(7938);
emp.setComm((long) 500);
emp.setEname("张三");
emp.setHiredate(new Time(new Date().getTime()));
emp.setJob("CLERK");
emp.setMgr((long) 7902);
emp.setSal((long) 7500);
emp.setEmpno(10);
session.save(emp);
}
}
二、配置Hibernate
1.hibernate.cfg.xml 核心配置文件
必须的配置
连接数据库的基本参数
驱动类 com.mysql.jdbc.Driver
url路径 jdbc:mysql:///项目名(三个/代表省略了项目路径)
用户名
密码
方言
可选的配置
显示sql语句 <property name="hibernate.show_sql">true</property>
格式化sql语句 <property name="hibernate.format_sql">true</property>
自动建表<property name="hibernate.hbm2ddl.auto">update</property>
none:不使用hibernate的自动建表
create:如果数据库中已经有表了,删除原有表重新创建。没有就新建。(测试)
create-drop:如果数据库中已经有表了,删除原有表执行操作,删除这个表。没有就新建,使用完了删除该表。(测试,需要关sessionFactory)
update:如果数据库中已经有表了,使用原有表。没有就新建。(可以更新表结构)
validate:如果没有表不创建,只会使用数据库中原有的表。它会校验表结构,如果表结构不对会报错。
映射文件的引入
通过< mapping>标签引入映射文件的位置
<mapping resource="com/jingt/hibernate/demo1/Customer.hbm.xml"/>
hibernate核心配置的两种方式
i 属性文件的配置
hibernate.properties
hibernate.connection.driver_class=com.mysql.jdbc.Driver
...
hibernate.connection.password=abc
属性文件的方式不能引入映射文件(手动编写代码加载映射文件)
ii xml文件的配置
hibernate.cfg,xml
通过上面核心配置的标签
2.持久化类
2.1 什么是持久化类
持久化:将内存中的一个对象持久化到数据库中的过程。hibernate框架就是用来进行持久化的框架。
持久化类:一个java对象与数据库的表建立了映射关系。
2.2 持久化类的编写规则
对持久化类提供一个***无参的构造方法*** (hibernate的底层需要用反射生成实例)
属性需要私有,对私有属性提供public的***set和get方法***(hibernate中获取、设置对象的值)
对持久化类提供***唯一标识OID与数据库主键对应***(Java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一条记录。在hibernate中通过持久化类的OID属性区分是否是同一个对象)
持久化类中的属性尽量使用***包装类类型***(基本数据类型的默认值是0,0会有很多的歧义。很难区分是否有没有赋值。包装类的类型默认值是null。)
持久化类不要使用***final***进行修饰(延迟加载本身是hibernate优化手段。返回的是代理对象(javaassist可以对没有实现接口的类产生代理——使用了非常底层的字节码的增强技术,继承这个类进行代理)。如果不能被继承,就不能产生代理对象,延迟加载失效,load()和get()相同了。)
3.映射文件
1、class标签的配置
用来建立类与表的映射关系
属性:
name :类的全路径
table :表名(如果类名和表名一致,table可以省略)
catlog :数据库名(可以不填)
2、id标签的配置
用来建立类中的属性与表中的主键的对应关系
属性:
* name :类中的属性名
* column :表中的字段名(类中的属性名和表中的字段名一直,column可以省略)
* length :长度 (用于创建映射的表)
* type :类型 (可以不用写)
property标签的配置
3、用来建立类中的普通属性与表的字段的对应关系
属性:
name :类中的属性名
column :表中的字段名
length :长度
type :类型
not-null :设置true非空
unique :设置true唯一
使用Hibernate API
1.关键接口和类
分支主题
分支主题
Configuration:Hibernate的配置对象
通过该类的对象对Hibernate进行配置,对它进行启动。在Hibernate启动过程中,Configuration类的实例首先定位映射文件的位置,读取这些配置,然后创建一个SessionFactory对象。是启动hibernate时所遇到的第一个对象。
作用:
加载核心配置文件
加载映射文件
SessionFactory:Session工厂对象
SessionFactory接口负责Hibernate的初始化和建立Session对象。用到了工厂模式。一般情况下,一个项目通常只需要一个SessionFactory,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存。是线程安全的对象。一个项目创建一个对象即可。
抽取一个工具类
public class HibernateUtils {
//Hibernate的工具类
public static final Configuration cfg;
public static final SessionFactory sf;
static {
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session openSession() {
return sf.openSession();
}
}
Session:类似Connection对象是连接对象
Session代表的是Hibernate与数据库连接的对象,是非线程安全的(必须定义在方法内部,否则会有线程安全问题)。与数据库交互的桥梁。
Session中的API
Serializable save(Object obj); //向数据库保存数据,返回可序列化的ID。
<T> T get(Class c,Serializable id); //查询对象
<T> T load(Class c,Serializable id); //查询对象
void update(Object obj); //修改对象,建议先查询再修改
void delete(Object obj); //删除对象,先查询再删除
void saveOrUpdate(); //保存或更新
get()方法和load()方法的区别:
get()方法
立即加载,执行到这行代码就会马上发送SQL语句进行查询。
返回的是真实对象本身。
查询找不到的对象时,返回null
load()方法
采用延迟加载(lazy懒加载),执行到这行代码不会发送SQL语句,到真正使用这个对象的时候才会发送SQL语句。
查询返回的是代理对象。
查询找不到的对象时,返回ObjectNotFoundException
Transaction:事务对象
Hibernate中管理事物的对象
Transaction的API
void commit(); //提交事务
void rollback(); //回滚事务
抽取工具类
public class HibernateUtils {
public static final Configuration c;
public static final SessionFactory sf;
static {
c = new Configuration().configure();
sf = c.buildSessionFactory();
}
public static Session openSession() {
Session s = sf.openSession();
return s;
}
}
2.持久化类对象的状态
hibernate是持久层框架,通过持久化类完成ORM操作。hibernate为了更好的管理持久类,将持久类分成三种状态。
持久化类 = java类 + 映射
2.1 瞬时态 transient
这种对象没有唯一的标识OID,没有被session所管理。
Customer c = new Customer(); //瞬时态对象:没有唯一标识OID,没有被Session所管理
2.2 持久态 persistent
这种对象有唯一的标识OID,被session所管理。
Serializable id = s.save(c); //持久态对象:有唯一标识OID,被session所管理
持久化类的持久态对象可以自动更新数据库
2.3 脱管态 detached
这种对象有唯一的标识OID,没有被session所管理。
s.close(); //脱管态对象:有唯一的标识OID,没有被session所管理
2.4 持久化类的状态转换
2.4.1 瞬时态对象
获得
Customer c = new Customer();
状态转换
瞬时态→持久
调用save(); / saveOrUpdate(); session,save(c);
瞬时态 →脱管
c.setCust_id(1l);
2.4.2 持久态对象
获得
get(); / load() ;Customer c = session.get(Customer.class,1l);
状态转换
持久→瞬时
delete() sesion.delete();
持久→脱管
session.close();
2.4.3 脱管态
获得
Customer c = new Customer();
c.setCust_id(1l);
转换
脱管→持久
session.update(c);
session.saveOrUpdate(c);
脱管→瞬时
c.setCust_id(null);
2.4.4 持久态对象特征
有自动更新数据库的功能(不需要session.update()),依赖于底层的一级缓存
public void run() {
Session s = HibernateUtils.openSession();
Transaction t = s.beginTransaction();
//获得持久态对象
Customer c = s.get(Customer.class, 1l);
c.setCust_name("heshibi");
//s.update(c);
t.commit();
s.close();
}
3.刷新缓存机制
3.1 缓存
一种优化方式,将数据存入在内存中,使用的时候直接从缓存中读取,不用通过数据源。
3.2 hibernate的一级缓存
hibernate框架中提供了优化手段:缓存、抓取策略。hibernate中提供了两种缓存机制:一级缓存、二级缓存。
hibernate的一级缓存被称为是Session级别的缓存,一级缓存生命周期与Session一致(一级缓存是由Session中的一系列的Java集合构成)。一级缓存是自带的不可卸载的。
二级缓存是SessionFactory级别的缓存,需要配置。在开发中不常用。
public void run() {
Session s = HibernateUtils.openSession();
Transaction t = s.beginTransaction();
Customer c1 = s.get(Customer.class,1l); //发送了SQL语句
Customer c2 = s.get(Customer.class,1l); //不发送SQL语句,因为在一级缓冲区中已经有了该对象
t.commit();
s.close();
}
什么样的数据适合存放到第二级缓存中?
1) 很少被修改的数据
2) 不是很重要的数据,允许出现偶尔并发的数据
3) 不会被并发访问的数据
4) 常量数据
不适合存放到第二级缓存的数据?
1) 经常被修改的数据
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3) 与其他应用共享的数据。
3.3 一级缓冲区的特殊结构:快照区
当更改对象的属性并且提交事务后,提交的内容会和快照区进行对比,如果一致就不更新数据库。不一致就更新数据库。
3.4 Hibernate查找对象如何应用缓存?
当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;如果都查不到,再查询数据库,把结果按照ID放入到缓存删除、更新、增加数据的时候,同时更新缓存。
3.5.一级缓存与二级缓存的对比图。
存放数据的形式
相互关联的持久化对象
对象的散装数据
缓存的范围
事务范围,每个事务都拥有单独的一级缓存
进程范围或集群范围,缓存被同一个进程或集群范围内所有事务共享
并发访问策略
由于每个事务都拥有单独的一级缓存不会出现并发问题,因此无须提供并发访问策略
由于多个事务会同时访问二级缓存中的相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别
数据过期策略
处于一级缓存中的对象永远不会过期,除非应用程序显示清空或者清空特定对象
必须提供数据过期策略,如基于内存的缓存中对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间
物理介质
内存
内存和硬盘,对象的散装数据首先存放到基于内存的缓存中,当内存中对象的数目达到数据过期策略的maxElementsInMemory值,就会把其余的对象写入基于硬盘的缓存中
缓存软件实现
在Hibernate的Session的实现中包含
由第三方提供,Hibernate仅提供了缓存适配器,用于把特定的缓存插件集成到Hibernate中
启用缓存的方式
只要通过Session接口来执行保存,更新,删除,加载,查询,Hibernate就会启用一级缓存,对于批量操作,如不希望启用一级缓存,直接通过JDBCAPI来执行
用户可以再单个类或类的单个集合的粒度上配置第二级缓存,如果类的实例被经常读,但很少被修改,就可以考虑使用二级缓存,只有为某个类或集合配置了二级缓存,Hibernate在运行时才会把它的实例加入到二级缓存中
用户管理缓存的方式
一级缓存的物理介质为内存,由于内存的容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目,Session的evit()方法可以显示的清空缓存中特定对象,但不推荐
二级缓存的物理介质可以使内存和硬盘,因此第二级缓存可以存放大容量的数据,数据过期策略的maxElementsInMemory属性可以控制内存中的对象数目,管理二级缓存主要包括两个方面:选择需要使用第二级缓存的持久化类,设置合适的并发访问策略;选择缓存适配器,设置合适的数据过期策略。SessionFactory的evit()方法也可以显示的清空缓存中特定对象,但不推荐
3.6 Hibernate的缓存机制如何应用?
1. 一级缓存的管理:
evict(Object obj) 将指定的持久化对象从一级缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。
clear() 将一级缓存中的所有持久化对象清除,释放其占用的内存资源。
contains(Object obj) 判断指定的对象是否存在于一级缓存中。
flush() 刷新一级缓存区的内容,使之与数据库数据保持同步。
2.一级缓存应用:
save()。当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。 get()和load()。当session对象调用get()或load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。 使用HQL和QBC等从数据库中查询数据。
public class Client
{
public static void main(String[] args)
{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = null;
try
{
/*开启一个事务*/
tx = session.beginTransaction();
/*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/
Customer customer1 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
System.out.println("customer.getUsername is"+customer1.getUsername());
/*事务提交*/
tx.commit();
System.out.println("-------------------------------------");
/*开启一个新事务*/
tx = session.beginTransaction();
/*从数据库中获取id="402881e534fa5a440134fa5a45340002"的Customer对象*/
Customer customer2 = (Customer)session.get(Customer.class, "402881e534fa5a440134fa5a45340002");
System.out.println("customer2.getUsername is"+customer2.getUsername());
/*事务提交*/
tx.commit();
System.out.println("-------------------------------------");
/*比较两个get()方法获取的对象是否是同一个对象*/
System.out.println("customer1 == customer2 result is "+(customer1==customer2));
}
catch (Exception e)
{
if(tx!=null)
{
tx.rollback();
}
}
finally
{
session.close();
}
}
}
结果
Hibernate:
select
customer0_.id as id0_0_,
customer0_.username as username0_0_,
customer0_.balance as balance0_0_
from
customer customer0_
where
customer0_.id=?
customer.getUsername islisi
-------------------------------------
customer2.getUsername islisi
-------------------------------------
customer1 == customer2 result is true
输出结果中只包含了一条SELECT SQL语句,而且customer1 == customer2 result is true说明两个取出来的对象是同一个对象。其原理是:第一次调用get()方法, Hibernate先检索缓存中是否有该查找对象,发现没有,Hibernate发送SELECT语句到数据库中取出相应的对象,然后将该对象放入缓存中,以便下次使用,第二次调用get()方法,Hibernate先检索缓存中是否有该查找对象,发现正好有该查找对象,就从缓存中取出来,不再去数据库中检索。
3.二级缓存的管理:
evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源。
sessionFactory.evict(Customer.class, new Integer(1));
evict(Class arg0) 将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源。
sessionFactory.evict(Customer.class);
evictCollection(String arg0) 将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源。
sessionFactory.evictCollection("Customer.orders");
4.二级缓存的配置
常用的二级缓存插件
EHCache org.hibernate.cache.EhCacheProvider
OSCache org.hibernate.cache.OSCacheProvider
SwarmCahe org.hibernate.cache.SwarmCacheProvider
JBossCache org.hibernate.cache.TreeCacheProvider
<!-- EHCache的配置,hibernate.cfg.xml -->
<hibernate-configuration>
<session-factory>
<!-- 设置二级缓存插件EHCache的Provider类-->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- 启动"查询缓存" -->
<property name="hibernate.cache.use_query_cache">
true
</property>
</session-factory>
</hibernate-configuration>
<!-- ehcache.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--缓存到硬盘的路径-->
<diskStore path="d:/ehcache"></diskStore>
<!--
默认设置
maxElementsInMemory : 在內存中最大緩存的对象数量。
eternal : 缓存的对象是否永远不变。
timeToIdleSeconds :可以操作对象的时间。
timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。
overflowToDisk :内存满了,是否要缓存到硬盘。
-->
<defaultCache maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache>
<!--
指定缓存的对象。
下面出现的的属性覆盖上面出现的,没出现的继承上面的。
-->
<cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false"
timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache>
</ehcache>
<!-- *.hbm.xml -->
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<class>
<!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional-->
<cache usage="read-write"/>
</class>
</hibernate-mapping>
若存在一对多的关系,想要在在获取一方的时候将关联的多方缓存起来,需要在集合属性下添加<cache>子标签,这里需要将关联的对象的hbm文件中必须在存在<class>标签下也添加<cache>标签,不然Hibernate只会缓存OID。
<hibernate-mapping>
<class name="com.suxiaolei.hibernate.pojos.Customer" table="customer">
<!-- 主键设置 -->
<id name="id" type="string">
<column name="id"></column>
<generator class="uuid"></generator>
</id>
<!-- 属性设置 -->
<property name="username" column="username" type="string"></property>
<property name="balance" column="balance" type="integer"></property>
<set name="orders" inverse="true" cascade="all" lazy="false" fetch="join">
<cache usage="read-only"/>
<key column="customer_id" ></key>
<one-to-many class="com.suxiaolei.hibernate.pojos.Order"/>
</set>
</class>
</hibernate-mapping>
刷新缓存:
Session是Hibernate向应用程序提供的操作数据库的主要接口,它提供了基本的保存,更新,删除和加载java对象的方法,Session具有一个缓存,可以管理和追踪所有持久化对象,对象和数据库中的相关记录对应,在某些时间点,Session会根据缓存中对象的变化来执行相关SQL语句,将对象包含的变化数据更新到数据库中,这一过程成为刷新缓存.
脏检查:
当刷新缓存时,Hibernate会对Session中的持久状态的对象进行检测,判断对象的数据是否发生了改变,这种判断成为脏检查,
脏检查的原理:
当一个对象被加入到Session缓存中时,Session会为对象的值类型的属性复制一份快照.当Session刷新缓存时,会先进行脏检查,即比较对象的当前属性与它的快照,来判断对象的属性是否发生了改变,如果发生了改变,Session会根据脏对象的最新属性值来执行相关的SQL语句,将变化更新到数据库中.
运行结果:
从结果可以看出,sql语句只生成了一次,证明第二次修改经过脏检查发现数据并没有进行改变,所以没有向数据库发送SQL语句!
缓存清理机制
当Session缓存中对象的属性每次发生了变化,Session并不会立即清理缓存和执行相关的SQL update语句,而是在特定的时间点才清理缓存,这使得Session能够把几条相关的SQL语句合并为一条SQL语句,一遍减少访问数据库的次数,从而提高应用程序的数据访问性能。
在默认情况下,Session会在以下时间点清理缓存。
1. 当应用程序调用org.hibernate.Transaction的commit()方法的时候.commit方法先清理缓存,然后再向数据库提交事务。Hibernate之所以把清理缓存的时间点安排在事务快结束时,一方面是因为可以减少访问数据库的频率,还有一方面是因为可以尽可能缩短当前事务对数据库中相关资源的锁定时间。
2. 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,就会清理缓存,使得Session缓存与数据库已经进行了同步,从而保证查询结果返回的是正确的数据。
3. 当应用程序显示调用Session的flush()方法的时候。
0 条评论
下一页