Java必知必会系列:ORM框架与Hibernate

作者:禅与计算机程序设计艺术

1.背景介绍

Hibernate是一个功能强大的Java持久化框架。它提供了一个对象/关系映射工具,用于开发人员通过简单的配置将面向对象的模型映射到关系型数据库中。Hibernate可以轻松地实现对关系型数据库的访问、管理、优化和同步。由于其简单易用、快速性能以及灵活性,Hibernate已经成为Java编程领域最流行的ORM框架。本系列文章将讨论Hibernate的基本概念和技术原理,以及如何应用Hibernate解决实际项目中的实际问题。

2.核心概念与联系

2.1 Hibernate概述

Hibernate是一种开源的J2EE框架,用于ORM(Object-Relational Mapping,对象-关系映射)框架。在Hibernate中,我们可以利用对象关系映射技术将应用程序中的对象存储在关系型数据库中。通过Hibernate,我们可以在程序运行期间修改数据库中的数据,而不需要重新编译或者重启服务器。因此,Hibernate提供了一种类似于面向对象的编程方法,让我们更容易地访问数据库。

Hibernate具有以下几个主要特征:

  1. 对象/关系映射:Hibernate使得数据库表可以与程序中的对象相对应,每个对象都可以很方便地被检索、更新、删除或添加到数据库中。

  2. SQL生成:Hibernate可以自动根据应用程序对象所做的修改生成相应的SQL语句,从而简化了数据的读写操作。

  3. 框架内置事务管理:Hibernate提供事务管理机制,可以简化业务逻辑层与持久层的数据处理过程。

  4. 缓存:Hibernate支持应用级别的缓存,可以提高查询效率并减少数据库访问次数。

  5. 查询DSL:Hibernate支持HQL(Hibernate Query Language)查询语言,可以用一种类似SQL的方式编写复杂的查询。

2.2 Hibernate体系结构

Hibernate体系结构分成三层:

  1. Core Layer:Core Layer包括一个领域对象(Domain Object)层和一个持久化服务(Persistence Service)层。领域对象层定义实体类,该层对象与关系型数据库中的表字段进行一一对应;持久化服务层封装了底层的JDBC API,对领域对象进行CRUD操作时,首先将其转换为SQL语句,然后执行该SQL语句,并返回执行结果。
  2. Mapping Layer:Mapping Layer负责定义映射文件。映射文件定义了领域对象与数据库表之间的关系、约束条件等信息,帮助Hibernate实现ORM特性。
  3. Framework Integration Layer:Framework Integration Layer实现了Hibernate框架与其他框架的整合接口。例如,Spring和Struts2可以通过Hibernate提供的接口与Hibernate集成。

2.3 Hibernate配置

Hibernate的配置文件hibernate.cfg.xml分成四个部分:

  1. SessionFactory设置:SessionFactory代表Hibernate的核心类,包含连接池、线程安全策略及一些Hibernate设置。SessionFactory由Hibernate Configuration和Mapping文件组成。
  2. JDBC ConnectionProvider设置:ConnectionProvider实现了JDBC连接的获取和释放策略。
  3. TransactionManager设置:TransactionManager实现了事务管理策略。
  4. Classpath 设置:Classpath 设置包含了Hibernate需要的jar包路径。

2.4 实体类与关联关系

实体类:Hibernate实体类是JavaBean类型的类,即包含了一些属性(field),这些属性值可以通过getters、setters进行访问,可以直接使用,也可以把它序列化为JSON或者XML格式。实体类的例子如下:

public class Customer {
    private int id;
    private String name;

    // getters and setters omitted for brevity

    public void addOrder(Order order){
        orders.add(order);
    }

    public Collection<Order> getOrders(){
        return orders;
    }
}

@Entity
public class Order {
    @Id
    private int orderId;
    private Date date;

    // many-to-one relationship with customer 
    @OneToOne
    @JoinColumn(name="customer_id")
    private Customer customer;

    // getters and setters omitted for brevity

}

实体类的注解:

  1. @Entity:该注解标注在类上,表示该类是一个实体类。
  2. @Id:该注解标注在主键属性上,表示该属性的值唯一标识了一个实体对象。
  3. @Column:该注解用来定义列名和其他相关属性。
  4. @OneToMany:该注解定义了一对多的关联关系。
  5. @ManyToOne:该注解定义了多对一的关联关系。
  6. @ManyToMany:该注解定义了多对多的关联关系。
  7. @JoinTable:该注解定义了中间表。
  8. @JoinColumn:该注解定义了主表外键。

2.5 HQL查询语言

HQL(Hibernate Query Language)是Hibernate提供的面向对象的查询语言。HQL能方便地进行复杂的查询,避免了SQL语句的拼接。

HQL语法示例:

from Employee as e where e.salary > :minSalary 
and e.department = 'IT' 
group by e.department 
order by e.salary desc

HQL查询过程:

  1. 使用关键字“from”指定要查询的实体类名称。
  2. “as”关键字可为类取别名。
  3. 使用“where”子句来指定搜索条件。
  4. “:”符号后面的参数名用于绑定参数值。
  5. “group by”子句用于分组。
  6. “order by”子句用于排序。

