Hibernate

前言

1、基础

  • Hibernate 是一个高性能的对象关系型持久化存储和查询的服务,其遵循开源的 GNU Lesser General Public License (LGPL) 而且可以免费下载。

  • 使用 JDBC 做数据库相关功能开发会做很多重复性的工作,比如创建连接,关闭连接,把字段逐一映射到属性中。

  • Hibernate 把这一切都封装起来了,使得数据库访问变得轻松而简单,代码也更加容易维护。

1.1 配置

  • 配置访问数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!-- 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>
    <session-factory>
    <!-- Database connection settings -->
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8</property>
    <property name="connection.username">root</property>
    <property name="connection.password">admin</property>

    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="current_session_context_class">thread</property>
    <property name="show_sql">true</property>
    <property name="hbm2ddl.auto">update</property>
    <mapping resource="com/how2java/pojo/Product.hbm.xml" />
    </session-factory>
    </hibernate-configuration>
  • 配置对象与表的映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <!-- Product.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.how2java.pojo">
    <class name="Product" table="product_">
    <id name="id" column="id">
    <generator class="native">
    </generator>
    </id>
    <property name="name" />
    <property name="price" />
    </class>

    </hibernate-mapping>
  • 基本原理

    • 应用程序通过 Hibernate 把一个对象插入到数据库的表中。
    • hibernate.cfg.xml 配置文件提供链接数据库的基本信息,账号、密码、驱动、数据库、ip、端口。
    • Product.hbm.xml 提供对象与表的映射关系,对应哪个表、什么属性、对应什么字段。
  • 使用基本步骤

    • 获取 SessionFactory。
    • 通过 SessionFactory 获取一个 Session。
    • 在 Session 基础上开启一个事务。
    • 通过调用 Session 的 save 方法把对象保存到数据库。
    • 提交事务。
    • 关闭 Session。
    • 关闭 SessionFactory。

1.2 对象状态

  • 实体类对象在 Hibernate 中有 3 种状态

    • 瞬时:指的是没有和 Hibernate 发生任何关系,在数据库中也没有对应的记录,一旦 JVM 结束,这个对象也就消失了。
    • 持久:指得是一个对象和 Hibernate 发生联系,有对应的 session,并且在数据库中有对应的一条记录。
    • 脱管:指的是一个对象虽然在数据库中有对应的一条记录,但是它所对应的 session 已经关闭了。

1.3 CRUD

  • 插入数据:插入数据到数据库。

    1
    session.save(p);
  • 删除数据:根据 id 把对象从表里删除掉,hibernate 在删除一条数据之前,先要通过 id 把这条记录取出来。

    1
    2
    Product p = (Product) session.get(Product.class, 5);
    session.delete(p);
  • 修改数据:修改一个对象的属性,并更新到数据库。

    1
    2
    3
    Product p = (Product) session.get(Product.class, 6);
    p.setName("iphone-modified");
    session.update(p);
  • 获取数据:通过 id 获取对象。

    1
    2
    3
    session.get(Product.class, 6);

    session.load(Product.class, 6);
  • 查询 数据

    • HQL 查询语句(Hibernate Query Language)是 hibernate 专门用于查询数据的语句,有别于 SQL,HQL 更接近于面向对象的思维方式。

      1
      2
      3
      4
      String name = "iphone";
      Query q = session.createQuery("from Product p where p.name like ?");
      q.setString(0, "%" + name + "%");
      List<Product> ps = q.list();
    • 使用 Criteria 进行数据查询,与 HQL 和 SQL 的区别是 Criteria 完全是面向对象的方式在进行数据查询,将不再看到有 sql 语句的痕迹。

      1
      2
      3
      4
      String name = "iphone";
      Criteria c = session.createCriteria(Product.class);
      c.add(Restrictions.like("name", "%" + name + "%"));
      List<Product> ps = c.list();
    • 通过标准 SQL 语句进行查询,Hibernate 依然保留了对标准 SQL 语句的支持,在一些场合,比如多表联合查询,并且有分组统计函数的情况下,标准 SQL 语句依然是效率较高的一种选择。

      1
      2
      3
      4
      String name = "iphone";
      String sql = "select * from product_ p where p.name like '%"+name+"%'";
      Query q = session.createSQLQuery(sql);
      List<Object[]> list = q.list();

2、关系

2.1 多对一

  • Product 和 Category 是多对一的关系。

    • 一个 Product 对应一个 Category。
    • 一个 Category 对应多个 Product。
  • 为 Product.java 增加 Category 属性。

    1
    2
    3
    4
    5
    6
    7
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Product" table="product_">

    <many-to-one name="category" class="Category" column="cid" />

    </class>
    </hibernate-mapping>

