hibernate学习笔记之一

Hibernate是一个开放源代码对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JavaEE架构中取代CMP,完成数据持久化的重任。----百度百科

了解完Hibernate是什么后,我们正式开始Hibernate。

Hibernate ORM

先来说说什么是jdbc

​ JDBC 代表 Java Database Connectivity ,它是提供了一组 Java API 来访问关系数据库的 Java 程序。这些 Java APIs 可以使 Java 应用程序执行 SQL 语句,能够与任何符合 SQL 规范的数据库进行交互。

​ JDBC 提供了一个灵活的框架来编写操作数据库的独立的应用程序,该程序能够运行在不同的平台上且不需修改,能够与不同的 DBMS 进行交互。

jdbc的优缺点

JDBC 的优点JDBC 的缺点
干净整洁的 SQL 处理大项目中使用很复杂
大数据下有良好的性能很大的编程成本
对于小应用非常好没有封装
易学的简易语法难以实现 MVC 的概念
查询需要指定 DBMS

为什么是对象关系映射(ORM)?

我们先看一个实体类以及对应的数据库表

@Setter
@Getter
@NoArgsConstructor
public class User {
    private String id;

    private String name;

    private String password;

    private Date createTime;

    private Date expireTime;

    public User(String name,String password,Date createTime,Date expireTime){
        this.name = name;
        this.password = password;
        this.createTime = createTime;
        this.expireTime = expireTime;
    }
}
create table `user` (
	`id` varchar (765),
	`name` varchar (765),
	`password` varchar (765),
	`createTime` datetime ,
	`expireTime` datetime ,
    PRIMARY KEY (`id`)
); 

我们发现实体类与表的关系一旦确定那么,就能难在进行改变了。

如果在开发中,我们写了大半,却要修改数据库到的设计怎么办?

在关系型数据库中加载和存储对象时我们要面临以下五个不匹配的问题,怎么解决?

在这里插入图片描述

ORM就能够很好的解决。

什么是ORM

ORM 表示 Object-Relational Mapping (ORM),是一个方便在关系数据库和类似于 Java, C# 等面向对象的编程语言中转换数据的技术。一个 ORM 系统相比于普通的 JDBC 有以下的优点。

在这里插入图片描述

ORM解决方案由一下四个实体组成

在这里插入图片描述

hibernate 简介

Hibernate是一个开放源代码对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JavaEE架构中取代CMP,完成数据持久化的重任。----百度百科

Hibernate 是传统 Java 对象和数据库服务器之间的桥梁,用来处理基于 O/R 映射机制和模式的那些对象。

在这里插入图片描述

Hibernate 优势

  • Hibernate 使用 XML 文件来处理映射 Java 类别到数据库表格中,并且不用编写任何代码。
  • 为在数据库中直接储存和检索 Java 对象提供简单的 APIs。
  • 如果在数据库中或任何其它表格中出现变化,那么仅需要改变 XML 文件属性。
  • 抽象不熟悉的 SQL 类型,并为我们提供工作中所熟悉的 Java 对象。
  • Hibernate 不需要应用程序服务器来操作。
  • 操控你数据库中对象复杂的关联。
  • 最小化与访问数据库的智能提取策略。
  • 提供简单的数据询问。

Hibernate 架构

​ Hibernate 架构是分层的,作为数据访问层,你不必知道底层 API 。Hibernate 利用数据库以及配置数据来为应用程序提供持续性服务(以及持续性对象)。

在这里插入图片描述

详细的 Hibernate 应用程序体系结构视图以及一些重要的类

在这里插入图片描述

Hibernate 使用不同的现存 Java API,比如 JDBC,Java 事务 API(JTA),以及 Java 命名和目录界面(JNDI)。JDBC 提供了一个基本的抽象级别的通用关系数据库的功能, Hibernate 支持几乎所有带有 JDBC 驱动的数据库。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器相集成。

配置对象

配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。

  • 数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.propertieshibernate.cfg.xml
  • 类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。

SessionFactory 对象

配置对象被用于创造一个 SessionFactory 对象,使用提供的配置文件为应用程序依次配置 Hibernate,并允许实例化一个会话对象。SessionFactory 是一个线程安全对象并由应用程序所有的线程所使用。

SessionFactory 是一个重量级对象所以通常它都是在应用程序启动时创造然后留存为以后使用。每个数据库需要一个 SessionFactory 对象使用一个单独的配置文件。所以如果你使用多种数据库那么你要创造多种 SessionFactory 对象。

Session 对象

一个会话被用于与数据库的物理连接。Session 对象是轻量级的,并被设计为每次实例化都需要与数据库的交互。持久对象通过 Session 对象保存和检索。

Session 对象不应该长时间保持开启状态因为它们通常情况下并非线程安全,并且它们应该按照所需创造和销毁。

Transaction 对象

一个事务代表了与数据库工作的一个单元并且大部分 RDBMS 支持事务功能。在 Hibernate 中事务由底层事务管理器和事务(来自 JDBC 或者 JTA)处理。

这是一个选择性对象,Hibernate 应用程序可能不选择使用这个接口,而是在自己应用程序代码中管理事务。

Query 对象

Query 对象使用 SQL 或者 Hibernate 查询语言(HQL)字符串在数据库中来检索数据并创造对象。一个查询的实例被用于连结查询参数,限制由查询返回的结果数量,并最终执行查询。

Criteria 对象

Criteria 对象被用于创造和执行面向规则查询的对象来检索对象。

Hibernate 配置

Hibernate 需要事先知道在哪里找到映射信息,这些映射信息定义了 Java 类怎样关联到数据库表。Hibernate 也需要一套相关数据库和其它相关参数的配置设置。所有这些信息通常是作为一个标准的 Java 属性文件提供的,名叫 hibernate.properties。又或者是作为 XML 文件提供的,名叫 hibernate.cfg.xml

Hibernate属性

重要主属性列表

S.N.属性和描述
1hibernate.dialect 这个属性使 Hibernate 应用为被选择的数据库生成适当的 SQL。
2hibernate.connection.driver_class JDBC 驱动程序类。
3hibernate.connection.url 数据库实例的 JDBC URL。
4hibernate.connection.username 数据库用户名。
5hibernate.connection.password 数据库密码。
6hibernate.connection.pool_size 限制在 Hibernate 应用数据库连接池中连接的数量。
7hibernate.connection.autocommit 允许在 JDBC 连接中使用自动提交模式。

Hibernate会话

会话

