您的位置:首页 > 其它

Hibernate学习4 二级缓存强化和事务管理 注解方式实现

2017-08-04 20:10 771 查看
如何证明二级缓存和一级缓存的对象是不一样的?

//第一次查询
Country country = session.get(Country.class, 2);

System.out.println("country  = " + country);
//已经把session清空了,然后不通过select还能查询导数据说明二级缓存确实存在
session.clear();

//如何证明二级缓存和一级缓存对象不一样,去除其中一个tostring看原始地址是否一致即可

//二级缓存缓存得是对象的详情内容,不是缓存的引用  二级换粗你的类缓存   那么集合缓存缓存的是什么呢?

//第2次查询
Country country1 = session.get(Country.class, 2);

System.out.println("country1  = " + country1);

session.clear();

//第3次查询
Country country2 = session.get(Country.class, 2);

System.out.println("country2  = " + country2);


如何证明二级缓存的集合里面存储的是什么?

//第一次查询
Country country = session.get(Country.class, 1);

Set<Minister> ministers = country.getMinisters();

System.out.println("ministers.size = " +ministers.size());

//第2次查询
Country country2 = session.get(Country.class, 1);

Set<Minister> ministers2= country2.getMinisters();

System.out.println("ministers.size = " +ministers2.size());
session.clear();

/*
* 类缓存对象存放在专门的一个称为实体区域的缓存中,缓存的内容为对象的详情,
* 集合缓存对象存放在专门的一个称为集合区域的缓存中,缓存的内容为集合中所包含的对象的id
*/
//第3次查询
Country country3 = session.get(Country.class, 1);

Set<Minister> ministers3 = country3.getMinisters();

System.out.println("ministers.size = " +ministers3.size());


Query缓存

首先需要开启Query查询的总开关,在总配置文件中进行设置

<!-- 开启Query查询缓存总开关-->
<property name="hibernate.cache.use_query_cache">true</property>
证明Query查询    同时也需要开启Query查询子开关
//第一次查询
String hql = "from Country where cid=2";
//开启query查询缓存总开关
Country country = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();

System.out.println("country  = " + country);

//第2次查询
Country country2 = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();

System.out.println("country2  = " + country2);

/*                                session.clear();*/

//第3次查询
Country country3 = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();

System.out.println("country3  = " + country3);
证明Query查询内容
//证明  Query缓存的内容 :其从Query缓存中查找的依据不再是查询结果对象的id,而是Query查询语句Query查询结果存到Query缓存时
//其key为Query查询语句,value未查询结果。每次不同sql语句需要多次对country进行查询

//第一次查询
String hql = "from Country where cid=2";
//开启query查询缓存总开关
Country country = (Country) session.createQuery(hql).setCacheable(true).uniqueResult();

System.out.println("country  = " + country);

//第2次查询  cid = 2
String hql2 = "from Country where cid in (2)";
Country country2 = (Country) session.createQuery(hql2).setCacheable(true).uniqueResult();

System.out.println("country2  = " + country2);

/*                                session.clear();*/

//第3次查询
String hql3 = "from Country where cid like 2";
Country country3 = (Country) session.createQuery(hql3).setCacheable(true).uniqueResult();

System.out.println("country3  = " + country3);

//执行更新
Country country = session.get(Country.class, 2);
System.out.println("country = " + country);


executeUpdate的方法

//此跟新绕过了一级缓存,没有进行实际的跟新    不建议这样写,会出现缓存数据和DB数据不一致的情况
//总会执行,次修改与快照没有任何关系,不和快照进行对比
String hql = "update Country set cname='吉隆坡' where cid=2";

session.createQuery(hql).executeUpdate();

//查询结果肯定会放入到二级缓存和一级缓存  ,先查询一级缓存在查找二级缓存,但为啥呢么没有直接从二级缓存总直接读取改数据,而是从DB中直接查询数据


Country country2 = session.get(Country.class, 2);

System.out.println("country2.Name  = " +country2.getCname());

session.clear();


//此查询没有绕过 二级缓存  但为啥呢么没有直接从二级缓存总直接读取改数据,而是从DB中直接查询数据
//因为Query的executeUpdate()方法会修改二级缓存对象中的一个属性,UpdateTimestamp,修改时间戳
//什么意思呢?实际上二级缓存对象中缓存的内容要比一级缓内容多一个属性,修改时间戳,
//一旦这个属性被修改,那么,查询会不从二级缓存中读取数据,而是直接从DB中读取数据


在没有提交之前就能查询到修改后的数据,说明session存在缓存,这就是为什么会发生脏读的原因

Country country3 = session.get(Country.class, 2);

System.out.println("country2.Name  = " +country3.getCname());


与二级缓存相关的方法 是session的方法 来创建

Session session = sessionFactory.getCache()

事务处理: 事务四大性(SCID)

原子性:事务中的全部操作在数据库中不可分割的,要么全部完成,要么均不执行

一致性:几个并发执行的事务,执行结果必须与按某一顺序串行执行的结果相一致

隔离性:事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的(互不干扰,各行其道)

持久性:对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现障碍

事务的并发问题:

