Session方法
操作Session缓存:
1.flush()
2.refresh()
3.clear()
flush():使session缓存数据和数据库中一致(可能会调用update)
1.在Transaction 的 commit() 方法中:先调用session的flush方法,在提交事务
2.flush() 方法会执行sql语句,但是不会提交事务
3.在未提交或是显式调用flush之外还有可能调用flush方法
1)执行HQL或QBC查询,会先进行flush()操作,以得到最新数据(数据库中数据没变,但是Session中已经是最新数据了)
2)若记录的ID是由底层数据库自增的方式生成的,则在调用save()方法时,就会立即发送insert,因为save方法后,必须保证对象ID是存在的
refresh():会强制发送select语句,即使session缓存数据一致
注意:数据库的隔离级别会导致数据库即使改变,但数据还是不变
Mysql的默认隔离级别是可重复读,修改其为读已提交,就可以改变
1:读操作未提交(Read Uncommitted)
2:读操作已提交(Read Committed)
4:可重读(Repeatable Read)
8:可串行化(Serializable)
<property name="connection.isolation">2</property>
Clear() :会清理缓存
可以用以下测试:清除之后会再次发送select语句以获取:
@Test
public void testSession(){
News news=(News)session.get(News.class,1);
session.clear();
News news1=(News)session.get(News.class,1);
}
————————————————————————————————
Session核心方法:
四种持久化状态
1.持久化状态 存在于Session和数据库种
2.临时状态 Session和数据库种都不存在
3.游离状态 只存在于数据库中
4.删除状态 Session和数据库种都不存在(不该再被使用)
Save()——完成Insert操作
1.使一个临时对象变为持久化对象
2.为对象分配ID
3.在flush缓存时会发送insert语句
4.在save方法之前的setID是无效的
5.ID是不可改变的
Persist()——完成Insert操作
和Save区别:
1.在执行persist()之前已经如果有ID了,则不会进行Insert,并且抛出异常
get(),Load()——根据OID从数据库加载一个对象
区别:
1.执行get方法会立即执行select ; 执行load方法不会立即查询,而返回一个代理对象
2.执行get方法如果没有记录,则会返回null ; 执行load 如果没有记录,当使用此记录时则出现异常
3.load方法可能会抛出懒加载(LazyInitializationException)异常,因为在需要初始化代理对象之前就已经关闭了Session.
Update()——完成update操作
update方法使一个游离对象(不在Session中)转变为持久化对象,并执行一条update语句
1.若更新一个持久化对象不需要显式的执行update方法,因为在执行commit方法时,会先执行Session的flush方法
2.更新游离对象才需要显式的调用update方法
3.游离对象执行update方法即使对象没有改变,都会发送update语句,持久化对象只有发生改变才会发送语句
4.通过设置select-before-update=true(默认为false) 用于在执行update前先select看是否需要更新
5.update 更新数据库中没有的对象会出异常
6.更新Session中已经存在的OID的对象,会抛出异常,因为Session中不能有二个OID的对象
<class name="com.chenx.hibernate.helloworld.News" table="tb_news" select-before-update="true">
SaveOrUpdate() ——执行Insert或Update
1.若OID不为空,但数据表中没对应的记录,会抛异常
2.OID值等于ID的unsaved-value 属性值的对象,
<id name="id" type="java.lang.Integer" unsaved-value="20">
delete()——执行删除操作
执行删除操作,只要OID和数据表记录一致,就执行
1.无论是持久化对象还是游离对象,只要OID一样
2.删除不存在的记录会抛异常,可以配置使删除之后把持久化对象和游离对象变为临时对象(id置为null):
<property name="use_identifier_rollback">true</property>
Evict()——从session缓存中把指定的持久化对象移除
把session缓存中此对象删除,就会使其变为游离对象,即使set变化后,flush也不会更新,因为没有此对象,无需同步。
@Test
public void testSession(){
News news=session.get(News.class,1);
news.setAuthor("tt");
session.evict(news);
}
Session 一级缓存,二级缓存:
一级缓存:
是session级别的缓存,它是属于事务范围内的缓存,这一级别的缓存是由hibernate管理的
根据如下测试可以看出其特点:
@Test
public void testSession(){
Employee employee=session.get(Employee.class,3);
System.out.println(employee.getName());
Employee employee1=session.get(Employee.class,3);
System.out.println(employee1.getName());
}
可以看到执行了二次查询,查询的是同一个employee,但是只发送了一句select语句,因为在用一个事务中,查询第一次的时候把结果存入了session中,第二次查询的时候先从session中查找是否有此对象,有的话就不发送select语句了
先在其中提交事务,并开启一个新的事务,观察其是否执行二次select语句,关闭session后会清空其缓存:
@Test
public void testSession(){
Employee employee=session.get(Employee.class,3);
System.out.println(employee.getName());
transaction.commit();
session.close();
session=sessionFactory.openSession();
transaction=session.beginTransaction();
Employee employee1=session.get(Employee.class,3);
System.out.println(employee.getName());
}
二级缓存:
二级缓存是SessionFactory级别的缓存,它是属于进程范围的缓存
在此我们将使用Ehcache:
-> Ehcache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
-> 要使用Ehcache,首先需要加入三个jar包:
ehcache-core-2.4.3.jar
hibernate-ehcache-4.3.11.Final.jar
slf4j-api-1.6.1.jar
->接着配置二级缓存:
<property name="cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
还需要配置其作用在哪些类上:
①配置在cfg.xml中:<class-cache class="com.chenx.hibernate.HQL.Employee" usage="read-only"></class-cache>
②配置在hbm.xml中:<cache usage="read-write"></cache>
->最后还需加入ehcache的xml配置文件
集合级别的二级缓存配置:
我们在department中有一个集合的属性,一个Employee的集合,现要达到获取集合存入sessionfactory,达到同样效果
配置需要注意:不但要把集合属性对应的配置缓存,还需要配置此类的二级缓存:
<class-cache class="com.chenx.hibernate.HQL.Department" usage="read-write"></class-cache>
<collection-cache collection="com.chenx.hibernate.HQL.Department.employeeSet" usage="read-write"></collection-cache>
<class-cache class="com.chenx.hibernate.HQL.Employee" usage="read-write"></class-cache>
如果只配置了集合,没有配置其类的话,反而会更加麻烦,因为配置了集合就会把employee的id存下来,但是没有存储其别的参数,当再次查询的时候,要初始化employee,原本可以统一的初始化,现在因为有每个ID,要一个一个的select获取
同样也可以在hbm.xml中配置:
<set name="employeeSet" table="tb_employee" >
<cache usage="read-write"></cache>
<key column="DepartmentID"></key>
<one-to-many class="com.chenx.hibernate.HQL.Employee"></one-to-many>
</set>
ehcache.xml 配置文件解析:
<?xml version="1.0" encoding="UTF-8"?><ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="C:\\Users\\chenx\\Desktop\\yy"/>
<defaultCache
maxElementsInMemory="20000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="com.chenx.hibernate.HQL.Department.employeeSet"
maxElementsInMemory="1"
eternal="true"
overflowToDisk="true"
diskPersistent="false"
timeToLiveSeconds="0"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
.........
通过设置配置,使HQL和SQL查询也可使用:(查询缓存依赖于二级缓存)
<property name="cache.use_query_cache">true</property>
还需在使用时调用setCacheable(true);
@Test
public void testSession(){
String hql="from Employee";
org.hibernate.Query query= session.createQuery(hql);
query.setCacheable(true);
List<Employee> list=query.list();
System.out.println(list.size());
list=query.list();
System.out.println(list.size());
}
时间戳缓存:
@Test
public void testSession(){
String hql="from Employee";
org.hibernate.Query query= session.createQuery(hql);
query.setCacheable(true);
List<Employee> list=query.list();
System.out.println(list.size());
Employee employee=(Employee) session.get(Employee.class,4);
employee.setSalary(11);
list=query.list();
System.out.println(list.size());
}
如上测试,会发送三个语句,因为中间执行了更新,Ehcache会在存储更新或删除操作的时间戳,根据此来判断缓存数据的值是否过期,再执行操作
Query接口的iterate() 方法:
从上得知我们使用HQL语句可以用查询缓存对其优化,现在同样使用HQL,SQL语句,使用query的iterate():
@Test
public void testSession(){
Department department=(Department) session.get(Department.class,10);
System.out.println(department.getEmployeeSet().size());
Query query=session.createQuery("from Employee e where e.department.id=10");
Iterator<Employee> employees=query.iterate();
while ((employees.hasNext())){
System.out.println(employees.next().getName());
}
}
测试看出,iterate没有额外发送语句,而是从缓存中直接获取到了数据,但是经过测试发现,如果缓存中没有数据,他则根据返回的id一个一个再次从表中获取完整数据,所以iterate有时可以优化,有时候反而更差