Session 用于获取与数据库的物理连接。 Session 对象是轻量级的,并且设计为在每次需要与数据库进行交互时被实例化。持久态对象被保存,并通过 Session 对象检索找回。

Hibernation 映射文件

这里直接使用上面的表与实体类

基于这两个实体之上,我们可以定义下列映射文件来指示 Hibernate 如何将已定义的类或类组与数据库表匹配。

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
   "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
 <class name="com.hibernate.User">
 <id name="id">
   <generator class="uuid"/> 
   </id>
   <property name="name"/> 
   <property name="password"/> 
   <property name="createTime"/> 
   <property name="expireTime"/> 
 </class>
</hibernate-mapping>

需要以格式 <classname>.hbm.xml保存映射文件。我们保存映射文件在 User.hbm.xml 中。

标签的使用:

  • 映射文件是一个以 <hibernate-mapping> 为根元素的 XML 文件,里面包含所有<class>标签。
  • <class> 标签是用来定义从一个 Java 类到数据库表的特定映射。Java 的类名使用 name 属性来表示,数据库表明用 table 属性来表示。
  • <meta> 标签是一个可选元素,可以被用来修饰类。
  • <id> 标签将类中独一无二的 ID 属性与数据库表中的主键关联起来。id 元素中的 name 属性引用类的性质,column 属性引用数据库表名的列。type 属性保存 Hibernate 映射的类型,这个类型会将从 Java 转换成 SQL 数据类型。
  • 在 id 元素中的 <generator> 标签用来自动生成主键值。设置 generator 标签中的 class 属性可以设置 native 使 Hibernate 可以使用 identity, sequencehilo 算法根据底层数据库的情况来创建主键。
  • <property> 标签用来将 Java 类的属性与数据库表的列匹配。标签中 name 属性引用的是类的性质,column 属性引用的是数据库表的列。type 属性保存 Hibernate 映射的类型,这个类型会将从 Java 转换成 SQL 数据类型。

映射类型

在这里插入图片描述

介绍完上面的知识,我们就可以开始Hibernate的第一个安利了

第一个Hibernate程序

本人创建的是maven项目

目录结构

在这里插入图片描述

pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>hibernate</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>hibernate Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
    </dependency>
    
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>
    
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-c3p0</artifactId>
      <version>4.3.5.Final</version>
    </dependency>

    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>

    <!-- 添加Hibernate依赖 -->


  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-annotations</artifactId>
    <version>3.2.1.ga</version>
  </dependency>
  <dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-entitymanager</artifactId>
    <version>3.2.1.ga</version>
  </dependency>

<!--    SLF4J-->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.2</version>
    </dependency>

    <!-- 添加javassist -->
    <dependency>
      <groupId>javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.12.0.GA</version>
    </dependency>

    <!-- mysql数据库的驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
  </dependencies>

  <build>

    <!--配置Maven 对resource文件 过滤 -->
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
          <include>**/*.xls</include>
          <include>**/*.xlsx</include>
        </includes>
        <filtering>true</filtering>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
      </resource>
    </resources>

  </build>
</project>

编写一个实体类
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private String id;

    private String name;

    private String password;

    private Date createTime;

    private Date expireTime;

    public User(String name,String password,Date createTime,Date expireTime){
        this.name = name;
        this.password = password;
        this.createTime = createTime;
        this.expireTime = expireTime;
    }
}

由于 Hibernate 默认访问的是各个属性的 getter 和 setter 方法,所以为了实现类的封装性,建议为持久化类的各个属性添加 getter 和 setter 方法。

通常持久化类的编写应该遵循一些规则,具体如下。

  • 提供一个无参数的 public 访问控制符的构造器。
  • 持久化类中所有属性使用 private 修饰。
  • 所有属性提供 public 修饰的 setter 和 getter 方法。
  • 提供一个标识属性 OID,映射数据表主键字段,例如 User 表的 id 属性。
  • 标识属性应尽量使用基本数据类型的包装类型,目的是为了与数据库表的字段默认值 null 一致。
  • 不要用 final 修饰持久化类,否则无法生成代理对象。
编写映射文件User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <!-- name代表的是类名 ,table代表表名,table="user"可不写-->
    <class name="com.hibernate.User"  table="user">
        <!-- name代表的是User类中的id属性,column代表的是user表中的主键id, column="id"可不写-->
        <id name="id" column="id">
            <!-- 主键生成策略 -->
            <generator class="increment"/>
        </id>
        <!-- 其他属性使用property标签映射 -->
        <property name="name"/>
        <property name="password"/>
        <property name="createTime"/>
        <property name="expireTime"/>
    </class>
</hibernate-mapping>

上述代码展示了实体类 User 与数据表 user 的映射关系。在上述映射文件中:

  • 节点用于配置实体类的映射信息,其中 name 属性表示实体类的完整类名,table 属性表示数据表的名称。
  • 节点用于定义实体类的唯一标识(对应数据表的主键),是 节点下的必须节点,其 name 属性对应实体类的属性, 用于对应数据库表中的列,这里即为表的主键, 子节点用于指定主键的生成策略。
  • 节点用于映射普通属性,其 name 属性对应实体中的属性,column 属性对应数据库表中的字段,type 属性表示其属性的类型。
编写核心配置文件 hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
      "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
      "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
   <session-factory>
      <!-- 链接数据库url -->
      <property name="hibernate.connection.url">jdbc:mysql://localhost/hibernatelearn</property>
      <!-- 数据库驱动 -->
      <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
      <!-- 连接数据库的用户名 -->
      <property name="hibernate.connection.username">root</property>
      <!-- 数据库的密码 -->
      <property name="hibernate.connection.password">root</property>
      <!-- 指定方言 -->
      <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
      <!-- 显示sql语句 -->
      <property name="hibernate.show_sql">true</property>
      <!-- 格式化sql语句 -->
      <property name="format_sql">true</property>
      
      <property name="hibernate.hbm2ddl.auto">update</property>
      <!-- 映射文件配置 -->
      <mapping resource="com/hibernate/User.hbm.xml"/>
   </session-factory>
</hibernate-configuration>

hibernate.hbm2ddl.auto参数的作用主要用于:自动创建|更新|验证数据库表结构。如果不是此方面的需求建议set value=“none”。
create:
每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,这就是导致数据库表数据丢失的一个重要原因。
create-drop :
每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
update:
最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,**即使表结构改变了但表中的行仍然存在不会删除以前的行。**要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate :
每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

