欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

day32_Hibernate学习笔记_04

程序员文章站 2023-08-26 14:23:27
一、Hbernate中的日志框架_整合log4j(了解) Hibernate 利用 Simple Logging Facade for Java (SLF4J)来记录不同系统事件的日志。SLF4J 可以根据你选择的绑定把日志输出到几个日志框架(NOP、Simple、log4j version1.2、 ......

一、Hbernate中的日志框架_整合log4j(了解)

  • Hibernate 利用 Simple Logging Facade for Java (SLF4J)
    来记录不同系统事件的日志。SLF4J 可以根据你选择的绑定把日志输出到几个日志框架(NOP、Simple、log4j version
    1.2、JDK 1.4 logging、JCL 或 logback)上。
  • slf4j 核心jar:slf4j-api-1.6.1.jar 。slf4j 是日志框架,将其他优秀的日志第三方进行了整合。
    day32_Hibernate学习笔记_04
  • 整合导入jar包,添加至构建路径
      1、log4j 核心包:log4j-1.2.17.jar
      2、过渡jar(整合jar):slf4j-log4j12-1.7.5.jar
  • 导入配置文件,该文件在 \hibernate-distribution-3.6.10.Final\project\etc 中,拷贝至项目的src 目录下
      log4j.properties,此配置文件配置log4j 如何输出日志。
  • 编写配置文件内容:
      1、记录器
      2、输出源
      3、布局
  • 记录器:
      例如:log4j.rootLogger=info, stdout, file
      格式:log4j.rootLogger=日志级别, 输出源1, 输出源2, ……
      log4j 日志级别:fatal 致命错误、error 错误、warn 警告、info 信息、debug 调试信息、trace 堆栈信息 (输出信息多少:日志信息量逐渐增加),项目上线时用error项目开发中用info
  • 输出源:
      例如:log4j.appender.file=org.apache.log4j.FileAppender
      格式:log4j.appender.输出源的名称=输出源的实现类
        输出源的名称:自定义
        输出源的实现类:log4j提供
      输出源的属性,例如:log4j.appender.file.File=d:\mylog.log
      输出源的属性格式:log4j.appender.名称.属性=值
      每一个输出源对应一个实现类,实现类都是属性(setter),底层执行setter方法进行赋值。
  • 常见的输出源实现类:
      org.apache.log4j.FileAppender 输出文件中
        file,表示文件输出位置
      org.apache.log4j.ConsoleAppender 输出到控制台
        Target,表示使用哪种输出方式,在控制台打印内容,取值:System.out / System.err
  • 布局 => 确定输出格式
      例如:log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      格式:log4j.appender.数据源.layout=org.apache.log4j.PatternLayout
      布局属性:log4j.appender. 数据源.layout.ConversionPattern=值
        例如1:log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
        输出为:21:57:26,197 INFO SessionFactoryImpl:927 - closing
        例如2:log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
        输出为:closing
  • 扩展:对指定的目录设置日志级别
      例如:log4j.logger.org.hibernate.transaction=debug
      格式:log4j.logger.包结构=级别

二、Hibernate的关联关系映射(一对一)(了解)

  • 情况1:主表的主键,与从表的外键(唯一),形成主外键关系。
  • 情况2:主表的主键,与从表的主键,形成主外键关系 (从表的主键又是外键,即主键同步)-- 推荐使用该方式
  • 如下图所示:
    day32_Hibernate学习笔记_04

2.1、情况1示例

Company.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping  package="com.itheima.domain">
    <class name="Company" table="t_company">
        <id name="cid" column="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname" column="cname" type="string"></property>

        <!-- 一方的配置:一对一,特殊的多对一。
             one-to-one:默认使用主键同步策略完成一对一的表关系体现。
                但是我们现在使用的是主外键引用来完成一对一的表关系体现。那么需要进行修正。
                使用 property-ref="company"   值是对方引用本方的属性名称
        -->
        <one-to-one name="address" class="Address" property-ref="company"></one-to-one>
    </class>
</hibernate-mapping>

Address.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping  package="com.itheima.domain">
    <class name="Address" table="t_address">
        <id name="aid" column="aid">
            <generator class="native"></generator>
        </id>
        <property name="aname" column="aname" type="string"></property>

        <!-- 多方的配置:一对一,特殊的多对一。外键唯一(外键不能重复),提供外键名称,unique默认值是false -->
        <many-to-one name="company" class="Company" column="company_id" unique="true"></many-to-one>
    </class>
</hibernate-mapping>

测试代码如下:

package com.itheima.a_one2one;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Address;
import com.itheima.domain.Company;
import com.itheima.utils.HibernateUtils;

// 演示:一对一
public class Demo1 {
    @Test
    // 一对一保存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Company c = new Company();
        c.setCname("传智播客");

        Address a = new Address();
        a.setAname("北京市朝阳区平房乡红门村28号");

        // 注意:在一对一使用外键引用的时候,即情况1,外键所在的对象才能维护外键关系,另一方无法维护外键关系。
        a.setCompany(c); // 维护外键关系

        // 注意:在一对一使用主外键同步的时候,即情况2,双方都能维护外键关系。
        // a.setCompany(c); // 维护外键关系
        // c.setAddress(a); // 维护外键关系

        session.save(c);
        session.save(a);

        session.getTransaction().commit();
        session.close();
    }

    @Test
    // 一对一查询
    // 注意:Hibernate中一对一查询,一定会使用表连接查询,所以也就没有懒加载的问题了。
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Company c = (Company) session.get(Company.class, 1);
        System.out.println(c);

        session.getTransaction().commit();
        session.close();
    }
}

