Hibernate(四)

1 二级缓存

1.1 概念

1.1.1 缓存

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

1.1.2 二级缓存

  • Hibernate提供缓存机制:一级缓存、二级缓存。
  • 一级缓存:Session级别的缓存,在一次请求中共享数据。
  • 二级缓存:SessionFactory级别的缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
  • SessionFactory缓存分为两部分:
  • 内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外是只读的。不能写入。
  • 外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存Hibernate只提供规范(接口),需要第三方来实现。

1.1.3 二级缓存的结构

1.1.4 并发访问策略

  •  transactional:事务型。
    • 仅在受管理的缓存中适用。
    • 提供Repeateable Read事务隔离级别。
    • 适用经常被读,很少修改的数据。
    • 可以防止脏读和不可重复读的并发问题。
    • 缓存支持事务,发生异常的时候,缓存也能够回滚。
  • read-write:读写型。
    • 提供Read Committed事务隔离级别。
    • 在非集群的环境中适用。
    • 使用经常被读,很少修改的数据。
    • 可以防止脏读。
    • 更新缓存的时候会锁定缓存中的数据。  
  • nonstrict-read-write:非严格读写型。
    • 适用极少被修改,偶尔允许脏读的数据(两个事务同时修改数据的情况很少见)。
    • 不保证缓存和数据库中数据的一致性。
    • 为缓存数据设置很短的过期时间,从而尽量避免脏读。
    • 不锁定缓存中的数据。  
  • read-only:只读型。
    • 适用从来不会被修改的数据。
    • 在此模式下,如果对数据进行更新操作,会有异常。
    • 事务隔离级别低,并发性能高。
    • 在集群环境中也能完美运作。  

1.1.5 应用场景

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

1.1.6 二级缓存的提供商

  • Hibernate中只定义了二级缓存的接口,实现需要自己选择提供商。
  • EHCache:可作为进程(单机)范围内的缓存,存放数据的物理介质可以是内存或者硬盘,对Hibernate的查询缓存提供了支持。支持集群。
  • OpenSymphony:可作为进程范围内的缓存,存放数据的物理介质可以是内存或者硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
  • SwarmCache:可作为集群范围内的缓存,但是不支持Hibernate的查询缓存。
  • JBossCache:可作为集群范围内的缓存,支持Hibernate的查询缓存。

1.2 整合

1.2.1 ehcache的坐标

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.8.1</version>
</dependency>

1.2.2 配置hibernate.cfg.xml

<!--配置二级缓存-->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.SingletonEhcacheRegionFactory</property>
  • 完整hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!--配置数据库连接参数-->
        <property name="connection.url">
            <![CDATA[
                jdbc:mysql://localhost:3306/hibernate?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
            ]]>
        </property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">123456</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
        <!--是否显示SQL-->
        <property name="show_sql">true</property>
        <!--是否格式化SQL-->
        <property name="format_sql">true</property>
        <!--是否自动提交事务-->
        <property name="connection.autocommit">true</property>

        <property name="hbm2ddl.auto">update</property>

        <property name="hibernate.id.new_generator_mappings">false</property>

        <!--配置c3p0-->
        <property name="hibernate.connection.provider_class">org.hibernate.c3p0.internal.C3P0ConnectionProvider
        </property>
        <property name="hibernate.c3p0.max_size">2</property>
        <property name="hibernate.c3p0.min_size">2</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_statements">100</property>
        <property name="hibernate.c3p0.acquire_increment">2</property>

        <!--配置二级缓存-->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.SingletonEhcacheRegionFactory</property>


        <!--设置隔离级别-->
        <property name="hibernate.connection.isolation">4</property>

        <!--配置JavaBean和表的映射关系-->
        <mapping class="com.sunxiaping.domain.Customer"/>
        <mapping class="com.sunxiaping.domain.Order"/>


    </session-factory>
</hibernate-configuration>
View Code

1.2.3 配置类和集合缓存

  • Customer.java
package com.sunxiaping.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;


/**
 * 客户
 */