数据操作:

    @Test
    public void test(){
// 1.创建Configuration对象并加载hibernate.cfg.xml配置文件
        Configuration config = new Configuration().configure();
        // 2.获取SessionFactory
        SessionFactory sessionFactory = config.buildSessionFactory();
        // 3.得到一个Session
        Session session = sessionFactory.openSession();
        // 4.开启事务
        Transaction transaction = session.beginTransaction();
        // 5.执行持久化操作
        User user = new User();
        user.setName("zhangsan");
        user.setPassword("12345");
        user.setCreateTime(new Date());
        user.setExpireTime(new Date());
        // 将对象保存到表中
        session.save(user);
/*
        // 更新数据
        session.update(user);
        
        //查询操作
        Object o = session.get(User.class, 1);

        //删除操作
        User user2 = (User) session.get(User.class, 1);
        session.delete(user2);
        
*/
        // 6.提交事务
        transaction.commit();
        // 7.关闭资源
        session.close();
        sessionFactory.close();
    }

在这里插入图片描述

图片来源
截图
在这里插入图片描述

Hibernate 运行流程

运行时的执行流程

在这里插入图片描述

图所示的 Hibernate 的执行流程过程具体如下。

1)创建 Configuration 实例,加载 Hibernate 核心配置文件和映射文件信息到 Configuration 对象中。

2)创建 SessionFactory 实例。通过 Configuration 对象读取到的配置文件信息创建 SessionFactory 对象,该对象中保存了当前数据库的配置信息和所有映射关系等信息。

3)创建 Session 实例,建立数据库连接。Session 主要负责执行持久化对象的增、删、改、查操作,创建一个 Session 就相当于创建一个新的数据库连接。

4)创建 Transaction 实例,开启一个事务。Transaction 用于事务管理,一个 Transaction 对象对应的事务可以包含多个操作。在使用 Hibernate 进行增、删、改操作时,必须先创建 Transaction 对象。需要注意的是,Hibernate 的事务默认是关闭的,需要手动开启事务和关闭事务。

5)利用 Session 接口通过的各种方法进行持久化操作。

6)提交事务,对实体对象持久化操作后,必须提交事务。

7)关闭 Session 与 SessionFactory,断开与数据库的连接。

Hibernate 映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!--映射文件的dtd约束信息-->
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <!-- name代表的是完整类名(包含的类名),table代表的是表名 -->
    <class name="className" table="tableName">
        <!-- name代表的是className类中的唯一标识属性,column代表的是tableName表中的主键id -->
        <id name="id" column="id">
            <!-- 主键生成策略 -->
            <generator class="native" />
        </id>
        <!-- name表示className的普通属性 column表示tableName表的普通字段 type 表示字段类型-->
        <property name="attrName" column="fieIdName" type="string" />
    </class>
</hibernate-mapping>

​ 首先进行了 xml 声明,然后定义了映射文件的 dtd 信息,此 dtd 信息读者不需要去手写,可以在项目的 Web App Libraries 目录(或 Referenced Libraries 目录)中,找到

<hibernate-mapping>元素
属性名是否必须说 明
package为映射文件中的类指定一个包前缀,用于非全限定类名
schema指定数据库 schema 名
catalog指定数据库 catalog 名
default-access指定 Hibernate 用于访问属性时所使用的策略,默认为 property。 当 default-access=“property” 时,使用 getter 和 setter 方法访问成员变量;当 default-access = "field"时,使用反射访问成员变量
default-cascade指定默认的级联样式,默认为空
default-lazy指定 Hibernate 默认所采用的延迟加载策略,默认为 true
<class> 元素

用于指定持久化类和数据表的映射关系,它是 XML 配置文件中的主要配置内容。

属性名是否必须说 明
name持久化类或接口的全限定名。如果未定义该属性,则 Hibernate 将 该映射视为非 POJO 实体的映射
table持久化类对应的数据库表名,默认为持久化类的非限定类名
catalog数据库 catalog 名称,如果指定该属性,则会覆盖 hibernate-mapping 元素中指定的 catalog 属性值
lazy指定是否使用延迟加载
<id>元素

元素内包含了一个 元素,该属性用于设定持久化类的 OID(Object identifier,对象标识符)和表的主键的映射

属性名是否必须说 明
name标识持久化类 OID 的属性名
type持久化类中标识属性的数据类型。如果没有为某个属性显式设定映射类型,Hibernate 会运用反射机制先识别出持久化类的特定属性的 Java 类型,然后自动使用与之对应的默认 Hibernate 映射类型。 Java 的基本数据类型和包装类型对应相同的 Hibernate 映射类型,基本数据类型无法表达 null,所以对于持久化类的 OID,推荐使用包装类型(integer,long,string 等)
column设置标识属性所映射的数据列的列名(主键字段的名字)
access指定 Hibernate 对标识属性的访问策略,默认为 property。若此处指定了该属性,则会覆盖 元素中指定的 default-access 属性
generator对于不同的关系型数据库和业务应用来说,其主键的生成方式往往也不同,有的是依赖数据库自增字段生成主键,有的是按照具体的应用逻辑决定

主键生成策略

1,自然主键

​ 把具有业务含义的字段作为主键,称为自然主键

2,代理主键

​ 把不具备业务含义的字段作为主键,称为代理主键

Hibernate中内置的主键生成策略

名 称描 述
increment用于 long.short 或 int 类型,由 Hibernate 自动以递增的方式生成唯一标识符,每次增量为 1。只有当没有其他进程向同一张表中插入数据时才可以使用,不能在集群环境下使用,适用于代理主键
identity采用底层数据库自身提供的主键生成标识符,条件是数据库支持自动增长数据类型。在 DB2、MySQL、SQL Server、Sybase 和 HypersonicSQL 数据库中可以使用该生成器,该生成器要求在数据库中把主键定义成为自增长类型,适用于代理主键
sequenceHibernate 根据底层数据库序列生成标识符。条件是数据库支持序列,适用于代理主键
hilo使用一个高/低位算法高效地生成 long、short 或 int 类型的标识符。给定一个表和字段(默认的表和字段分别为 hibernate_unique_key 和 next_hi)作为高位值的来源。高/低位算法产生的标识符仅在特定数据库中是唯一的
native根据底层数据库对自动生成标识符的能力选择 identity、sequence、hilo 三种生成器中的一种,适合跨数据库平台开发,适用于代理主键
uuidHibernate 采用 128 位的 UUID 算法生成标识符。该算法能够在网络环境中生成唯一的字符串标识符,其 UUID 被编码为一个长度为 32 位的十六进制字符串。这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间,适用于代理主键
assigned由 java 程序负责生成标识符,如果不指定 id 元素的 generator 属性,则默认使用该主键生成策略,适用于自然主键
<property>元素

