Hibernate学习笔记(十):一级缓存

项目结构:

实体类 User.java:

package com.hibernate.status;

import javax.persistence.*;
import java.util.Objects;

public class User {
    private int id;
    private String username;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                '}';
    }
}

映射文件 User.hbm.xml:

<?xml version="1.0"?>
<!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.hibernate.status">
	<class name="User" >
		<id name="id">
			<generator class="native"/>
		</id>
		<property name="username"/>
	</class>
</hibernate-mapping>

主配置文件 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">jdbc:mysql://localhost:3306/hib_demo</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <property name="show_sql">true</property>
        <property name="hbm2ddl.auto">update</property>
    </session-factory>
</hibernate-configuration>

测试程序 CacheDemo.java:

package com.hibernate.status;

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.Iterator;
import java.util.List;

/**
 *  为什么要用缓存?
 *      减少对数据库的访问次数,从而提升 hibernate 的执行效率;
 *
 *  hibernate 中的缓存分为 一级缓存,和 二级缓存;
 *
 *  一级缓存概念:
 *      1、hibernate 中的一级缓存,也叫做 session 缓存,他可以在 session 范围内减少数据库的访问次数;
 *          只在 session 范围有效,session 关闭,一级缓存失效;
 *      2、不同的 session 具有不同的缓存区,不会共享缓存数据;
 *      3、当调用 save/update/saveOrUpdate/get/load/list/iterator 等方法的时候,都会把对象放入 session 的缓存中;
 *      4、session 的缓存由 hibernate 维护,用户不能操作缓存内容;如果想操作缓存,必须通过 hibernate 提供的
 *          flush()、evict()、clear() 方法;
 *
 *  一级缓存特点:
 *      只在当前 session 范围有效,作用时间短,效果不是特别明显;
 *      在短时间内多次操作数据库,效果比较明显;
 *
 *  操作缓存的几个方法:
 *      session.flush():刷新缓存,让缓存数据同步到数据库中;
 *      session.evict():清空一级缓存中指定的对象;
 *      session.clear():清空一级缓存中缓存的所有对象;
 *
 *  一般在批量操作时,需要自己管理缓存:
 *      先同步数据库:session.flush()
 *      然后清除缓存:session.clear()
 */
public class CacheDemo {

    private static SessionFactory sessionFactory;
    static {
        sessionFactory = new Configuration()
                .configure()
                .addClass(User.class)   // 自动加载映射文件,测试时候使用
                .buildSessionFactory();
    }

    /**
     * 一级缓存
     */
    @Test
    public void testCache() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // 获取数据
        User user = session.get(User.class, 1);
        System.out.println(user);

        /**
         * hibernate 获取对象数据以后,会把对象放入缓存(一级缓存)中;
         * 修改对象属性的时候,会先把修改后的对象和 缓存中的对象进行比较;
         * 如果对象一样,则不更改数据库,如果对象不一样,则更新数据库;
         */
        user.setUsername("Tom");

        /**
         * 获取数据时,先检查缓存中是否有匹配的对象数据;如果没有,则向数据库查询数据;
         * 如果缓存中有数据,则不向数据库查询,直接从缓存中获取;
         * 此次获取数据,不会向数据库查询,因为前面已经获取到数据,存入缓存了;
         */
        user = session.get(User.class, 1);