3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

3.1 ORM的基本原理

ORM(Object-Relational Mapping,对象-关系映射)是一种程序设计技术,用于将关系数据库表转换为面向对象模型,这样就可以以面向对象的方式操纵数据库数据。

对象关系映射的两种主要形式:

  • 一对一映射(One-To-One Mapping):这种映射是指两个实体对象之间存在一种一对一的关系。例如,一个实体对象对应数据库中的一条记录,另一个实体对象也对应数据库中的一条记录。
  • 一对多映射(One-To-Many Mapping):这种映射是指一个实体对象对应多个关系数据库表中的记录。例如,一个实体对象对应一个订单表中的记录,而这个订单表中可能包含多个商品信息表中的记录。

3.2 Hibernate的数据持久化流程

Hibernate的工作流程如下图所示:

  1. 创建Session对象:Hibernate使用SessionFactory对象来创建Session对象,Session对象是Hibernate的入口点。
  2. 执行增删改查操作:用户通过调用Session的方法执行增删改查操作,Hibernate通过底层JDBC驱动执行相应的数据库操作。
  3. 操作持久化上下文:Hibernate通过持久化上下文(persistence context)维护一个对当前对象的状态的跟踪。
  4. 将数据写入数据库:Hibernate根据持久化上下文的内容将数据写入数据库。
  5. 提交事务:如果事务成功提交,Hibernate才会真正将数据写入数据库。

3.3 Hibernate的缓存机制

Hibernate缓存机制可以提高数据库访问的性能。Hibernate缓存分为两级:一级缓存和二级缓存。

一级缓存:Hibernate为每一个用户请求创建一个缓存区域,所有相同的对象都缓存在这块内存中,以便下次访问时能够直接从缓存中获取,而不是再次从数据库中加载。

二级缓存:Hibernate还为每个实体 bean 都维护一个二级缓存。当缓存中的对象被修改后,第二次访问该对象时不会再次从数据库中查询,而是直接从缓存中获取,从而提升了查询速度。

3.4 声明式事务管理

Hibernate通过声明式事务管理机制简化了业务逻辑层与持久层的数据处理过程。声明式事务管理是基于AOP(Aspect-Oriented Programming,面向切面编程)来实现的。

声明式事务管理就是不再需要显式地调用beginTransaction()和commit()等事务API函数,而是在事务代码块内部直接用注解来声明事务的边界,Hibernate框架会自动识别事务边界,并自动完成事务管理。

4.具体代码实例和详细解释说明

4.1 配置Hibernate环境

4.1.1 添加Maven依赖

在项目的pom.xml文件中加入如下依赖:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>${hibernate.version}</version>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <scope>runtime</scope>
</dependency>
<!-- Optional -->
<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>${h2.version}</version>
   <scope>test</scope>
</dependency>

4.1.2 创建Hibernate配置文件

Hibernate配置文件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">
<hibernate-configuration>

   <!-- SessionFactory settings -->
   <session-factory>
      <!-- Database connection settings -->
      <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
      <property name="connection.url">jdbc:mysql://localhost:3306/mydatabase?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8</property>
      <property name="connection.username">root</property>
      <property name="connection.password"></property>

      <!-- JDBC connection pool (optional) -->
      <!--<property name="pool.size">1</property>-->
      <!--<property name="pool.max_wait">30000</property>-->
      <!--<property name="pool.preferredTestQuery">SELECT 1</property>-->

      <!-- SQL dialect -->
      <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

      <!-- Logging -->
      <property name="show_sql">true</property>
      <property name="format_sql">true</property>

      <!-- Mapping information -->
      <mapping resource="com/example/domain/Customer.hbm.xml"/>
      <mapping resource="com/example/domain/Order.hbm.xml"/>

   </session-factory>

</hibernate-configuration>
  • connection.*:配置数据库连接参数。
  • dialect:设置数据库方言。
  • show_sql:设置是否显示SQL语句。
  • format_sql:设置是否格式化输出的SQL语句。
  • <mapping>:配置实体类映射文件。

4.1.3 配置实体类映射文件

实体类映射文件示例:

com/example/domain/Customer.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.example.domain">
   <class name="com.example.domain.Customer" table="customers">
      <id name="id" type="int">
         <generator class="native"/>
      </id>
      <property name="name" type="string"/>
   </class>
</hibernate-mapping>

com/example/domain/Order.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.example.domain">
   <class name="com.example.domain.Order" table="orders">
      <id name="orderId" type="int">
         <generator class="native"/>
      </id>
      <property name="date" type="timestamp"/>
      <many-to-one name="customer" column="customer_id" class="com.example.domain.Customer"/>
   </class>
</hibernate-mapping>
  • <class>:定义实体类。
  • <id>:定义主键。
  • <property>:定义普通属性。
  • <many-to-one>:定义一对多关联。

4.2 增删改查

4.2.1 插入数据