<class>元素内可以包含多个 <property>子元素,它用于表示持久化类的其他属性和数据表中非主键字段的映射关系

属性名说 明
name持久化类属性的名称,以小写字母开头
column数据表字段名
type数据表的字段类型
length数据表字段定义的长度
lazy指定当持久化类的实例首次被访问时,是否对该属性使用延迟加载,其默认值是 false
unique是否对映射列产生一个唯一性约束。常在产生 DDL 语句或创建数据库对象时使用
not-null是否允许映射列为空

添加 C3P0 的配置信息

		<!-- C3P0连接池设定 -->
		<!-- 使用 C3P0连接池配置连接池提供的供应商 -->
		<property name="connection.provider_class">
			org.hibernate.c3p0.internal.C3P0ConnectionProvider
		</property>
		<!--在连接池中可用的数据库连接的最少数目 -->
		<property name="c3p0.min_size">5 </property>
		<!--在连接池中所有数据库连接的最大数目 -->
		<property name="c3p0.max_sizen">20 </property>
		<!--设定数据库连接的过期时间,以ms为单位,如果连接池中的某个数据库连接空闲状态的时间 超过timeout时间,则会从连接池中清除 -->
		<property name="c3p0.timeout">120 </property>
		<!--每3000s检查所有连接池中的空闲连接以s为单位 -->
		<property name="c3p0.idle_test_period">3000 </property>
		
		<!-- 映射文件配置 -->
		<mapping resource="com/hibernate/User.hbm.xml"/>

首先声明了 C3P0 的供应商信息,然后配置了连接池中的连接最小数目和最大数目等。配置完这些信息后,C3P0 连接池就可以使用了。

hibernate的核心接口

有六个常用的核心接口,它们分别是 Configuration、SessionFactory、Session、Transaction、Query 和 Criteria

Configuration

主要用于启动、加载和管理 Hibernate 的配置文件信息,在启动 Hibernate 的过程中,Configuration 实例首先确定 Hibernate 文件的位置,然后读取相关配置,最后创建一个唯一的 SessionFactory 实例。

Configuration config=new Configuration().configure();Configuration config = new Configuration().configure("文件的位置");

需要注意的是,Configuration 对象只存在于系统的初始化阶段,它将 SessionFactory 创建完成后,就完成了自己的使命。

SessionFactory

SessionFactory 接口负责读取并解析映射文件,以及建立 Session 对象,它在 Hibernate 中起到一个缓冲区的作用,会将 Configuration 对象中的所有配置信息、Hibernate 自动生成的 SQL 语句以及某些可重复利用的数据加载到缓冲区中。同时,它还维护了 Hibernate 的二级缓存。

SessionFactory 实例是通过 Configuration 对象获取的.

SessionFactory sessionFactory = config.buildSessionFactory();

SessionFactory 具有以下特点。

  • 它是线程安全的,它的同一个实例能够供多个线程共享。
  • 它是重量级的,不能随意创建和销毁它的实例。

SessionFactory 是一个重量级的对象,占用的内存空间较大,所以通常情况下,一个应用程序只需要一个 SessionFactory 实例,只有应用中存在多个数据源时,才为每个数据源建立一个 SessionFactory 实例。为此,在实际开发时,通常会抽取出一个工具类提供 Session 对象。

public class HibernateUtils {
    // 声明一个私有的静态final类型的Configuration对象
    private static final Configuration config;
    // 声明一个私有的静态的final类型的SessionFactory对象
    private static final SessionFactory factory;
    // 通过静态代码块构建SessionFactory
    static {
        config = new Configuration().configure();
        factory = config.buildSessionFactory();
    }
    // 提供一个公有的静态方法供外部获取,并返回一个session对象
    public static Session getSession() {
        return factory.openSession();
    }
}

Session

主要用于读取、创建和删除映射对象的实例,这一系列的操作将被转换为数据表中的增加、修改、查询和删除操作。

Session 是轻量级的,实例的创建和销毁不需要消耗太多的资源,同时它还是 Hibernate 的一级缓存,这个缓存主要用于存放当前工作单元加载的对象

获取session

//采用openSession方法创建Session
Session session = sessionFactory.openSession();
//采用getCurrentSession()方法创建Session
Session session = sessionFactory.getCurrentSession();

openSession() 方法获取 Session 实例时,SessionFactory 直接创建一个新的 Session 实例,并且在使用完成后需要调用 close() 方法进行手动关闭;

getCurrentSession() 方法创建的 Session 实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。

提供的持久化操作方法

名称描述
save()用于执行添加对象操作
update()用于执行修改对象操作
saveOrUpdate()用于执行添加或修改对象操作
delete()用于执行删除对象操作
get()根据主键查询数据
load()根据主键查询数据
createQuery()用于数据库操作对象
createSQLQuery()用于数据库操作对象
createCriteria()面向对象的条件查询

Session 是线程不安全的,当多个并发线程同时操作一个 Session 实例时,就可能导致 Session 数据存取的混乱(当方法内部定义和使用 Session 时,不会出现线程问题)。因此设计软件架构时,应避免多个线程共享一个 Session 实例。

Transaction

Transaction 接口主要是用于管理事务,它是 Hibernate 的数据库事务接口,且对底层的事务接口进行了封装。

Transaction transaction = session.beginTransaction();

在 Transaction 接口中,提供了事务管理的常用方法,具体如下。

  • commit() 方法:提交相关联的 session 实例。
  • rollback() 方法:撤销事务操作。
  • wasCommitted() 方法:检查事务是否提交。

使用实例

try{
    transaction = session.beginTransaction();   //开启事务
    session.save(user); //执行操作
    transaction.commit();   //提交事务
}catch(Exception e) {
    transaction.rollback(); //回滚事务
}finally{
    session.close();    //关闭资源
}

Query

Query 接口是 Hibernate 的查询接口,主要用于执行 Hibernate 的查询操作。

Query 中包装了一个 HQL(Hibernate Query Language)查询语句,该语句采用了面向对象的查询方式,具有丰富灵活的查询特征。因此,Hibernate 官方推荐使用 HQL 语言进行查询。

@Test
public void testFindAll() {
    Configuration config = new Configuration().configure();
    SessionFactory sessionFactory = config.buildSessionFactory();
    // 1.得到一个Session
    Session session = sessionFactory.openSession();
    Transaction transaction = session.beginTransaction();
    // 2.编写 HQL,其中的User代表的是类
    String hql = "from User";
    // 3.创建Query查询对象
    Query query = session.createQuery(hql);
    // 4.使用query.list()方法查询数据,并放入list集合
    List<User> list = query.list();
    for (User u : list) {
        System.out.println(u);
    }
    transaction.commit();
    session.close();
    sessionFactory.close();
}