2.2、情况2示例

Company.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping  package="com.itheima.domain">
    <class name="Company" table="t_company">
        <id name="cid" column="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname" column="cname" type="string"></property>

        <!-- 一方的配置:一对一,真正的一对一。不需要进行修正了。
             one-to-one:默认使用主键同步策略完成一对一的表关系体现。我们现在就使用默认的策略。
              注意:此时可以使用以下3个属性。
                cascade     lazy        fetch
        -->
        <one-to-one name="address" class="Address" ></one-to-one>
    </class>
</hibernate-mapping>

Address.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping  package="com.itheima.domain">
    <class name="Address" table="t_address">
        <id name="aid" column="aid">
            <!-- foreign:声明该键既是主键又是外键 -->
            <generator class="foreign">
                <!-- 当本方作为外键时,注入的是引用对方的属性名称 -->
                <param name="property">company</param>
            </generator>
        </id>
        <property name="aname" column="aname" type="string"></property>

        <!-- 一方的配置:一对一,真正的一对一。既是主键又是外键,需要在某一方声明外键 ,constrained表示约束,默认值是false-->
        <one-to-one name="company" class="Company" constrained="true"></one-to-one>
    </class>
</hibernate-mapping>

测试代码同上:

三、二级缓存【掌握】

3.1、介绍

3.1.1、缓存

  缓存(Cache):是计算机领域非常通用的概念。它介于应用程序永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存
  缓存:程序 <-- (内存) --> 硬盘

3.1.2、什么是二级缓存?

  • Hibernate 提供缓存机制:一级缓存、二级缓存。
    一级缓存:Session 级别缓存,在一次请求*享数据。(当前有多少个线程连接到数据库,就会有多少个一级缓存。)
    二级缓存:SessionFactory 级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。(二级缓存中存放的是经常使用的、不经常被修改的数据。)
  • SessionFactory的缓存两部分:
    内置缓存:使用一个Map,用于存放配置信息,如预定义的HQL语句等,提供给Hibernate框架自己使用,对外只读。不能操作。
    外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。对于外置缓存,Hibernate只提供规范(接口),需要第三方实现类,所以我们使用二级缓存,还得导入第三方的jar包。外置缓存又称为二级缓存。

3.1.3、二级缓存的内部结构

二级缓存就是由4部分构成:
  类级别缓存
  集合级别缓存
  时间戳缓存
  查询级别缓存(二级缓存的第2大部分:三级缓存)
内部结构如下所示:

day32_Hibernate学习笔记_04

3.1.4、并发访问策略

访问策略:读写型(read-write)、只读型(read-only)

day32_Hibernate学习笔记_04

3.1.5、应用场景

  • 适合放入二级缓存中的数据:
    很少被修改
    不是很重要的数据,允许出现偶尔的并发问题
  • 不适合放入二级缓存中的数据:
    经常被修改
    财务数据,绝对不允许出现并发问题
    与其他应用数据共享的数据

3.1.6、二级缓存提供商(即实现了二级缓存接口的厂商)

  • EHCache:可作为进程(单机)范围内的缓存,存放数据的物理介质可以是内存或硬盘,对 Hibernate 的查询缓存提供了支持。且支持集群
  • OpenSymphony:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘。提供了丰富的缓存数据过期策略,对 Hibernate 的查询缓存提供了支持。
  • SwarmCache:可作为集群范围内的缓存,但不支持 Hibernate 的查询缓存。
  • JBoss Cache:可作为集群范围内的缓存,支持 Hibernate 的查询缓存。
    day32_Hibernate学习笔记_04