2.2 一对多

  • Category 和 Product 是一对多的关系。

    • 一个 Product 对应一个 Category。
    • 一个 Category 对应多个 Product。
  • 为 Category 增加一个 Set 集合。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Category" table="category_">

    <set name="products" lazy="false">
    <key column="cid" not-null="false" />
    <one-to-many class="Product" />
    </set>

    </class>
    </hibernate-mapping>

2.3 多对多

  • Product 和 User 之间是多对多的关系。

    • 一种 Product 可以被多个 User 购买。
    • 一个 User 可以购买多种 Product。
  • 要实现多对多关系,必须有一张中间表 user_product 用于维护 User 和 Product 之间的关系。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <hibernate-mapping package="com.how2java.pojo">
    <class name="User" table="user_">

    <set name="products" table="user_product" lazy="false">
    <key column="uid" />
    <many-to-many column="pid" class="Product" />
    </set>

    </class>
    </hibernate-mapping>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Product" table="product_">

    <set name="users" table="user_product" lazy="false">
    <key column="pid" />
    <many-to-many column="uid" class="User" />
    </set>

    </class>
    </hibernate-mapping>

3、注解

  • Hibernate 的注解是什么

    • 简单的说,本来放在 hbm.xml 文件里的映射信息,现在不用配置文件做了,改由注解来完成。
    • Hibernate 里常用注解包括:类注解,属性注解,关系注解,其他的注解。
  • 类与属性 注解

类注解 介绍
@Entity 表示这是一个实体类,用于映射表
@Table(name = “product_”) 表示这是一个类,映射到的表名:product_
属性注解 介绍
@Id 表示这是主键
@GeneratedValue(strategy = GenerationType.IDENTITY) 表示自增长方式使用 mysql 自带的
@Column(name = “id”) 表示映射到字段 id
关系注解 介绍
@ManyToOne 表示多对一关系
@OneToMany 表示一对多。fetch=FetchType.EAGER 表示不进行延迟加载,FetchType.LAZY 表示要进行延迟加载
@ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) 表示多对多
@JoinColumn(name=”cid”) 表示关系字段是 cid
  • 注解手册

    • 列出了常用的注解以供使用的时候查询。
  • 注解 vs XML

    • 小项目,参与人数不多,不复杂的用注解,开发快速。
    • 复杂项目,多人交互,配置量大,维护复杂度高的,用 XML 配置文件。

4、相关概念

4.1 事务

  • Hibernate 的任何对数据有改动的操作,都应该被放在事务里面。在事务中的多个操作行为,要么都成功,要么都失败

  • Hibernate 中的事务由 s.beginTransaction(); 开始,由 s.getTransaction().commit(); 结束。

    1
    2
    3
    4
    Session s = sf.openSession();

    s.beginTransaction();
    s.getTransaction().commit();
  • 在 MySql 中,只有当表的类型是 INNODB 的时候,才支持事务,所以需要把表的类型设置为 INNODB,否则无法观察到事务。

    1
    2
    3
    4
    5
    # 修改表的类型为 INNODB
    > alter table product_ ENGINE = innodb;

    # 查看表的类型的 SQL
    > show table status from test;

4.2 延迟加载

  • Hibernate 中的延迟加载(lazyload)分为两种

    • 属性的延迟加载:当使用 load 的方式来获取对象的时候,只有访问了这个对象的属性,hibernate 才会到数据库中进行查询。否则不会访问数据库。
    • 关系的延迟加载:在 one-many、many-many 的时候都可以使用关系的延迟加载。
  • 属性延迟加载

    1
    2
    3
    Product p = (Product)s.load(Product.class, 1);

    System.out.println(p.getName());
  • 关系延迟加载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Category" table="category_">

    <set name="products" lazy="true">
    <key column="cid" not-null="false" />
    <one-to-many class="Product" />
    </set>

    </class>
    </hibernate-mapping>

4.3 级联

  • 简单的说,没有配置级联的时候,删除分类,其对应的产品不会被删除。

  • 但是如果配置了恰当的级联,那么删除分类的时候,其对应的产品都会被删除掉。

  • 级联通常用在 one-many 和 many-to-many上,几乎不用在 many-one 上。

  • 级联有 4 种类型

类型 介绍
all 所有操作都执行级联操作
none 默认,所有操作都不执行级联操作
delete 删除时执行级联操作
save-update 保存和更新时执行级联操作
  • delete 级联

    1
    <set name="products" cascade="delete" lazy="false">
  • save-update 级联

    1
    <set name="products" cascade="save-update" lazy="false">

4.4 缓存

  • Hibernate 默认是开启一级缓存的,一级缓存存放在 session 上。

  • Hibernate 本身不提供二级缓存,都是使用第三方的二级缓存插件。二级缓存是在 SessionFactory 上。

    1
    2
    3
    4
    5
    6
    7
    8
    <hibernate-configuration>
    <session-factory>

    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

    </session-factory>
    </hibernate-configuration>
    1
    2
    3
    4
    5
    6
    7
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Category" table="category_">

    <cache usage="read-only" />

    </class>
    </hibernate-mapping>