常用方法

名 称描 述
setterQuery 接口中提供了一系列的 setter 方法用于设置查询语句中的参 数,针对不同的数据类型,需要用到不同的 setter 方法
Iterator iterator()该方法用于查询语句,返回的结果是一个 Iterator 对象,在读取时只能按照顺序方式读取,它仅把使用到的数据转换成 Java 实体对象
Object uniqueResult()该方法用于返回唯一的结果,在确保只有一条记录的查询时可以 使用该方法
int executeUpdate()该方法是 Hibernate 3 的新特性,它支持 HQL 语句的更新和删除操作
Query setFirstResult(int firstResult)该方法可以设置获取第一个记录的位置,也就是它表示从第几条 记录开始查询,默认从 0 开始计算
Query setMaxResult(int maxResults)该方法用于设置结果集的最大记录数,通常与 setFirstResult() 方法结合使用,用于限制结果集的范围,以实现分页功能

Criteria

Criteria 接口是 Hibernate 提供的一个面向对象的查询条件接口,通过它完全不需要考虑数据库底层如何实现,以及 SQL 语句如何编写。Criteria 查询又称为 QBC 查询(Query By Criteria)

名 称描 述
Criteria add(Criterion criterion)用于设置查询的条件,这个方法的参数为 Criterion 对象的实例
Criteria addOrder(Order order)用于设置结果集的排序规则,其参数为一个 Order 对象实例
Criteria createCriteria(String associationPath)用于创建一个新的 Criteria,这个方法用于执行符合查询时设置查询条件
List list()用于执行数据库查询,返回查询的结果
Criteria setFirstResult(int firstResult)设置获取第一个记录的位置,这个位置从 0 开始算
Object uniqueResult()这个方法用于得到唯一的结果对象实例。在确保最多只有一个 满足条件的查询结果时,可以选择适用这个方法

使用

@Test
    public void testQBC() {
        Configuration config = new Configuration().configure();
        SessionFactory sessionFactory = config.buildSessionFactory();
        // 1.得到一个Session
        Session session = sessionFactory.openSession();
        Transaction transaction = session.beginTransaction();
        // 2.通过session获得Criteria对象
        Criteria criteria = session.createCriteria(User.class);
        // 3.使用Restrictions的eq方法设定查询条件为name="zhangsan"
        // 4.向Criteria对象中添加查询条件
        criteria.add(Restrictions.eq("name", "zhangsan"));
        // 5.执行Criterita的list()方法获得结果
        List<User> list = criteria.list();
        for (User u : list) {
            System.out.println(u);
        }
        transaction.commit();
        session.close();
        sessionFactory.close();
    }

Hibernate持久化对象的状态及状态转换

在 Hibernate中,持久化对象是存储在一级缓存当中的,一级缓存指 Session 级别的缓存,它可以根据缓存中的持久化对象的状态改变同步更新数据库。

Hibernate 是持久层的 ORM 框架,专注于数据的持久化工作。在进行数据持久化操作时,持久化对象可能处于不同的状态当中。这些状态可分为三种:

1)瞬时态(transient)

瞬时态也称为临时态或者自由态,瞬时态的对象是由 new 关键字开辟内存空间的对象,不存在持久化标识 OID(相当于主键值),且未与任何的 Session 实例相关联,在数据库中也没有记录,失去引用后将被 JVM 回收。瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系。

2)持久态(persistent)

持久态的对象存在一个持久化标识 OID,当对象加入到 Session 缓存中时,就与 Session 实例相关联。它在数据库中存在与之对应的记录,每条记录只对应唯一的持久化对象。需要注意的是,持久态对象是在事务还未提交前变成持久态的。

3)脱管态(detached)

脱管态也称离线态或者游离态,当持久化对象与 Session 断开时就变成了脱管态,但是脱管态依然存在持久化标识 OID,只是失去了与当前 Session 的关联。需要注意的是,脱管态对象发生改变时 Hibernate 是不能检测到的。(当new一个对象并且设id值,而id在数据库中存在的,此时对对象处于脱管态)

注意:

oid是持久化类中的一个属性,与数据库中的id属性对应(user类中的id)

hibernate底层是通过持久化类的反射方法操作的,所以持久化类中的每一个属性都应该有set和get方法,oid也不例外

持久化类对象是否有oid,是该对象是瞬时态还是持久态的标志。

总结:

**瞬时态:**没有持久化标识OID,没有被纳入Session对象的管理

**持久态:**有持久化标识OID,已经被纳入Session对象的管理

**托管态:**有持久化标识OID,但没有被Session对象管理

①瞬时态(临时态、自由态):不存在持久化标识OID,尚未与Hibernate Session关联对象,被认为处于瞬时态,失去引用将被JVM回收
②持久态:存在持久化标识OID,与当前session有关联,并且相关联的session没有关闭 ,并且事务未提交
③脱管态(离线态、游离态):存在持久化标识OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

持久化对象的转换

在这里插入图片描述

当一个对象通过 new 关键字创建后,该对象处于瞬时态;当对瞬时态对象执行 Session 的 save() 或 saveOrUpdate() 方法后,该对象将被放入 Session 的一级缓存中,此时该对象处于持久态。

当对持久态对象执行 evict()、close() 或 clear() 操作后,对象会进入脱管态。

当直接执行 Session 的 get()、load()、find() 或 iterate() 等方法从数据库中查询出对象时,查询到的对象也会处于持久态。

当对数据库中的纪录进行 update()、saveOrUpdate() 以及 lock() 等操作后,此时脱管态的对象就过渡到持久态;由于瞬时态和脱管态的对象不在 session 的管理范围内,所以会在一段时间后被 JVM 回收。

实例:

pom.xml文件

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

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>hibernatedemo02</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>hibernatedemo02 Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
    </dependency>
    
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>

    <!-- 添加Hibernate依赖 -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.2.1.ga</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>3.2.1.ga</version>
    </dependency>

    <!--    SLF4J-->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.32</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.2</version>
    </dependency>

    <!-- 添加javassist -->
    <dependency>
      <groupId>javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.12.0.GA</version>
    </dependency>

    <!-- mysql数据库的驱动包 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.6</version>
    </dependency>
  </dependencies>

  <build>

    <!--配置Maven 对resource文件 过滤 -->
    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
          <include>**/*.xls</include>
          <include>**/*.xlsx</include>
        </includes>
        <filtering>true</filtering>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>true</filtering>
      </resource>
    </resources>

  </build>