3.2、配置

  配置即操作:亦即使用二级缓存提供商的提供的jar。

  • 1、导入jar包并添加至构建路径:ehcache-1.5.0.jar(核心包)、commons-logging.jar(依赖包)、backport-util-concurrent.jar(依赖包)
  • 2、开启二级缓存(我要使用二级缓存)
  • 3、确定二级缓存提供商(我要使用哪个二级缓存)
  • 4、确定需要缓存内容
      (1)配置需要缓存的类
      (2)配置需要缓存的集合
  • 5、配置ehcache的自定义配置文件

3.2.1、导入jar包并添加至构建路径

day32_Hibernate学习笔记_04

3.2.2、开启二级缓存(我要使用二级缓存)

  先在 hibernate.properties 中找到对应的键和值:

day32_Hibernate学习笔记_04
  再在 hibernate.cfg.xml 中配置开启二级缓存:
day32_Hibernate学习笔记_04

3.2.3、确定二级缓存提供商(我要使用哪个二级缓存)

  先在 hibernate.properties 中找到对应的键和值:

day32_Hibernate学习笔记_04
  再在 hibernate.cfg.xml 中配置确定二级缓存提供商:
day32_Hibernate学习笔记_04

3.2.4、确定缓存内容

  在 hibernate.cfg.xml 中确定 类级别缓存集合级别缓存 配置项:
  先确定这两个缓存所在配置文件中的位置:

day32_Hibernate学习笔记_04
  具体配置:
day32_Hibernate学习笔记_04

3.2.5、ehcache配置文件

  • 步骤1:从jar包复制 ehcache-failsafe.xml 文件
    day32_Hibernate学习笔记_04
  • 步骤2:将 ehcache-failsafe.xml 重命名 ehcache.xml
    day32_Hibernate学习笔记_04
  • 步骤3:将修改后的 ehcache.xml,拷贝到src下
    day32_Hibernate学习笔记_04
  • 步骤4:删除掉 ehcache.xml 文件中无用的注释,得到清爽的 ehcache.xml 文件,文件内容如下:

ehcache.xml

 <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"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>

  一般,该文件的配置不是我们的工作,我们使用默认的配置即可,如果想配置,请看下文的 3.4、ehcache配置文件详解

3.3、演示

3.3.1、证明二级缓存存在

  示例代码如下:见类缓存中的示例代码

3.3.2、类缓存

  • 类缓存:只存放数据
  • 一级缓存:存放对象本身
    如下图所示:
    day32_Hibernate学习笔记_04
    示例代码如下:
package com.itheima.a_one2one;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;

// 演示:二级缓存
public class Demo1 {
    @Test
    // 演示:证明二级缓存存在 + 二级缓存中的类缓存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        // 证明二级缓存存在
        Customer c1 = (Customer) session.get(Customer.class, 1); // 有select语句
        session.clear(); // 清空一级缓存中的内容
        Customer c2 = (Customer) session.get(Customer.class, 1); // 没有select语句

        // 二级缓存中的类缓存
        System.out.println(c1 == c2); // false
        // 由以上说明,二级缓存中的类缓存在缓存数据时,并不是以对象的形式进行缓存的,而是缓存的是对象数据的散列,每次从二级缓存中取出数据,会在类缓存中组装成对象,并返回对象。

        session.getTransaction().commit();
        session.close();
    }
}

3.3.3、集合缓存

  如下图所示:

day32_Hibernate学习笔记_04

day32_Hibernate学习笔记_04
  示例代码如下:
package com.itheima.a_one2one;

import java.util.Iterator;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;

// 演示:二级缓存操作
public class Demo2 {
    @Test
    // 演示:二级缓存中的集合缓存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c1 = (Customer) session.get(Customer.class, 1); // 一条 select 语句,查客户,是类
        for (Order o : c1.getOrders()) { // 一条 select 语句,查客户的订单,是集合
            System.out.println(o.getOname());
        }

        session.clear(); // 清空一级缓存

        // 二级缓存中的集合缓存
        Customer c2 = (Customer) session.get(Customer.class, 1); // 没有select语句
//        // 增强for遍历
//        for (Order o : c1.getOrders()) { // 没有select语句
//            System.out.println(o.getOname());
//        }
        // 迭代器遍历
        Iterator<Order> it = c2.getOrders().iterator(); // 没有select语句
        while (it.hasNext()) {
            Order o = it.next();
            System.out.println(o.getOname());
        }
        // 如果在使用二级缓存的集合缓存的时候,如果把集合对应的类缓存没有配置上去的话,那么在遍历集合中的元素的时候,select语句会发送很多次。
        // 由上可知,二级缓存中的集合缓存中放的是对象的OID,每次从二级缓存中取出数据时,会根据IOD先从类缓存中查找OID对应的数据,如果没找到,会拿着OID从数据库中找。