多个事务对数据库的并发操作,可能会破坏事务的隔离性和一致性

脏读 (Dirty read) 事务A读取了事务B未提交的数据 A事务修改了数据岁未提交,但是B读取了该数据,然后A发生了回滚,此时B读取了不存在的数据

不可重复读

事务A读取了某个数据,事务B对该数据进行修改、删除、增加后,当事务A再次读,发现不一致称为不可重复读

丢失修改 两个事务A和B,读入同一数据并修改,B提交的结果破坏了A提交的结果,导致A的修改丢失

幻读 也称为虚读,现实存在的主要是并发访问所引发的问题 在同一事务中,虽然多次执行 相同的查询,查询结果是一样的,但是后面读取的数据已经不是数据库的真正数据,是虚的数据,就是DB中的幻读

事务的隔离级别:

四个隔离级别:读取未提交 读取已提交 可重复读 串行化 JDK中的connection接口已经定义了事务级别

封锁机制:事务的隔离级别,是DBMS隐式的为数据添加了锁

锁:可以分为

乐观锁、:每次访问数据时,都会乐观的认为其他事务此时肯定不会同时修改该数据,会在代码中通过锁的状态来判断数据是或否被其他事务修改过是在代码中完成的,所以乐观锁是加在代码中的 实现原理:版本号 + 时间戳 (每次提交之前先看一下版本号是否一致)

悲观锁、:其在访问数据库时,在数据库中就会先给数据加锁,以防止其他事务同时修改该数据,所以锁是加载在数据库中的

分为两种,排它锁(X锁 写锁)和共享锁( S锁 读锁)

select * from student where id in(1,2,3) lock in share mode ,加读锁的目的:防止其他事务对该数据加其他锁 (写锁)只能在读取数据的时候只能加一把读锁

如何解决并发问题:

设置Hibernate隔离级别

Hibernate的注解:替换映射文件

一对多双向关联:

Hibernate的重点:

单表查询会写

关联关系映射

缓存

快照 ehcache

事务

数据库的基本理论(四大 特性 )

下面介绍一个学习的demo

jar包下载链接:

首先是结构图



实体类:Country.java

package com.vrv.yinkailong.bean;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;

//自定义一个类  国家可以看到部长
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Country {

@Id
@GeneratedValue(generator="xxx")
@GenericGenerator(name="xxx",strategy="native")
private Integer cid;
private String cname;
//关联属性
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="countryId")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
private Set<Minister> ministers;

public Country() {
ministers = new HashSet<Minister>();
}
public Country(String cname) {
this();    //直接去执行前面的   ministers = new HashSet<Minister>();
this.cname = cname;
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Set<Minister> getMinisters() {
return ministers;
}
public void setMinisters(Set<Minister> ministers) {
this.ministers = ministers;
}
@Override
public String toString() {
return "Country [cid=" + cid + ", cname=" + cname + ", ministers=" + ministers + "]";
}

}


另一个实体类:Minister.java

package com.vrv.yinkailong.bean;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.GenericGenerator;

//自定义一个属性  部长    看不到国家
@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Minister {

@Id
@GeneratedValue(generator="xxx")
@GenericGenerator(name="xxx",strategy="native")
private Integer mid;
private String  mname;

@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="countryId")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
private Country country;

public Minister() {
super();
}

public Minister(String mname) {
super();
this.mname = mname;
}

public Country getCountry() {
return country;
}

public void setCountry(Country country) {
this.country = country;
}

public Integer getMid() {
return mid;
}
public void setMid(Integer mid) {
this.mid = mid;
}
public String getMname() {
return mname;
}
public void setMname(String mname) {
this.mname = mname;
}

@Override
public String toString() {
return "Minister [mid=" + mid + ", mname=" + mname + "]";
}
}


提取工具类:HiberUtil.java

package com.vrv.yinkailong.util;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