</project>

核心配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- 指定方言 -->
        <property name="dialect">
            org.hibernate.dialect.MySQL5Dialect
        </property>
        <!-- 链接数据库url -->
        <property name="connection.url">
            jdbc:mysql://localhost:3306/hibernatelearn?useUnicode=true&amp;characterEncoding=utf-8
        </property>
        <!-- 连接数据库的用户名 -->
        <property name="connection.username">
            root
        </property>
        <!-- 数据库的密码 -->
        <property name="connection.password">
            root
        </property>
        <!-- 数据库驱动 -->
        <property name="connection.driver_class">
            com.mysql.jdbc.Driver
        </property>
        <!-- 显示sql语句 -->
        <property name="show_sql">
            true
        </property>
        <!-- 格式化sql语句 -->
        <property name="format_sql">true</property>
        <!-- 自动建表 -->
        <property name="hbm2ddl.auto">update</property>
        <!-- 映射文件配置 -->
        <mapping resource="Goods.hbm.xml" />
    </session-factory>
</hibernate-configuration>

工具类:

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

/**
 * TODO 类描述
 *
 * @author qijian.
 * @date 2021/8/23 19:35
 */
public class HibernateUtils {
    /***
     * 声明一个私有的静态final类型的Configuration对象
     */
    private static final Configuration config;
    /**
     * 声明一个私有的静态的final类型的SessionFactory对象
     */
    private static final SessionFactory factory;

    /**
     * 通过静态代码块构建SessionFactory
     */
    static {
        config = new Configuration().configure();
        factory = config.buildSessionFactory();
    }

    /**
     * 提供一个公有的静态方法供外部获取,并返回一个session对象
     * @return 返回session
     */
    public static Session getSession() {
        return factory.openSession();
    }
}

实体类

public class Goods {

    private Integer id; // 标识id

    private String name; // 商品名称

    private Double price; // 商品价格

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    // 重写toString()方法
    @Override
    public String toString() {
        return "Goods[id=" + id + ",name=" + name + ",price=" + price + "]";
    }
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mengma.domain.Goods" >
        <id name="id"   type="integer">
            <generator class="native" />
        </id>
        <property name="name"   />
        <property name="price"  />
    </class>
</hibernate-mapping>

测试:

// 演示持久化对象的三种状态
@Test
public void test1() {
    Session session = HibernateUtils.getSession(); // 得到session对象
    Transaction tx = session.beginTransaction();
    Goods goods = new Goods();
    goods.setName("铅笔");
    goods.setPrice(0.5);
    //此时goods处于Transient态
    session.save(goods);
    //此时对象已由Hibernate纳入管理容器,处于持久态
    tx.commit();
    //事务提交后,库表中已经插入一条Goods的记录
    Transaction tx1 = session.beginTransaction();
    goods.setPrice(1.0);
    tx1.commit;
    //虽然这个事务中我们没有显示的调用Session.save方法保存goods对象
    //但是由于处于Persistent状态的对象将自动被固华到数据库中,因此
    //对象的变化也会被同步到数据库中
    //也就是说数据库中goods的记录数据已被更新
    session.close();
    System.out.println(goods);
}

使用 new 关键字创建 Goods 对象时,该对象是没有标识(OID)的,也没有与 Session 进行关联,它处于瞬时状态。

执行 save() 方法后,Goods 对象已经处于 Session 的管理范围了,并且有了自己的 OID,此时的 Goods 对象已转换为持久态。当执行 commit() 方法并关闭 Session 后,Goods 对象就已不在 Session 的管理范围,此时 Goods 对象从持久态转换为托管态。

接下来我们进行debug调试

在这里插入图片描述

可以看出此时id的值是null。接着进入下一步操作。

在这里插入图片描述

此时id已被赋值。

也就是说:执行完 save() 方法后,Good 对象的 id 属性已被赋值,该值就是 Goods 对象的持久化标识 OID,这说明持久化对象在事务提交前就已经变成了持久态,也说明了瞬时态对象和持久态对象的区别就是,持久态对象与 Session 进行了关联并且 OID 有值。

当执行完 close() 方法后,Goods 对象与 Session 不再存在关联关系,此时的 Goods 对象会由持久态对象转换为脱管态,但通过控制台的输出内容可以发现 Goods 对象的 OID 属性依然是存在的,这说明脱管态对象与持久态对象的区别就是脱管状态对象没有了 Session 关联。

hibernate一级缓存

CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小得多但是交换速度却比内存要快得多。

缓存的工作原理是当CPU要读取一个数据时,首先从缓存中查找,如果找到就立即读取并送给CPU处理;如果没有找到,就用相对慢的速度从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。 正是这样的读取机制使CPU读取缓存的命中率非常高(大多数CPU可达90%左右),也就是说CPU下一次要读取的数据90%都在缓存中,只有大约10%需要从内存读取。这大大节省了CPU直接读取内存的时间,也使CPU读取数据时基本无需等待。总的来说,CPU读取数据的顺序是先缓存后内存。—百度百科

hibernate一级缓存其实就是 Session 缓存。Session 缓存是一块内存空间,用于存储与管理 Java对象。

在使用 Hibernate 查询对象时,首先会使用对象的 OID 值在 Hibernate 的一级缓存中查找,如果找到匹配的对象,则直接将该对象从一级缓存中取出使用;如果没有找到匹配的对象,则会去数据库中查询对应的数据。当从数据库中查询到所需数据时,该数据信息会存储到一级缓存中。由此可知,Hibernate 一级缓存的作用就是减少对数据库的访问次数。

Hibernate 的一级缓存具有如下特点。

1)当应用程序调用 Session 接口的 save()、update()、saveOrUpdate() 时,如果 Session 缓存中没有相应的对象,则 Hibernate 就会自动把从数据库中查询到的相应对象信息加入到一级缓存中。

2)当调用 Session 接口的 load()、get() 方法,以及 Query 接口的 list()、iterator() 方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询的对象,则再去数据库中查询对应对象,并添加到一级缓存中。

3)当调用 Session 的 close() 方法时,Session 缓存会被清空。

4)Session 能够在某些情况下,按照缓存中对象的变化,执行相关的 SQL 语句同步更新数据库,这一过程被称为刷出缓存(flush)。

在默认情况下,Session 在如下几种情况中会刷出缓存。

1)当应用程序调用 Transaction 的 commit() 方法时,该方法先刷出缓存(调用 session.flush() 方法),然后再向数据库提交事务(调用 commit() 方法)。