4.5 分页

  • Hibernate 使用 Criteria 进行分页查询,无论使用的是 Oracle,Mysql,NoSQL 还是 DB2,分页查询的代码写法都是一样的。

    1
    2
    3
    4
    5
    Criteria c = s.createCriteria(Product.class);
    c.add(Restrictions.like("name", "%" + name + "%"));

    c.setFirstResult(2); // 从第 3 条数据开始
    c.setMaxResults(5); // 一共查询 5 条数据

4.6 获取对象方式

  • 通过 id 获取对象有两种方式,分别是 get 和 load。

    1
    2
    3
    s.get(Product.class, 6);

    s.load(Product.class, 6);
  • 他们的区别分别在于:

    • 延迟加载:

      • get 方式是非延迟加载,无论后面的代码是否会访问到属性,马上执行 sql 语句。
      • load 方式是延迟加载,只有属性被访问的时候才会调用 sql 语句。
    • 对于 id 不存在的时候的处理:

      • get 方式会返回 null。
      • load 方式会抛出异常。

4.7 获得 Session 方式

  • Hibernate 有两种方式获得 session,分别是 openSession 和 getCurrentSession。

    1
    2
    3
    Session s = sf.openSession();

    Session s = sf.getCurrentSession();
  • 他们的区别在于:

    • 获取的是否是同一个 session 对象:

      • openSession 每次都会得到一个新的 Session 对象。
      • getCurrentSession 在同一个线程中,每次都是获取相同的 Session 对象,但是在不同的线程中获取的是不同的 Session 对象。
    • 事务提交的必要性:

      • openSession 只有在增加,删除,修改的时候需要事务,查询时不需要的。
      • getCurrentSession 是所有操作都必须放在事务中进行,并且提交事务后,session 就自动关闭,不能够再进行关闭。

4.8 N+1

  • N+1 的意思是,首先执行一条 sql 语句,去查询这 100 条记录,但是,只返回这 100 条记录的 ID,然后再根据 id,进行进一步查询。

  • 如果 id 在缓存中,就从缓存中获取 product 对象了,否则再从数据库中获取。

  • N+1 中的 1,就是指只返回 id 的 SQL 语句,N 指的是如果在缓存中找不到对应的数据,就到数据库中去查。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    String name = "iphone";
    Query q = s.createQuery("from Product p where p.name like ?");
    q.setString(0, "%" + name + "%");

    Iterator<Product> it = q.iterate();
    while(it.hasNext()){
    Product p = it.next();
    System.out.println(p.getName());
    }

4.9 查询总数

  • 返回满足条件的总数

    1
    2
    3
    4
    5
    String name = "iphone";
    Query q = s.createQuery("select count(*) from Product p where p.name like ?");
    q.setString(0, "%" + name + "%");

    long total = (Long) q.uniqueResult();

4.10 乐观锁

  • Hibernate 使用乐观锁来处理脏数据问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <hibernate-mapping package="com.how2java.pojo">
    <class name="Product" table="product_">
    <id name="id" column="id">
    <generator class="native">
    </generator>
    </id>

    <!-- version 元素必须紧挨着 id 后面 -->
    <version name="version" column="ver" type="int"></version>

    </class>
    </hibernate-mapping>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Product {
    int version;

    public int getVersion() {
    return version;
    }
    public void setVersion(int version) {
    this.version = version;
    }
    }

4.11 C3P0 连接池

  • C3P0 连接池 是免费的第三方的数据库连接池,并且有不错的表现。

  • 建立数据库连接时比较消耗时间的,所以通常都会采用数据库连接池的技术来建立多条数据库连接,并且在将来持续使用,从而节约掉建立数据库连接的时间。

  • hibernate 本身是提供了数据库连接池的,但是 hibernate 官网也不推荐使用他自带的数据库连接池。

  • 一般都会使用第三方的数据库连接池。

文章目录
  1. 1. 前言
  2. 2. 1、基础
    1. 2.1. 1.1 配置
    2. 2.2. 1.2 对象状态
    3. 2.3. 1.3 CRUD
  3. 3. 2、关系
    1. 3.1. 2.1 多对一
    2. 3.2. 2.2 一对多
    3. 3.3. 2.3 多对多
  4. 4. 3、注解
  5. 5. 4、相关概念
    1. 5.1. 4.1 事务
    2. 5.2. 4.2 延迟加载
    3. 5.3. 4.3 级联
    4. 5.4. 4.4 缓存
    5. 5.5. 4.5 分页
    6. 5.6. 4.6 获取对象方式
    7. 5.7. 4.7 获得 Session 方式
    8. 5.8. 4.8 N+1
    9. 5.9. 4.9 查询总数
    10. 5.10. 4.10 乐观锁
    11. 5.11. 4.11 C3P0 连接池
隐藏目录