        // 提交事务,关闭 session
        transaction.commit();
        session.close();
    }

    /**
     * flush() 刷新缓存:让缓存数据同步到数据库
     */
    @Test
    public void testFlush() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // 获取数据
        User user = session.get(User.class, 1);

        /**
         * 执行下面两行代码,只生成一条 update 语句,更新最后设置的数据到数据库;
         * 因为执行上面的 get() 方法后,会把查询的 user 对象放入缓存中,下面修改对象的属性,
         * 也是把修改后的对象放入缓存中,直到调用 transaction.commit() 方法,commit() 方法
         * 中调用了 session.flush() 方法,刷新缓存,将缓存数据同步到数据库;
         */
        user.setUsername("张三2号");
        user.setUsername("张三3号");

        // 手动刷新缓存:单步调试可以发现,调用 session.flush() 方法的时候,执行 update 语句,将缓存数据同步到数据库;
        session.flush();

        // 再次修改对象属性,执行 transaction.commit() 方法时,会将修改过的对象 重新更新到数据库;
        user.setUsername("Tom");

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

    /**
     * clear()、evict() 清除缓存
     */
    @Test
    public void testClear() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // 获取数据
        User user = session.get(User.class, 2);

        // 清除缓存:如果不清除缓存,两次 get() 方法只会向数据库查询一次,第二次查询直接从缓存中获取数据;
        // 清除缓存后,两次 get() 方法都会向数据库查询数据
        session.clear();    // 清除缓存中的所有内容
        session.evict(user);// 清除缓存中指定的对象

        user = session.get(User.class, 2);

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

    /**
     * list 查询:
     *  一次把所有的记录都查询出来,只生成一条 select 语句;
     */
    @Test
    public void testList() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // HQL 查询
        Query query = session.createQuery("from User");

        // list 方法:只生成一条 select 语句,一次性把数据库的数据全部查询出来
        List<User> list = query.list();
        for (User user : list) {
            System.out.println(user);
        }

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

    /**
     * Iterator 查询:
     *  根据生成的 sql 语句可以发现,
     *  首先生成的一条 select 语句,是查询表中的所有 id;
     *  然后再根据每个 id 从数据库 查询数据;
     *
     *  所以生成的 select 语句比表中的记录数多一条,叫做 N+1 查询;N 表示表中的记录数;
     */
    @Test
    public void testIterator() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // HQL 查询
        Query query = session.createQuery("from User");

        // Iterator 方法:N + 1 查询
        Iterator iterate = query.iterate();
        while (iterate.hasNext()){
            User user = (User) iterate.next();
            System.out.println(user);
        }

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

    /**
     * 测试 iterator 查询的数据会不会放入缓存,并从缓存获取:
     *  iterator 查询的数据会放入缓存,也会从缓存中获取数据;
     */
    @Test
    public void testIteratorCache() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // HQL 查询
        Query query = session.createQuery("from User");

        // 第一次调用 Iterator 方法
        Iterator iterate = query.iterate();
        while (iterate.hasNext()){
            User user = (User) iterate.next();
            System.out.println(user);
        }

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

        // 第二次调用 Iterator 方法
        // 从生成的 sql 语句可以发现,第二次调用 iterator 的时候,只生成了一条查询所有 id 的 select 语句,
        // 并没有生成从数据库查询数据的 select 方法;
        // 说明第一次调用 iterator 的时候,已经将查询的数据放入了缓存;第二次调用 iterator 的时候,
        // 只要查询到 id,然后再从缓存中根据 id 查询数据;
        iterate = query.iterate();
        while (iterate.hasNext()){
            User user = (User) iterate.next();
            System.out.println(user);
        }

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

    /**
     * 测试 list 查询的数据会不会放入缓存
     *  list 查询的数据会放入缓存,但不会从缓存中获取数据;
     */
    @Test
    public void testListCache() {
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();

        // HQL 查询
        Query query = session.createQuery("from User");

        // 第一次调用 list 方法
        List<User> list = query.list();
        for (User user : list) {
            System.out.println(user);
        }

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

        // 第二次调用 list 方法
        // 从生成的 sql 语句可以发现,第二次调用 list 方法的时候,同样生成了一条从数据库
        // 查询数据的 select 方法,说明 list 方法不能从缓存中获取数据;
        list = query.list();
        for (User user : list) {
            System.out.println(user);
        }

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

        // 调用 Iterator 方法:从生成的 sql 语句发现,只生成了一条查询 id 的 select 语句,
        // 说明前面 list 查询的数据放入了缓存中,此处 iterator 方法查询到 id 以后,直接
        // 根据 id 从缓存中获取数据了;
        Iterator iterate = query.iterate();
        while (iterate.hasNext()){
            User user = (User) iterate.next();
            System.out.println(user);
        }

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

最后一个 “测试 list 查询的数据会不会放入缓存” 结果:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值