2)当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态。

3)调用 Session 的 flush() 方法。

以上就是 Hibernate 一级缓存的刷出情况。对于刚接触 Hibernate 框架的读者来说并不是很容易理解,为了帮助读者更好地理解 Session 的一级缓存,下面通过具体案例演示一级缓存的使用。

利用上面的程序,添加如下的测试方法,证明缓存的存在:

// 证明一级缓存的存在
@Test
public void test2() {
    Session session = HibernateUtils.getSession(); // 得到session对象
    session.beginTransaction();
    // 获取goods1对象时,由于一级缓存中没有数据,所以会发送SQL语句,查询数据库中的内容
    Goods goods1 = (Goods) session.get(Goods.class, 1);
    System.out.println(goods1);
    // 获取goods2对象时,不会发出SQL语句,会从Session缓存中获取数据
    Goods goods2 = (Goods) session.get(Goods.class, 1);
    System.out.println(goods2);
    session.getTransaction().commit();
    session.close();
}

第一次执行 get() 方法时,由于一级缓存中不存在 id 为 1 的 Goods 对象,所以 Hibernate 会发出 SQL 语句查询数据库中 id 为 1 的数据。查到后,会将该数据信息保存到一级缓存中。当再次调用 get() 方法获取该对象时,此时将不会发出 SQL 语句,这是因为该对象是在一级缓存中获取的。

打断点测试:

在这里插入图片描述

执行完第一次get方法后,hibernate向mysql数据库发送了一天sql语句,说明此时的数据是从数据库中得来的。

在这里插入图片描述

执行完第二个给语句后,没有输出。注意Goods[id=1,name=铅笔,price=0.5]是来自于之前的输出语句。也就是说,第二次调用get方法没有从数据库中获取数据而是从以及缓存中获取的。

hibernate的快照技术

为了确保一级缓存中的数据和数据库中的数据保持一致,在 Hibernate 框架中提供了快照技术。

Hibernate 向一级缓存中存入数据的同时,还会复制一份数据存入 Hibernate 快照中。当调用 commit() 方法时,会清理一级缓存中的数据操作,同时会检测一级缓存中的数据和快照区的数据是否相同。如果不同,则会执行 update() 方法,将一级缓存的数据同步到数据库中,并更新快照区;反之,则不会执行 update() 方法

// hibernate快照
@Test
public void test3() {
    Session session = HibernateUtils.getSession(); // 得到session对象
    session.beginTransaction();
    Goods goods = new Goods();
    goods.setName("钢笔");
    goods.setPrice(5.0);
    session.save(goods);    // 向一级缓存中存入session对象
    goods.setPrice(4.5);    // 提交价格
    session.getTransaction().commit();    //提交事务
    session.close();    //关闭资源
}

执行后截图:

在这里插入图片描述

创建了一个 Goods 对象,并为该对象的属性进行赋值。在执行 save() 方法时,会将数据保存到一级缓存中。接着修改对象的 price 属性值,提交事务并关闭 session 对象。

从上面的运行结果中我们看到hibernate发送了两次sql语句。第一条sql语句当然是save语句执行发送的,那我们debug一下看一下这第二条语句发送的时机。

在这里插入图片描述

发现第二条sql语句是执行commit语句发送的。

为什么会发出 update 语句呢?

这是因为在执行 session 的 save() 方法时,Hibernate 一级缓存中保存了 Goods 对象的数据且会复制一份数据放入到 Hibernate 快照中,在执行 commit() 方法时,Hibernate 会检测快照中的数据与一级缓存中的数据是否一致,由于在此之前 Goods 对象的 price 属性值发生了变化,导致快照中的数据与一级缓存中的数据已经不一致,因此发送了 update 语句,更新了 Hibernate 一级缓存中的数据。

我们从数据库中查看一下(执行该方法前清空了表中的数据):

在这里插入图片描述

从上面查询的结果可以看出,插入的数据是更新完之后打的数据。也就证实了上面所说的。

一级缓存的常用操作

分别为刷出、清除和刷新操作

刷出(flush)

一级缓存刷出功能是指调用 Session 的 flush() 方法时会执行刷出缓存的操作。

session flush在commit之前默认都会执行他。也可以手动执行它,他主要做了两件事:
1) 清理缓存。
2) 执行SQL。

session在什么情况下执行flush

  • 默认在事务提交时
  • 显示的调用flush
  • 在执行查询前,如:iterate
    hibernate按照save(insert),update、delete顺序提交相关操作

测试:

// 刷出
@Test
public void test4() {
    Session session = HibernateUtils.getSession(); // 得到session对象
    session.beginTransaction();
    Goods goods = (Goods) session.get(Goods.class, 12);
    goods.setPrice(5.5);
    session.flush(); // 执行刷出操作,此时会发送update语句
    session.getTransaction().commit();
    session.close();
}

断点测试:

在这里插入图片描述

在这里插入图片描述

发现当程序在断点处停下来后,输出了select 语句,并且此时的数据依然是之前的。接下来我们执行下一句。

在这里插入图片描述

在这里插入图片描述

发现执行完session.flush();语句后输出了update语句, commit() 方法时,也同样发出了 update 语句,这说明在提交事务前,Hibernate 程序会默认先执行 flush() 方法。

提交完事务后数据库中的数据就改变了。

在这里插入图片描述

补充:

默认情况下 Session 在以下时间点刷新缓存:
1、显式调用 Session 的 flush() 方法
2、当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后在向数据库提交事务
3、当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态

Session 的 save() 方法使一个临时对象转变为持久化对象
Session 的 save() 方法完成以下操作:
1、把 News 对象加入到 Session 缓存中, 使它进入持久化状态
2、选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID. 在 使用代理主键的情况下, setId() 方法为 News 对象设置 OID 使无效的.
3、计划执行一条 insert 语句:在 flush 缓存的时候

清除(clear)

程序在调用 Session 的 clear() 方法时,可以执行清除缓存数据的操作。下面通过具体示例演示一级缓存中的清除功能。

测试代码:

    // 清除
    @Test
    public void test5() {
        Session session = HibernateUtils.getSession(); // 得到session对象
        session.beginTransaction();
        Goods goods = (Goods) session.get(Goods.class, 12);
        System.out.println(goods);
        goods.setPrice(6.5);
        session.clear(); // 清空一级缓存
        session.getTransaction().commit();
        session.close();
    }

执行完上面的操作后截图如下:

在这里插入图片描述

在这里插入图片描述