@Setter
@Getter
@NoArgsConstructor
@Entity
@Table(name = "`customer`")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Customer implements Serializable {
    /**
     * 主键
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    /**
     * 客户的名称
     */
    private String name;

    /**
     * 一个客户有许多订单
     * 一方放弃外键的维护权
     */
    @OneToMany(mappedBy = "customer", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
    private Set<Order> orders = new HashSet<>();


}
  • Order.java
package com.sunxiaping.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;

/**
 * 订单
 */
@Setter
@Getter
@NoArgsConstructor
@Entity
@Table(name = "`order`")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class Order implements Serializable {
    /**
     * 主键
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    /**
     * 订单名称
     */
    private String name;
    /**
     * 价格
     */
    private BigDecimal price;

    /**
     * 一个订单属于一个客户,多方维护外键
     */
    @ManyToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
    @JoinColumn(name = "cid")
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Customer customer;

}

1.2.4 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="D:\\temp"/>
    <!-- maxElementsInMemory 缓存最大个数 -->
    <!-- eternal 是否永久有效,如果该值设置为true,则timeout将失效 -->
    <!-- timeToIdleSeconds 失效前的闲置时间 -->
    <!-- timeToLiveSeconds 失效前的存活时间 -->
    <!-- diskSpoolBufferSizeMB 缓存区的大小 默认30M -->
    <!-- maxElementsOnDisk 硬盘最大缓存个数 -->
    <!-- overflowToDisk 超出缓存最大个数时写入硬盘 -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
    />
</ehcache>
View Code

1.3 测试

1.3.1 证明二级缓存的存在

  • 示例:
package com.sunxiaping.test;

import com.sunxiaping.domain.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

public class HibernateTest {

    @Test
    public void test() {

        Configuration configuration = new Configuration().configure();

        SessionFactory sessionFactory = configuration.buildSessionFactory();

        Session session = sessionFactory.openSession();

        Transaction transaction = session.beginTransaction();

        //执行查询后,放在一级缓存和二级缓存中
        Customer c1 = session.get(Customer.class, 1);
        System.out.println(c1);

        session.clear();

        //如果二级缓存中有数据,将从二级缓存中取数据
        Customer c2 = session.get(Customer.class, 1);
        System.out.println(c2);

        transaction.commit();

        session.close();
        sessionFactory.close();
    }

}

  • 总结:类级别缓存,只缓存数据,而一级缓存缓存的是对象本身。

1.3.2 证明集合级别的缓存

  •  示例:
package com.sunxiaping.test;

import com.sunxiaping.domain.Customer;
import com.sunxiaping.domain.Order;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import java.util.Set;

public class HibernateTest {

    @Test
    public void test() {

        Configuration configuration = new Configuration().configure();

        SessionFactory sessionFactory = configuration.buildSessionFactory();

        Session session = sessionFactory.openSession();

        Transaction transaction = session.beginTransaction();

        //执行查询后,放在一级缓存和二级缓存中
        Customer c1 = session.get(Customer.class, 1);
        System.out.println(c1);
        Set<Order> orders = c1.getOrders();
        System.out.println(orders);

        session.clear();

        //如果二级缓存中有数据,将从二级缓存中取数据
        Customer c2 = session.get(Customer.class, 1);
        System.out.println(c2);
        //默认二级缓存里面缓存的是Order的id,然后根据ID从类级别缓存中查找
        Set<Order> orders1 = c2.getOrders();
        System.out.println(orders1);

        transaction.commit();

        session.close();
        sessionFactory.close();
    }

}

  • 总结:集合缓存只缓存了关联对象的OID的值,如果需要,则从类缓存中寻找,如果类缓存没有,则从数据库中查找。

1.3.3 查询缓存

  • 查询缓存默认不使用,需要手动开启。
  • 查询缓存:将HQL语句和查询结果进行绑定。通过HQL相同语句可以缓存内容。
  • 默认情况下Query只将查询结果放在一级缓存和二级缓存中,但是却不从一级和二级缓存中获取。
  • 查询缓存可以让Query从二级缓存中获取。
  • 示例:
package com.sunxiaping.test;

import com.sunxiaping.domain.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;

import java.util.List;

public class HibernateTest {

    @Test
    public void test() {
        Configuration configuration = new Configuration().configure();
        SessionFactory sessionFactory = configuration.buildSessionFactory();
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        Query query = session.createQuery(" FROM Customer ");
        query.setCacheable(true);//设置允许缓存
        List<Customer> list = query.list();
        for (Customer customer : list) {
            System.out.println(customer);
        }

        Customer customer1 = session.get(Customer.class, 1);
        System.out.println(customer1);

        transaction.commit();
        session.close();

        sessionFactory.close();
    }

}

1.3.4 时间戳缓存

  •  任何操作都在时间戳中记录操作时间。
  • 示例:
package com.sunxiaping.test;

import com.sunxiaping.domain.Customer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.junit.Test;

public class HibernateTest {

    @Test
    public void test() {
        Configuration configuration = new Configuration().configure();
        SessionFactory sessionFactory = configuration.buildSessionFactory();
        Session session1 = sessionFactory.openSession();
        Transaction transaction = session1.beginTransaction();

        /**
         * 先修改用户信息
         */
        Query query = session1.createQuery(" update Customer set name =:name where id =:id");
        query.setParameter("name", "李四");
        query.setParameter("id", 1);
        query.executeUpdate();

        transaction.commit();
        session1.close();

        Session session2 = sessionFactory.openSession();
        Transaction transaction2 = session2.beginTransaction();

        Customer customer = session2.get(Customer.class, 1);
        System.out.println(customer);

        transaction2.commit();
        session2.close();
        sessionFactory.close();
    }

}

猜你喜欢

转载自www.cnblogs.com/xuweiweiwoaini/p/11562579.html