//自定义一个工具类
public class HiberUtil {
private static  SessionFactory sessionFactory = null;;

//必须保证sessionfactory是单例的,占资源
public static Session getSession()
{
/*Configuration configure = new Configuration().configure();
//2:创建session工厂对象
SessionFactory sessionFactory = configure.buildSessionFactory();
//3:获取session对象

Session session = sessionFactory.getCurrentSession();  */

SessionFactory sessionFactory = getSessionFactory();

return sessionFactory.getCurrentSession();

}
//抽取局部方法  alt + shift+ m
public static SessionFactory getSessionFactory() {
//此处做到单例,先判断在返回,能够保证做到单例
if (sessionFactory == null || sessionFactory.isClosed()) {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
return sessionFactory;
}
}


引入的ehcache.xml文件,具体操作在Hibernate 3中有

<!--
Mandatory Default Cache configuration. These settings will be applied to caches
created programmtically using CacheManager.add(String cacheName)
最多可以放多少个二级对象   是否永久   最长空闲时间s    最长生存时间s    溢出之后存到硬盘    应用技术 硬盘是否存放二级数据   硬盘到期线程时间s
内存存储驱除策略   LRU    最近最少使用算法   FIFO 先进先出    LFU  使用频率最小    LRU  未被使用时间最少        overflowToDisk="true"                   diskPersistent="false"-->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>


主配置文件 hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- session的不同含义   http:session (view层)   hibernate :session(DAO层)   MyBatis: session(SQL  session) -->
<!-- 三大数据库  Mysql  DB2 Oracal -->
<hibernate-configuration>
<session-factory>
<!-- DB链接四要素 -->
<property name="hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/test</property><!--  ///    相当于127.0.0.1:3306 -->
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<!-- 需要指定方言,才能生成相应的sql 语句  去hibernate core 里面找相应的版本  -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- 数据源  数据库连接池   链接数据库的那个容器我们成为数据库连接池 决定啥时候创建链接   :一般是先建立好,然后用户需要使用的时候直接从数据库连接池获取-->
<!--占用资源 :释放连接    连接个数  当达到下限需要再创建   然后释放之后需要设置最多放置多少条(上限)  空闲时间 :空闲多久会自动进行销毁   使用第三方机构的连接池-->
<!-- 链接提供者 -->
<property name="hibernate.co  nnection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider</property>
<!-- 保证在当前线程上下文中获取到的session是同一个session -->
<property name="hibernate.current_session_context_class">thread</property>

<!-- 配置自动建表   DDL数据库定义语言  DML数据库操作语言 DQL  数据源查询语言  create 每运行一次都会重新见一个表  update只会做更新 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 调试常用  :显示SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 生成sql语言进行格式化 -->
<property name="hibernate.format_sql">true</property>

<!-- 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<!-- 专门给二级缓存开辟了一个空间  注册二级缓存区域工厂    hibernate.cache.region.factory_class    在二级缓存的时候应该去除   .class   否则 会报错-->
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>

<!-- 实体类映射    相比以往的resource 的路径,这里是实体类映射-->
<mapping class="com.vrv.yinkailong.bean.Country"/>
<mapping class="com.vrv.yinkailong.bean.Minister"/>
</session-factory>
</hibernate-configuration>


最后是测试类:MyTest.java

package com.vrv.yinkailong.test;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.Session;
import org.hibernate.hql.internal.ast.HqlASTFactory;
import org.junit.Test;

import com.vrv.yinkailong.bean.Country;
import com.vrv.yinkailong.bean.Minister;
import com.vrv.yinkailong.util.HiberUtil;

public class MyTest {

@Test
public void test00()
{
Session session = HiberUtil.getSession();

try {
session.getTransaction().begin();
//session.beginTransaction();

Minister minister1 = new Minister("洛杉矶");
Minister minister2 = new Minister("自由女神");
Minister minister3 = new Minister("纽约");

Minister minister4 = new Minister("北京");
Minister minister5 = new Minister("新疆");
Minister minister6 = new Minister("武汉");

Country country = new Country("USA");
country.getMinisters().add(minister1);
country.getMinisters().add(minister2);
country.getMinisters().add(minister3);
session.save(country);

Country country2 = new Country("中国");
country2.getMinisters().add(minister4);
country2.getMinisters().add(minister5);
country2.getMinisters().add(minister6);
session.save(country2);

session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}

}

//证明后面查询数据都是从二级查询中获取的,没有经过在此二次查询
@Test
public void test01()
{
Session session = HiberUtil.getSession();

try {
session.getTransaction().begin();

//第一次查询
Country country = session.get(Country.class, 2);

System.out.println("country  = " + country);
//已经把session清空了,然后不通过select还能查询导数据说明二级缓存确实存在
session.clear();

//第2次查询
Country country1 = session.get(Country.class, 2);

System.out.println("country1  = " + country1);

session.clear();

//第3次查询
Country country2 = session.get(Country.class, 2);

System.out.println("country2  = " + country2);

session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
}

///演示集合缓存中所缓存的内容到底是什么?   是集合对象的所有id 没有详情  所以他需要再次根据id去查询
@Test
public void test01Collection()
{
Session session = HiberUtil.getSession();

try {
session.getTransaction().begin();
//第一次查询
Country country = session.get(Country.class, 1);

Set<Minister> ministers = country.getMinisters();

System.out.println("ministers.size = " +ministers.size());

//第2次查询
Country country2 = session.get(Country.class, 1);

Set<Minister> ministers2= country2.getMinisters();

System.out.println("ministers.size = " +ministers2.size());
session.clear();

/*
* 类缓存对象存放在专门的一个称为实体区域的缓存中,缓存的内容为对象的详情,
* 集合缓存对象存放在专门的一个称为集合区域的缓存中,缓存的内容为集合中所包含的对象的id
*/
//第3次查询
Country country3 = session.get(Country.class, 1);

Set<Minister> ministers3 = country3.getMinisters();

System.out.println("ministers.size = " +ministers3.size());

session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
}
}

//查看文件的临时路径并输出
@Test
public void test()
{
String PATH = System.getProperty("java.io.tmpdir");
System.out.println(PATH);
}
}


运行结果截图:

其中一个操作截图:



这些程序都是本人亲手写的,如果有需要互相学习和demo的可以直接留言,本人每天都会坚持写一篇博客,希望遇见真正热爱技术的你!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息