发现只输出了一条select语句。并且数据库中的值没有被修改。下面我们把clear语句注释了。再执行一下。

在这里插入图片描述

在这里插入图片描述

会发现输出了一天select语句和一条update语句,并且数据库中的数据也修改了。

这是因为在执行 clear() 方法时,清空了一级缓存中的数据,所以 Goods 对象的修改操作并没有生效。

需要注意的是,如果将上述方法中的 session.clear() 方法更改为 session.evict(goods)方法,也可以实现同样的效果。这两个方法的区别是:clear() 方法是清空一级缓存中所有的数据,而 evict() 方法是清除一级缓存中的某一个对象

刷新(refresh)

程序在调用 Session 的 refresh() 方法时,会重新查询数据库,并更新 Hibernate 快照区和一级缓存中的数据。

// 刷新
@Test
public void test6() {
    Session session = HibernateUtils.getSession(); // 得到session对象
    session.beginTransaction();
    Goods goods = (Goods) session.get(Goods.class, 12);
    goods.setPrice(7.5);
    session.refresh(goods); // 查询数据库,恢复快照和一级缓存中的数据
    session.getTransaction().commit();
    session.close();
}

运行截图:

在这里插入图片描述

执行完get方法后hibernate把数据库中查询到的值赋给了对象

在这里插入图片描述

在这里插入图片描述

setPrice方法执行,缓存中goods对象的price属性值被设置为7.5,且此时控制台中只输出了select语句。

在这里插入图片描述

在这里插入图片描述

执行refresh方法后,price又别为了6.5,并且控制台中多了一条select语句。因为refresh方法的作用就是使快照和以及缓存中的数据与数据库中的数据保持一直。

说明:

其实当我们进行第一句查询时,hibernate把返回的Customer对象一式两份,一份保存到一级缓存,一份保存到快照当中,然后返回给我们的是缓存中的对象。

在这里插入图片描述
本次学习主要来自:http://c.biancheng.net/hibernate/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
3.6.10.Final 2012-02-09 3.6.9.Final 2011-12-15 3.6.8.Final 2011-10-27 3.6.7.Final 2011-08-17 3.6.6.Final 2011-07-21 3.6.5.Final 2011-06-09 3.6.4.Final 2011-05-05 3.6.3.Final 2011-04-06 3.6.2.Final 2011-03-10 3.6.1.Final 2011-02-03 3.6.0.Final 2010-10-14 3.6.0.CR2 2010-09-29 3.6.0.CR1 2010-09-16 3.5.6-Final 2010-09-15 3.6.0.Beta4 2010-09-02 3.5.5-Final 2010-08-18 3.6.0.Beta3 2010-08-18 3.6.0.Beta2 2010-08-04 3.5.4-Final 2010-07-22 3.6.0.Beta1 2010-07-21 3.5.3-Final 2010-06-17 3.5.2-Final 2010-05-14 3.5.1-Final 2010-04-15 3.5.0-Final 2010-03-31 3.5.0-CR-2 2010-02-25 3.5.0-CR-1 2010-02-10 3.5.0-Beta-4 2010-01-29 3.5.0-Beta-3 2010-01-14 3.5.0-Beta-2 2009-11-03 3.5.0.Beta-1 2009-08-21 3.3.2.GA 2009-06-24 3.2.7.ga 2009-06-03 3.3.1.GA 2008-09-11 3.3.0.SP1 2008-08-20 3.3.0.GA 2008-08-15 3.3.0.cr2 2008-08-01 3.3.0.cr1 2008-04-29 3.2.6.ga 2008-02-07 3.2.5.ga 2007-07-31 3.2.4.sp1 2007-05-18 3.2.4.ga 2007-05-09 3.2.3.ga 2007-04-02 3.2.2.ga 2007-01-24 3.2.1.ga 2006-11-17 3.2.0.ga 2006-10-16 3.2.0.cr5 2006-10-05 3.2.0.cr4 2006-08-25 3.2.0.cr3 2006-07-06 3.2 cr2 2006-05-06 3.2 cr1 2006-03-27 3.1.3 2006-03-20 3.2 alpha2 2006-03-16 3.2 alpha1 2006-03-01 3.1.2 2006-01-28 3.1.1 2006-01-18 3.1 2005-12-12 3.1 rc1 2005-12-12 3.1 rc3 2005-11-18 3.1 rc2 2005-10-17 3.1 beta 3 2005-09-13 3.1 beta2 2005-08-16 3.1 beta1 2005-07-21 3.1 alpha1 2005-06-24 3.0.5 2005-05-25 3.0.4 2005-05-23 3.0.3 2005-05-08 3.0.2 2005-04-27 3.0.1 2005-04-18 3.0 final 2005-03-31 3.0 rc 1 2005-02-28 3.0 beta 4 2005-02-11 3.0 beta 3 2005-01-30 3.0 beta 2 2005-01-24 3.0 beta 1 2004-12-20 3.0 alpha 2004-08-23
你好!关于学习Hibernate,我可以为你提供一些指导。Hibernate是一个Java持久化框架,用于将Java对象映射到关系数据库中。它简化了与数据库的交互,提供了ORM(对象关系映射)的功能。 如果你想学习Hibernate,以下是一些建议的步骤: 1. 了解基本概念:开始之前,建议你先了解一些Hibernate的基本概念,例如持久化、实体类、会话(Session)等。这将帮助你更好地理解Hibernate的工作原理。 2. 学习Hibernate配置:Hibernate使用一个配置文件来连接数据库和定义映射关系。你可以学习如何配置Hibernate以适应你的项目需求。这包括数据库连接配置、实体类映射等。 3. 学习Hibernate实体映射:Hibernate通过注解或XML文件将Java实体类映射到关系数据库表中。你可以学习如何使用注解或XML文件来定义实体类的映射。 4. 学习Hibernate查询语言(HQL):HQL是一种类似于SQL的查询语言,用于对数据库进行查询操作。学习如何使用HQL进行查询和操作数据库是很重要的。 5. 学习Hibernate事务管理:事务管理是在处理数据库操作时非常重要的一部分。学习如何使用Hibernate进行事务管理,包括开启事务、提交事务、回滚事务等。 6. 实践项目:最好的学习方法是通过实际项目来应用所学的知识。尝试在一个小型项目中使用Hibernate来进行数据库操作,这将帮助你更好地理解和掌握Hibernate的使用。 除了上述步骤,还有很多其他方面的内容可以学习,例如缓存管理、性能优化等。希望这些步骤能够帮助你入门Hibernate!如果你有任何进一步的问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值