        session.getTransaction().commit();
        session.close();
    }
}

3.3.4、查询缓存

  • 查询缓存又称为三级缓存(民间叫法)
  • 查询缓存默认不使用。需要手动开启。
  • 查询缓存:将HQL语句与查询结果进行绑定。通过HQL相同语句可以缓存内容。
    默认情况Query对象只将查询结果存放在一级和二级缓存中,不从一级或二级缓存中获取。
    查询缓存就是让Query可以从二级缓存中获得内容。

步骤一:开启查询缓存
  先在 hibernate.properties 中找到对应的键和值:

day32_Hibernate学习笔记_04
  再在 hibernate.cfg.xml 中配置开启查询缓存:
day32_Hibernate学习笔记_04
步骤二:在查询query对象时,需要设置缓存内容(注意:存放和查询 都需要设置
day32_Hibernate学习笔记_04
  示例代码如下:
package com.itheima.a_one2one;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;

// 演示:二级缓存操作
public class Demo3 {
    @SuppressWarnings({ "unused", "unchecked" })
    @Test
    // 演示:二级缓存中的查询缓存
    // 查询缓存是对hql语句查询的缓存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Query query = session.createQuery("from Customer");
        // 使用二级缓存中的查询缓存时,需要单独再打开
        query.setCacheable(true); // 查询时,会先从二级缓存中取结果,取不到就执行语句,将执行结果放入二级查询缓存中
        List<Customer> list = query.list(); // 一条 select 语句

        session.clear(); // 清空一级缓存

        Query query2 = session.createQuery("select c from Customer c");
        query2.setCacheable(true);
        List<Customer> list2 = query2.list(); // 没有select语句

        session.getTransaction().commit();
        session.close();
    }
}

3.3.5、时间戳缓存

  • 时间戳:任何操作都在时间戳中记录操作时间。

  示例代码如下:

package com.itheima.a_one2one;

import org.hibernate.Session;
import org.junit.Test;

import com.itheima.domain.Customer;
import com.itheima.utils.HibernateUtils;

// 演示:二级缓存操作
public class Demo4 {
    @SuppressWarnings("unused")
    @Test
    // 演示:时间戳缓存
    public void fun1() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();

        Customer c1 = (Customer) session.get(Customer.class, 1); // 一条 select 语句
        session.createQuery("update Customer set cname=:cname where cid =:cid").setString("cname", "rose").setInteger("cid", 1).executeUpdate(); // 使用HQL绑定参数
        session.clear(); // 清空一级缓存
        Customer c2 = (Customer) session.get(Customer.class, 1); // 又一条 select 语句,因为所有的操作都会在时间戳中进行记录,如果数据不一致(时间戳不一样),将触发select语句进行查询

        session.getTransaction().commit();
        session.close();
    }
}

  二级缓存中类缓存、集合缓存、查询缓存、时间戳缓存的总结图:

day32_Hibernate学习笔记_04

3.4、ehcache配置文件详解

<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"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
</ehcache>

<!-- 
    <diskStore path="java.io.tmpdir"/>  设置临时文件存放位置。(缓存一般写入内存,一定程度时,写入硬盘。)

    缓存详细设置
    <defaultCache />    所有的缓存对象默认的配置
    <cache name="类">    指定对象单独配置

    参数设置
        maxElementsInMemory="10000"     内存最大数(类内存中存储对象数据的散列的最大数)
        eternal="false"                 是否永久(内存常驻留)
        timeToIdleSeconds="120"         对象在内存中最多空闲多少秒
        timeToLiveSeconds="120"         对象在内存中最多存活多少秒
        overflowToDisk="true"           内存满了,是否写入到硬盘
        maxElementsOnDisk="10000000"    硬盘最大数(硬盘中存储对象数据的散列的最大数)
        diskPersistent="false"          关闭JVM,是否将内存保存硬盘中
        diskExpiryThreadIntervalSeconds="120"  轮询
        memoryStoreEvictionPolicy="LRU"
            Least Recently Used (specified as LRU)
            First In First Out (specified as FIFO) 
            Less Frequently Used (specified as LFU)

        •maxElementsInMemory    设置基于内存的缓存中可存放的对象最大数目 
        •eternal                设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性,默认值是false 
        •timeToIdleSeconds      设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。 
        •timeToLiveSeconds      设置对象生存最长时间,超过这个时间,对象过期。
                                如果此值为0,表示对象可以无限期地存在于缓存中。该属性值必须大于或等于 timeToIdleSeconds属性值。
 &nb
(0)
打赏 day32_Hibernate学习笔记_04 微信扫一扫

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

day32_Hibernate学习笔记_04
验证码: day32_Hibernate学习笔记_04