【Hibernate】从0开始学Hibernate--004(完) -- 关联映射、缓存、三层架构
向上连接到Hibernate--002 Session操作数据库、关联查询、数据存储的三种状态、脏数据与刷新缓存。
https://blog.csdn.net/hekaikai666/article/details/81913142
回顾与总结
(一)Hibernate中查询数据的三种方式
HQL语句查询(注意区分HQL和SQL语句的关系:没有半毛钱关系)
动态参数查询(静态方法很多,注意返回值和参数列表)
原生SQL语句查询(不建议用,就相当于绕了一大圈,封装好的JDBC又使用全JDBC)
(二)Hibernate中的关联查询
一对多,多对一(级联操作)
多对多
一对一
(三)脏数据与刷新缓存
一、关联映射级联关系
(一)映射级联关系
在配置文件中,可以进行关联映射配置,属性为(cascade)、值可以分为四个(none、save-update、delete、all)
none --> 默认值只操作当前对象,忽略它所关联的所有对象
save-update --> 当前对象执行save/update/saveOrUpdate会级联的去往它所关联的表中插入数据或修改数据
delete --> 对当前对象执行delete()删除,他会级联的去删除关联对象以及表中数据
all --> baohansave-updaye/delete的级联行为
(二)延迟加载
延迟加载方法(懒加载):lazy-load 在尽可能晚的情况下,当真正需要使用到数据时,才会向数据库发送sql语句执行查询加载数据到内存中.避免无谓的性能开销。其中有两个属性方式:load()延迟加载不发送SQL语句,默认返回代理对象,只保存对象的id;get()非延迟加载但是只会查询当前对象的完整信息 如果当前对象还有关联的对象或集合数据。
延迟加载属性:
1.一对多和多对一延迟加载对方集合信息。属性lazy,值为
true --> 默认值 延迟加载,只会加载当前表中数据,不会发送sql语句去加载其关联的集合信息,当真正需要使用到集合中数据时,才会发送sql语句查询;
false --> 取消延迟加载,当加载当前表中数据时,会立即发送sql语句去向其关联的表中执行查询;
extra --> 增强延迟加载,如果调用了集合中的size()/contains()方法时,不会发送查询所有数据的sql语句,而是按照需求发送一条sql语句查询所需要知道的信息
2.多对多和一对一的延迟加载对方实体信息。属性lazy,值为
false --> 取消延时加载,在加载当前对象信息时,也会发送sql语句级联的去关联的一方对象信息
proxy --> 默认值 延迟加载,加载当前对象信息时,不会加载对方完整信息,而是返回一个代理对象
no-proxy --> 延迟加载,默认只加载当前对象信息,当需要使用对方对象信息时,发送sql 返回一个非代理对象
二、OpenSession与getCurrentSession方法比较(*)
openSession() :无论当前线程有没有session对象,都会直接去创建一个session
getCurrentSession():
1.从当前线程获取一个session对象 ,如果当前线程没有session则创建一个并绑定到当前线程中去
2.无论是DML操作还是DQL操作,要求当前线程必须绑定一个事物(界定事务边界)
3.在事物提交后session会自动关闭
4.在主配置文件中添加配置(上下文配置)
<!--给当前线程绑定一个session -->
<property name="hibernate.current_session_context_class">thread</property>
openSession()与getCurrentSession()方法比较
1.创建方式不同 一种是get 一种是open(MDZZ脑残说的话)
// 通过session工厂获取session对象
Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
Session session = sf.getCurrentSession(); // 获得Session
Session session = sf.openSession(); // 打开Session
2.实现过程不同
openSession 可以看得出来,是打开一个新的session对象,而且每次使用都是打开一个新的session,假如连续使用多次,则获得的session不是同一个对象,并且使用完需要调用close方法关闭session。
getCurrentSession 可以看得出来,是获取当前上下文一个session对象,当第一次使用此方法时,会自动产生一个session对象,并且连续使用多次时,得到的session都是同一个对象,这就是与openSession的区别之一,简单而言,getCurrentSession 就是:如果有已经使用的,用旧的,如果没有,建新的。
注意:在实际开发中,往往使用getCurrentSession多,因为一般是处理同一个事务(即是使用一个数据库的情况),所以在一般情况下比较少使用openSession这比较老旧的一套接口了;
3.简单实例
// openSession方式 :
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.hibernate.model.Student; // 注意包路径
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
s.setId(1);
s.setName("s1");
s.setAge(1);
Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
Session session = sessionFactory.openSession(); // 打开Session
session.beginTransaction(); // 看成一个事务,进行操作
session.save(s); // 会找到 Student 这个类,寻找set方法
session.getTransaction().commit(); // 提交对数据的操作
session.close();
sf.close();
}
}
// getCurrentSession方式 :
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.hibernate.model.Student; // 注意包路径
public class StudentTest {
public static void main(String[] args) {
Student s = new Student();
s.setId(1);
s.setName("s1");
s.setAge(1);
Configuration cfg = new Configuration(); // 获得配置信息对象
SessionFactory sf = cfg.configure().buildSessionFactory(); //解析并建立Session工厂
Session session = sessionFactory.getCurrentSession(); // 打开Session
session.beginTransaction(); // 看成一个事务,进行操作
session.save(s); // 会找到 Student 这个类,寻找set方法
session.getTransaction().commit(); // 提交对数据的操作
sf.close();
}
}
// Student 类代码 :
package com.hekaikai666.test1;
public class Student {
private int id;
private String name;
private int age;
// 此处省略get&&set&&constructor
}
三、HIbernate缓存机制
(一)什么是缓存?
缓存时介于应用程序和永久性存储数据源之间的介质。
(二)缓存的作用?
一般把一些不经常修改,但是会经常去访问的数据放入缓存中,那么下一次可以直接从缓存中读取数据,从而减少与服务器或者数据库的交互。从而提升系统的性能(速度、流量)
(三)Hibernate中缓存(Hibernate中的一级、二级缓存和Mybatis中的一级、二级缓存有什么区别)
3_1:一级缓存
主要是session缓存,主要缓存持久化对象,当调用方法想数据查询对象时(get/load)会先查询session中有没有这个对象,如果有就直接返回,如果没有,就直接发送SQL语句到数据库执行查询,并将查询结果存入到session缓存中。一级缓存在Hibernate中是默认开启的。
当session关闭时,session缓存消失。
3_2:二级缓存
二级缓存的特点:
1.二级缓存时sessionFactory级别的缓存,是所有session所共享的
2.二级缓存是应用程序级别缓存,其生命周期依赖于应用程序
3.二级缓存在Hibernate中默认没有开启,需要在主配置文件中主动开启
4.二级缓存需要借助一个第三方插件完成,可以将数据缓存在内存(内存中有很多的session区),也可以将数据缓存到硬盘中。第三方插件:EHCache、OSCache、SwarmCache、JBossCache(集群范围内、分布式都需要使用Redis)
(EH/OS:应用程序范围内的缓存,可以将数据缓存在内存或者硬盘中;S/JB:集群范围内缓存,不支持查询缓存)
二级缓存的使用步骤:
1.导入插件jar和配置文件ehcache.xml
2.主配置文件中开启二级缓存
3.指定二级缓存提供商
4.指定哪些类,集合需要二级缓存
5.设置ehcache.xml配置(以下为常用参数)
maxElementsInMemory: 占用内存的空间存1000个对象.
eternal: 缓存里面的对象是否是永久有效?只有是false,下面的两个参数才能配置
timeToIdleSeconds: 空闲的时间:指定对象使用间隔超过60秒,从缓存移除该对象
timeToLiveSeconds: 活跃时间:进入缓存空间最多呆300s,超过就清出.
overflowToDisk: 超出1000个对象,也存,存在 diskStore path="java.io.tmpdir",这个磁盘存储路径里面
3_3:查询缓存
查询缓存的特点: 查询缓存是基于二级缓存之上的;查询缓存主要缓存对象的属性值;查询缓存是以key-value键值对的形式对数据进行缓存的,两次发送相同的SQL语句,会使用查询到缓存
key:SQL语句
value:SQL语句的查询结果(value值一般都是对象的属性值,如果value值是一个对象,那么他只会存储代理,只有id的对象(oid),当需要使用这个对象时,会根据对象的id去一二级缓存中进行查询,一二级缓存中没有则去数据库中查询)
查询缓存使用步骤:主配置文件中开启缓存配置
<!-- 开启二级缓存,使用EhCache缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>
<!-- 开启查询缓存 -->
<property name="hibernate.cache.use_query_cache">true</property>
// 获取session工厂对象
Session session = sessionFactory.openSession();
// 通过对象开启事务
session.beginTransaction();
// 创建查询对象
Query query = session.createQuery("from Users where id = :id");
// 设置属性值
query.setParameter("id", 6);
// 启动查询缓存
query.setCacheable(true); //启用查询缓存
// 获取缓存对象里的属性值
Users user = (Users)query.list().get(0);
System.out.println("用户名:" + user.getUsername());
// 提交事务
session.getTransaction().commit();
// 关闭session
session.close();
四、三层架构和openSessionInView
三层架构:软件生成
表现层:面向用户(UI:User Interface 面向用户)
1、用户能看到的(UI-->jsp、html、swing、androad)
2、能接受用户请求的
3、给用户返回数据(servlet、struts2、SpringMVC)
业务层:用于要处理的业务逻辑
数据层:面向数据库的一层(jdbc、Hibernate、MyBatis)
MVC是一种表现层设计模式,属于三层架构中的表现层;三层架构是一种软件工程设计架构
Dao层属于数据持久层
openSessionInView:在表现层打开session。
本来的session的开启与关闭应该在业务层完成,但是这样在数据延迟加载时会存在session已经关闭的异常,所以把session的开启和关闭从业务层提到表现层,在Spring中提供了一个openSessionInViewFilter,可以把它配置在web.xml中,当一个请求到来时可以打开session,开启事务,当请求完成返回页面时提交事务,关闭session,这样也可以保证一个请求(一个响应)对应一个session对象。
使用场景:SSH+表关联查询+延迟加载
SSH与三层架构之间的关系:
感谢威哥的图,对理解很有用