import org.hibernate.*;
import com.example.domain.Customer;
...
try{
   Session session = factory.openSession();
   Transaction tx = session.beginTransaction();
   try{
      // create a new customer object
      Customer customer = new Customer();
      customer.setName("John Smith");
      // persist the customer object
      session.save(customer);
      tx.commit();
   }catch (HibernateException e){
      if (tx!= null){
         tx.rollback();
      }
      throw e;
   }finally{
      session.close();
   }
}catch (HibernateException e){
   e.printStackTrace();
}
  • 通过SessionFactory获取Session。
  • 在一个事务中执行插入操作,并提交事务。
  • 如果出现异常,则回滚事务。

4.2.2 删除数据

import org.hibernate.*;
import com.example.domain.Customer;
...
try{
   Session session = factory.openSession();
   Transaction tx = session.beginTransaction();
   try{
      // delete an existing customer object
      Integer custId =...;
      Customer customer = (Customer) session.load(Customer.class, custId);
      session.delete(customer);
      tx.commit();
   }catch (HibernateException e){
      if (tx!= null){
         tx.rollback();
      }
      throw e;
   }finally{
      session.close();
   }
}catch (HibernateException e){
   e.printStackTrace();
}
  • 通过SessionFactory获取Session。
  • 在一个事务中执行删除操作,并提交事务。
  • 如果出现异常,则回滚事务。

4.2.3 更新数据

import org.hibernate.*;
import com.example.domain.Customer;
...
try{
   Session session = factory.openSession();
   Transaction tx = session.beginTransaction();
   try{
      // update an existing customer object
      Integer custId =...;
      Customer customer = (Customer) session.load(Customer.class, custId);
      customer.setName("Mary Johnson");
      session.update(customer);
      tx.commit();
   }catch (HibernateException e){
      if (tx!= null){
         tx.rollback();
      }
      throw e;
   }finally{
      session.close();
   }
}catch (HibernateException e){
   e.printStackTrace();
}
  • 通过SessionFactory获取Session。
  • 在一个事务中执行更新操作,并提交事务。
  • 如果出现异常,则回滚事务。

4.2.4 查询数据

import org.hibernate.*;
import com.example.domain.Customer;
import java.util.List;
...
try{
   Session session = factory.openSession();
   Transaction tx = session.beginTransaction();
   try{
      // retrieve all customers
      List results = session.createQuery("from Customer").list();
      for (Object result : results){
         System.out.println(((Customer)result).getName());
      }
      tx.commit();
   }catch (HibernateException e){
      if (tx!= null){
         tx.rollback();
      }
      throw e;
   }finally{
      session.close();
   }
}catch (HibernateException e){
   e.printStackTrace();
}
  • 通过SessionFactory获取Session。
  • 在一个事务中执行查询操作,并提交事务。
  • 获取查询结果列表。
  • 如果出现异常,则回滚事务。

4.3 分页查询

分页查询的SQL语法如下:

SELECT * FROM Customers LIMIT?,?;

?占位符用于表示参数,用于传递分页参数。

在Hibernate中,可以使用firstResult()maxResults()方法对分页参数进行设置,如下所示:

List results = session.createQuery("from Customer")
                    .setFirstResult((page - 1) * pageSize)
                    .setMaxResults(pageSize)
                    .list();

这里的(page - 1) * pageSize表示跳过的记录数,pageSize表示每页记录数。

5.未来发展趋势与挑战

ORM框架一直处于不断更新升级的过程中。随着Web应用的日益普及,需求变得越来越复杂,传统的基于SQL语句的开发模式正在慢慢被淘汰,新的分布式、高可用、弹性伸缩的架构模式逐渐浮出水面。同时,人们越来越重视软件的生命周期管理,微服务架构正在成为主流,同时也带动着各种ORM框架的更新升级。

今后的ORM框架将会有哪些主要方向?

  1. Spring Data:这是Spring IO Platform Team主推的ORM框架。Spring Data是基于Spring FrameWork的抽象层,在ORM框架之上提供统一的数据访问接口。它的优势在于提供更加面向对象的方式访问数据,降低了程序员的学习成本。另外,Spring Data还提供各种Repository,允许用户自定义数据访问操作。

  2. Mybatis: MyBatis是Apache基金会孵化的开源项目,提供了ORM框架的一种实现。MyBatis可以很好地与Java中的其它ORM框架配合使用,比如Hibernate。

  3. EclipseLink:EclipseLink是一个开放源代码的JPA实现,由Eclipse Foundation管理。EclipseLink在JPA规范之上增加了很多特性,包括集成缓存,异步查询,租户隔离等。并且,EclipseLink在性能上也有比较大的优势。

  4. TopLink:TopLink是一个非常古老的ORM框架,曾经被Sun公司收购。其中的OpenJPA,是一个开源版本的TopLink。但是,近年来OpenJPA被Oracle收购,所以现在大家更多地使用的是Hibernate。

6.附录常见问题与解答

猜你喜欢

转载自blog.csdn.net/universsky2015/article/details/133593863