- Hibernate的工作原理—ORM
对象-关系映射(Object-Relationship Mapping)
在我们的应用程序(App)中,数据用对象来体现,而在数据库中,数据是使用表的形式保存。
Hibernate用于应用程序中的对象(Object)与表中的数据关系(Relationship)之间的映射
(Mapping),即把对象保存到关系表中或者把关系表中数据取出映射为对象。
可以这样理解,当我们使用Hibernate框架技术,就可以直接从数据库中取出Java对象,或者把
Java对象直接保存于数据库中,中间写sql语句等繁琐的步骤被Hibernate封装,对我们是透明的。 - Hibernate HelloWorld
核心步骤
1) 导入Jar包
2) Hibernate配置文件(只有1个)
hibernate.cfg.xml
用于数据库连接信息及Hibernate的一些配置信息
3) Hibernate映射文件(可以有n个)
用来指明类和表之间的对应关系,Hibernate根据该文件生成SQL语句
比如POJO类名为Emp.java,对应的映射文件就名为Emp.hbm.xml - 新建配置文件hibernate.cfg.xml
注意:应该放在源文件的src目录下,默认为hibernate.cfg.xml
文件内容是Hibernate工作时必须用到的基础信息
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 1. 数据库连接信息 -->
<property name="connection.url">
jdbc:mysql://localhost:3306/test
</property>
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- 2. Hibernate配置信息 -->
<!-- dialect是方言,用于配置生成针对哪个数据库的SQL语句-->
<property name="dialect">
<!--Hibernate提供的方言类,用于封装某种特定数据库的方言-->
org.hibernate.dialect.MySQLDialect
</property>
<!--将执行sql打印到控制台,一般用于sql调优-->
<property name="hibernate.show_sql">true</property>
</session-factory>
</hibernate-configuration> - 主键生成方式
常用的主键生成方式有如下几种:
1) identity 用于自动生成主键方式,除了Oracle不支持,其他数据库一般都支持
(较常用)
2) sequence Oracle中使用,用序列生成ID主键
3) native 主键生成方式如果是native,那就看配置文件hibernate.cfg.xml中
方言<property name="dialect">是什么,
如果方言是Mysql,相当于identity,如果方言是Oracle,相当于sequence
4) increment 不常用
5) assigned 不常用,手动生成id - Hibernate映射类型
案例描述
常用Hibernate映射类型有如下几种:
string
interger
double
date 日期,只表示年月日
datetime 日期,只表示年月日
timestamp 时间戳,存放年月日时分秒
yes_no 将在数据库中存一个字符Y或者N
true_false 将在数据库中存放一个字符T或者F,功能同yes_no是相同的 - 持久化对象和一级缓存机制
对于Hibernate而言,对象的状态分为3种:
1) 暂时态
当对象刚创建,和Session没有发生任何关系时,当程序运行完就立刻消失,被称为暂时态。
2) 持久态
当执行如下代码时,对象变为持久态
Emp e = new Emp();
session.save();
持久态的对象和Session发生了关系,如执行了save、get、query等方法
Session中会缓存该对象(Session的缓存叫一级缓存)
Session再获取对象时,首先去查找一级缓存,如果没有才查询数据库
Session要负责将持久态对象的变化更新到数据库
(在是flush()的时候更新,tx在提交的时候会自动调用session的flush())
3) 游离态
调用了session.evict(Object obj)方法,和Session解除了关系
1.2. 一级缓存机制
其一,如果session被查询,session将先到缓存中查找是否有被查询的对象,找到则直接取出,
否则才查询数据库;
其二,session需要负责实时维护在缓存中的数据,保证缓存中的数据与数据库中数据的一致性,
一旦用户对缓存中的数据做了修改,session立刻将数据更新到数据库中。
7.Hibernate延迟加载机制
2.1. 基本原理延迟加载机制的基本原理
当访问实体对象时,并不是立即到数据库中查找。而是在真正要使用实体对象的时候,才去数据库
查询数据。
具备这样功能的方法
session.load(...)
query.iterator()
注意:这些方法返回的对象,里面没有数据,数据在使用的时候(调用getXXX()方法时)才取。
2.2. 实现原理
1) load方法、iterator方法返回的对象不是实体类,而是该实体类动态子类对象,
该子类重写了getXXX方法,在该方法中触发了对数据库的访问。
2) 当调用load方法或iterator方法时,具体Hibernate调用了GBLIB的功能实现了
动态生成子类。
2.3. OpenSessionInView和ThreadLocal
1) OpenSessionInView技术把Session的关闭延迟到View组件运行完之后
2) 如果用延迟加载必须使用OpenSessionInView技术,否则在取数据时,session已经关闭了
3) 实现OpenSessionInView可以采用很多技术:
Servlet——过滤器
Struts2——拦截器
Spring —— AOP
4) 使用OpenSessionInView必须满足Session的线程单例
一个线程分配一个Session,在该线程的方法中可以获得该Session,
具体使用ThreadLocal——其实是一个线程为KEY的Map,
5) Hibernate的支持
配置文件中:
<property name="current_session_context_class">thread</property>
然后调用:
essionFactory.getCurrentSession();
自动实现线程单例
8.什么是动态生成一个类?
一般情况下,我们想创建并使用一个类的流程如下:a. 编译Java源文件 -> 在硬盘上生成一个二进制.class文件
b. JVM加载.class文件,将类读入一块内存(方法区)中
c. 应用程序调用方法区中的类及其方法。
而动态生成技术,是应用程序直接在内存中创建了一个类。就像当我们调用load方法,
我们并没有创建Foo$$EnhancerByCGLIB$$b3a0560c 这个类,
该类是由Hibernate动态生成的。
严格来讲,动态生成类技术也不是由Hibernate完成的,是由其他组件生成的,
asm.jar的作用就是在内存中生成类;
cglib-2.1.3.jar是在调用asm.jar的基础上动态的生成子类。因为asm.jar非常底层,
cglib-2.1.3.jar对其做了封装,用于生成某个类的子类。
于是,Hibernate调用了cglib-2.1.3.jar实现延迟加载。
9.线程单例
回到服务器中,只要是服务器,每一个浏览器访问服务器时,服务器会为每个浏览器创建一个线程。假设Some就是Session,如果使用这种机制获取Session,当同一个用户浏览器不论怎么调用
session都是同一个(只要在相同的线程中)。
这种机制就叫做线程单例。
线程单例的实现原理就是如上SomeFactory做的。
ThreadLocal
接下来继续回到ThreadLocal
ThradLocal就相当于Map,只不过key是固定的,就是当前线程号。
10.many-to-one关联映射
3.1. 数据表的关联数据表单的关联(只有一种情况)和Hibernate关联映射。
Hibernate关联映射是在数据表关联的基础上,根据业务需求,为了存取数据的方便高效而设计的。
数据表的关联不一定导致Hibernate关联映射。
3.2. many-to-one
基础表
t_emp (t_id,...t_dept_id)
t_dept (t_id)
需求
在取出Emp(many)的时候Dept(one)关联的取出
步骤1
class Emp {
...
private Dept dept;
}
步骤2
<many-to-one name="dept" class="Dept" column="t_dept_id"/>
3.3. many-to-one 需求下的常见操作
1) 保存Emp
Emp中有已经存在的Dept
2) 取出Emp(带着Dept)
Emp emp = (Emp)session.get(Emp.class,empId);
关联属性默认是延迟加载
<many-to-one ... lazy="false"/>
但是:取Dept还是用单独的SQL,可以设置fetch方式
<many-to-one ... fetch="join"/>
此时用join的方式生成SQL
3)查询Emp(带着Dept)
HQL: from Emp
关联属性默认是延迟加载
<many-to-one ... lazy="false"/>
但是:取Dept还是用单独的SQL
HQL : from Emp e left outer join fetch e.dept
4)根据特定条件查询Emp(带着Dept)
HQL : from Emp e where e.name='...'
HQL : from Emp e left outer join fetch e.dept where e.name='...'
5)根据Dept的属性查询Emp(带着Dept)
HQL : from Emp e where e.dept.name='...'
HQL : from Emp e left outer join fetch e.dept where e.dept.name='...'
11.当两个表有关联,并且有many-to-one的需求时,我们使用Hibernate提供的<many-to-one>
关联关系映射。Hibernate提供的<many-to-one>关联关系映射实现步骤:
a. many方(Emp)添加one方(Dept)的属性
b. many方配置文件(Emp.hbm.xml)中添加<many-to-one>信息
c. <many-to-one>要提供One方的class名,对应的列名
12. one-to-many关联映射
one-to-many关联映射1) one-to-many基本概念
基础:
t_order(t_id,...);
t_item(t_id,...t_order_id);
需求:
操作Order的时候一般都需要操作Item
2) 基本配置:
类:
class Order {
Set<Item> items;
}
配置文件:
<set name="items">
<key column="t_order_id"/>
<one-to-many class="Item"/>
</set>
3) 基本操作
保存Order,同时关联的保存Item
session.save(order); 生成的SQL:
insert into t_order ...
(因为save(order))insert into t_item....
(因为配置了级联保存<set .. cascade="save-update">)
(但是,存入的数据没有t_order_id,该字段不能为非空)
如何改变?many一方反向关联,Item里面关联Order
所以:one-to-many一般都是双向关联
update t_item set t_order_id...
(因为order的Set中关联了item,所以要将关联关系更新到数据库)
如何改变?many一方已经维护了关联,one这一方没有必要维护
<set ... inverse="true"/>
删除Order,同时级联删除与之关联的Item
<set cascade="delete"/>或者<set cascade="all"/>
更新Order,Order中解除了和一个Item的关系,
希望在数据库中删掉对应的记录
<set ... cascade="delete-orphan"/> 或者
<set ... cascade="all-delete-orphan"/>
查询Order,同时带出Item
from Order 用select的方式取Item
select distinct(o) from Order o join fetch o.items 用join的方式叏Item
主要重复的问题!!!
查询出包含特定Item的Order
select i.order from Item i where i.productName='...'
select distinct(o) from Order o join fetch o.items i where i.productName='...'
查询Order中包含Item的数目
from Order o;
order.getItems().size()....
Formula的方式:
class Order {
private int itemsNum;
}
< property name="itemsNum" type="integer"
formula="(select count(*) from t_item i where i.t_order_id=t_id"/>
13.加入inverse="true",inverse表示“反转”。
如果不加该属性,Hibernate会自动在One方(Order这方)维护关联关系;写inverse="true"表示关联关系由对方(或Many方,Item)维护,我(Order)就不维护了。
一般由Many方维护关联关系就够了。
one-to-many是一个难点,一定要弄清楚。
对比加属性inverse="true"之前和之后:
在加该属性之前,关联关系是由Order和Item双方来维护的,相当于这样
在加该属性之后,就是告诉Hibernate,关联关系由Many方(Item)来维护
注意:为了保证效率,一般one-to-many关系映射我们还是使用默认,双向来维护关联关系。
many-to-one有时有单向维护关系,比如说员工Emp关联部门Dept,部门Dept中不需要所有
员工的信息。
14.cascade="delete"表示级联删除
如果想保存、更新、删除都有级联操作,那么使用cascade="all"cascade="delete-orphan" orphan意为“孤儿”
所以,当删除Order中的Item时,如果想数据库同步,那么使用cascade="delete-orphan"
那么,如果我这些操作都想要:save-update、delete、delete-orphan,那么该写什么?
cascade="all-delete-orphan"表示既有“all”+“delete-orphan”
总结:
cascade属性的可选值共6个:
1.save-update
2.delete
3.delete-orphan
4.all
5.all-delete-orphan
6.none 默认为none,不做级联操作
fetch属性取值为:
fetch="select" 默认,Sql分开写,用于延迟加载
fetch="subselect" 子查询方式
fetch="join" 连接方式
fetch="select" 默认,Sql分开写,用于延迟加载
fetch="subselect" 子查询方式
fetch="join" 连接方式
15.many-to-many关联映射
1) many-to-many基本概念基础:
t_student(t_id,...);
t_couse(t_id,...);
t_student_couse(t_id, t_student_id,t_couse_id)
需求:
操作Student的时候一般都需要操作Course
操作Course的时候一般都需要操作Student
2) 基本配置:
class Student {
private Set<Course> courses;
}
Student.hbm.xml:
... ...
<set name="courses" table="t_student_course">
<key column="t_student_id"/>
<many-to-many class="Course" column="t_course_id"/>
</set>
16.继承关系映射到多个表
1) 基础class Product
class Book extends Product
class Computer extends Product
t_product (t_id...)
t_book (t_product_id....)
t_computer (t_product_id...)
2) 配置
<class name="Product"....>
....
<joined-subclass name="" table="">
<key column="t_product_id"/>
<property ... />
</joined-subclass>
</class>
3) 基本操作
保存: Hibernate会根据具体的类型(Book或者Computer)
来确定插入哪个子表,父表一定会插入
删除:Hibernate会根据具体的类型(Book或者Computer)
来确定删除哪个子表,父表一定会删除
查询子类 from Book:Hibernate会关联查询t_product和t_book
查询父类 from Product:Hibernate会关联查询t_product和所有的子表
返回的对象是具体的子类类型(可以强制转型)
只查询父表数据,不关联子表
select new Product(p.id, p.name, p.price) from Product p
17.继承关系映射到1个表
1) 基础class Question
class ChoiceQuestion extends Question
class EssayQuestion extends Question
t_question(t_id,...t_type,......)
2) 配置
<class name="Question"....>
<discriminator type="string" column="t_type"/>
....
<subclass name="ChoiceQuestion" discriminator-value="c">
<property ... />
</subclass>
</class>
3) 操作
存: Hibernate会根据类型(哪一个子类)
来插入与之对应的区分器字段的值
取: Hibernate会根据区分器字段的值来选择
用哪一个子类来封装并返回数据
18.log4j.appender
定义一个追加器,用于写明我们的日志要写入文件还是打印到控制台。一般记录到文件中,使用org.apache.log4j.FileAppender
此处,我们为了方便查看,使用输出到控制台
log4j.appender.abc
其中“abc”是自定义的追加器的名字,名字随便起
log4j.appender.abc.layout
用于定义日志输出的格式
org.apache.log4j.PatternLayout
是“模式匹配的格式”的意思
log4j.appender.abc.layout.ConversionPattern
在这里定义了”模式匹配的格式“
%d{yyyy-MM-dd hh:mm:ss} %5p %c{1}:%L - %m%n
这样的数据是定义好的“模式匹配”
%d
表示日志输出的日期
{yyyy-MM-dd hh:mm:ss}
表示日期的格式
%p
表示日志的级别
%5p
表示这个日志的级别占5个空格,用于“对齐”
%c{1};
表示输出的是哪个类
%L
表示行
%m
表示日志的信息
%n
表示“回车符号”
这么一大串,不用记,只要知道这是输出日志的格式即可。
log4j.rootLogger
表示
log4j.logger.com.tarena.tts = error , abc
log4j.logger
是固定写法
com.tarena.tts
表示这个包里的类
log4j.logger.com.tarena.tts = error , abc
表示在之前定义的名为abc的追加器上输出日志,只输出Error级别以上的日志,
如果在调试程序时,我们可以将之改为debug,abc,这样所有debug级别的日志就可以
看到了
log4j.rootLogger=debug, abc
表示默认的,其它所有没有定义的包中类的日志使用abc追加器,debug级别以上的日志。
所以,如果定义了log4j.rootLogger=debug, abc
那举log4j.logger.com.tarena.tts = error , abc可以省略为
log4j.logger.com.tarena.tts = error
log4j.logger.org.hibernate=info
表示Hibernate中的日志,info级别以上的都输出
在Hibernate中调用了log4j的API来记录日志,那么我们自己能不能调用?可以
如果想在程序运行时输出日志,有固定的写法
首先,为该类写一个静态私有成员
其次,在方法中调用,
Log4j记录日志的方法共有5种,从上向下严重程度依次增加,
Log4j定义了5种级别的日志,由用户自行选择在什么情况下调用何种级别的记录方式
19.新建TestLog4j
package com.tarena.tts.test;import org.apache.log4j.Logger;
public class TestLog4J {
private static Logger logger =
Logger.getLogger(TestLog4J.class);
public static void main(String[] args) {
//log4j记录日志的方式共5种
logger.debug("debug message"); // 输出一条DEBUG级别日志 debug
logger.info("info message");// 输出一条INFO级别日志 提示信息
logger.warn("warn message");// 输出一条WARN级别日志 警告
logger.error("error message");// 输出一条ERROR级别日志 错误
logger.fatal("fatal message");// 输出一条FATAL级别日志 致命错误
}
}
举例:
如果在catch块中记录,最起码也记录一条ERROR级别的日志,
又如tomcat的启劢,提示tomcat正在启动的信息,那么可以使用INFO级别
连接数据库时,提示用户没有使用默认配置,那么可以使用WARN级别
Debug和INFO差不多,用于程序出错时进行调错,给程序员用的。
20.one-to-many(List)
1) 基础
class Team{
List<Person> persons;
}
class Person {}
t_team(t_id,...,)
t_person(t_id,...,t_team_id,t_turn,...)
2) 配置
<list name="persons" cascade="all">
<key column="t_team_id"/>
<list-index column="t_turn" base="0"/>
<one-to-many class="Person"/>
</list>
3) 操作
同Set的one-to-many;但是,不要inverse="true",也就不需要反向关联
21.Ant工具:
<list-index column="" base=""> column中填写的是决定person排老几的字段
base="0" 表示排序的起始计数,一般我们写0
basedir="."表示“相对路径相对于谁”,此处相对于当前路径
"*.jar"表示当前路径下的所有jar文件都引入
"**/*.jar"表示除当前路径下的所有Jar文件,当前路径下的子路径及孙子路径下的所有Jar文件都引入
todir表示拷贝到哪个目录;
dir表示要拷贝的文件所在目录
Include表示拷贝的文件包括src_dir目录下的所有“**/*.java”文件
encode表示源文件的编码形式
src表示源文件在哪里
desc表示目标文件放到哪
base="0" 表示排序的起始计数,一般我们写0
basedir="."表示“相对路径相对于谁”,此处相对于当前路径
"*.jar"表示当前路径下的所有jar文件都引入
"**/*.jar"表示除当前路径下的所有Jar文件,当前路径下的子路径及孙子路径下的所有Jar文件都引入
todir表示拷贝到哪个目录;
dir表示要拷贝的文件所在目录
Include表示拷贝的文件包括src_dir目录下的所有“**/*.java”文件
encode表示源文件的编码形式
src表示源文件在哪里
desc表示目标文件放到哪
22. destdir="." 表示生成数据库表的sql文件放在当前目录
<classpath refid="cp" /> 表示工作时使用的类路径
configurationfile="${dir.classes}/hibernate.cfg.xml"
用于指明Hibernate配置文件的位置
<hbm2ddl > 表明根据映射文件生成表
drop="true" 表示如果数据库中有同名表,先删除
create="true" 表示创建新表
console="true" 表示将生成的sql语句显示到控制台
export="true" 表示生成sql的同时,就要用JDBC连接数据库进行建表,
如果为false表示只是生成该sql语句,并不会运行该sql
outputfilename="schema.sql" 表示生成sql文件的名字
delimiter=";" 表示建表之间的分号“;”
format="true" 表示格式化显示这些sql
<classpath refid="cp" /> 表示工作时使用的类路径
configurationfile="${dir.classes}/hibernate.cfg.xml"
用于指明Hibernate配置文件的位置
<hbm2ddl > 表明根据映射文件生成表
drop="true" 表示如果数据库中有同名表,先删除
create="true" 表示创建新表
console="true" 表示将生成的sql语句显示到控制台
export="true" 表示生成sql的同时,就要用JDBC连接数据库进行建表,
如果为false表示只是生成该sql语句,并不会运行该sql
outputfilename="schema.sql" 表示生成sql文件的名字
delimiter=";" 表示建表之间的分号“;”
format="true" 表示格式化显示这些sql
23. HQL
tst5()
不同的HQL语句返回的List结果集中的内容不同
from Emp
返回 List<Emp>
select emp.id from Emp emp
返回 List<Integer>
select emp.id , emp.name from Emp emp
返回 List<Object[]>
select new Emp(emp.id, emp.name) from Emp emp
返回的仍然是 List<Emp>,此时的Emp对象中只有id和name,并且不是持久态对象
tst5()
不同的HQL语句返回的List结果集中的内容不同
from Emp
返回 List<Emp>
select emp.id from Emp emp
返回 List<Integer>
select emp.id , emp.name from Emp emp
返回 List<Object[]>
select new Emp(emp.id, emp.name) from Emp emp
返回的仍然是 List<Emp>,此时的Emp对象中只有id和name,并且不是持久态对象
24. Hibernate 二级缓存
缓存的意义
缓存机制就是将数据库中常用的数据取出放入内存中,程序调用时直接从内存中取,不用每次使用
数据都访问数据库,这样提高了效率。
缓存需要关注的问题
1) 缓存的更新
缓存中的数据必须是同数据库中数据保持一致。
2) 缓存的命中率
提高缓存数据的利用率,缓存中存放的是用户常用的数据,如果缓存中存放的是用户不常用的,
那么就说缓存的命中率不高。
有些时候,是某些缓存数据在某个时刻使用率高,某个时刻使用率低,所以需要时刻更新,
以提高缓存命中率。
ehcache
缓存机制如果做好了,是比较困难的,有一些专门的组件就是用于专门解决缓存问题的。
比如ehcache
缓存的意义
缓存机制就是将数据库中常用的数据取出放入内存中,程序调用时直接从内存中取,不用每次使用
数据都访问数据库,这样提高了效率。
缓存需要关注的问题
1) 缓存的更新
缓存中的数据必须是同数据库中数据保持一致。
2) 缓存的命中率
提高缓存数据的利用率,缓存中存放的是用户常用的数据,如果缓存中存放的是用户不常用的,
那么就说缓存的命中率不高。
有些时候,是某些缓存数据在某个时刻使用率高,某个时刻使用率低,所以需要时刻更新,
以提高缓存命中率。
ehcache
缓存机制如果做好了,是比较困难的,有一些专门的组件就是用于专门解决缓存问题的。
比如ehcache
Hibernate的缓存机制
Hibernate提供了二级缓存机制。
首先,Hibernate中的一级缓存机制(也叫做事务内的缓存)是与Session绑定在一起的。
当一个Session开启,一级缓存创建;当一个Session关闭,一级缓存销毁。
若使用一级缓存机制(Session的缓存,每个用户线程对应一块Session缓存)
现在有5个用户(5个线程)访问Hibernate,
那么Hibernate会为5个用户创建5个不同的Session(一个线程分配一个Session)。
25.参数的含义分别是
maxElementInMemory 表示该缓存中可以放如多少个对象,此处为10000个,根据内存的多少可以配置
eternal
表示是否设置这些放入二级缓存的数据对象为永久的(即放入即保存,不再清除)
一般都为false
timeToIdleSeconds=120
表示如果120秒内,放入的对象没有被再次访问到,就清除出去
timeToLiveSeconds=120
表示对象在缓存中存活的时间,一个对象进入到本缓存中120秒后,就会自动被清除(一般
设置的时间会比timeToIdleSeconds时间长),设置此属性是为了让更多活跃的对象进入到
缓存中来。
overflowToDisk="true"
表示如果活跃对象已经超出maxElementInMemory设置的最大值时,超出的对象要被写入
到硬盘上保存下来,用于缓解活跃用户较多的情况。
26.region属性 表示指定使用哪个二级缓存
usage属性 表示二级缓存的使用方式有两种:read-only和read-write
read-only 如果值为read-only,那么就不能修改。
这样ehcache就不用考虑修改和更新的操作。
read-write 设置为read-write,ehcache还需要考虑更新和修改。
这样会降低效率。
所以,设置usage属性是很重要的,需要根据实际情况判断存入的对象使用二级缓存的方式。
27.使用二级缓存的步骤
1) 导入Jar包2) Hibernate配置文件中指明使用二级缓存ehcache
3) 配置ehcache配置文件,定制缓存策略,共需指定5个属性。
4) 选择哪个对象使用二级缓存机制
在该对象的配置文件*.hbm.xml中配置如何使用二级缓存机制:
首先,通过region属性指定要使用的二级缓存;
其次,通过usage属性指定使用二级缓存的方式
28.Hibernate 查询缓存
和二级缓存不同的是,有时候我们需要取出的并不是Emp对象,比如select emp.name from Emp emp;
该查询取出的数据结果不是Emp对象,而是字符串。
或者是数组
select emp.name , emp.salary from Emp emp;
二级缓存默认是不会将他们保存到二级缓存中的。
二级缓存只管保存对象,而不会管诸如取字符串、取数组之类的其它数据。
但是如果这条语句使用也很频繁,我们有需要将它保存到二级缓存中的需求,那该怎么做?
使用查询缓存。
通过配置查询缓存后,Hibernate会将这条语句及查询结果封装为一个对象保存,
当再有用户调用该语句时,就直接返回该对象中封装好的查询结果。
查询缓存适用于查询结果数据量巨大,查询结果一般不轻易改变的查询。
如果查询数据总改变,维护查询缓存的系统开销也会很大。比如查询商品信息Product的语句就可以设置为查询缓存,因为商品信息一般不会改变。