持久化类

概述

持久化:将内存中的对象持久化到数据库中的过程就是持久化。Hibernate 就是用来进行持久化的框架。

持久化类:一个 Java 对象与数据库的表建立了映射关系,那么这个类在 Hibernate 中称为是持久化类。

也可以看作:持久化类 = JavaBean + 映射文件

编写规则

1、对持久化类提供一个无参构造方法。

Hibernate 底层使用反射生成实例。

2、属性需要私有,对私有属性提供 public 的 get 和 set 方法。

Hibernate 底层获取和设置对象的值就是通过 get 和 set 方法。

3、对持久化类提供一个唯一标识 OID 与数据库表主键对应。

Java 中通过对象的地址区分是否是同一个对象,数据库中通过主键区分是否是同一条记录,而在 Hibernate 中是通过持久化类的 OID 属性区分是否是同一个对象。

4、持久化类中属性尽量使用包装类类型。

因为包装类类型默认值为 null ,基本数据类型有默认值,如 int 默认为 0 。假如有一个 int 类型的 score 字段用来记录分数,为 null 时表示缺考,此时 int 就不支持了。

5、持久化类不要用 final 修饰。

延迟加载是 Hibernate 的一个优化手段,而它的原理是通过 javassist 返回一个代理对象,而 javassist 是通过底层字节码继承持久化类对持久化类实现产生代理,使用 final 描述的持久化类就不能继承,延迟加载也就会失效。

对象的三种状态

Hibernate 是持久层的框架,通过持久化类完成 ORM 操作。为了更好的管理,Hibernate 将持久化类对象分为了三种状态。

  • 瞬时态

    没有唯一标识 OID,且没有被 session 管理。

  • 游离(托管)态

    有唯一标示 OID,且没有被 session 管理。

  • 持久态

    有唯一标识 OID,且被 session 管理。

Session session1 = HibernateUtil.openSession();
Transaction transaction1 = session1.beginTransaction();
Customer customer = new Customer(); // 瞬时态:无唯一标识,未被 session1 管理。
customer.setCust_id(1L); // 未被 session1 管理,如果数据库数据有与之对应的唯一标示(即数据库表中有一行 cust_id=1 的数据),则为游离态,否则依旧是瞬时态。
customer.setCust_name("李四");
session1.saveOrUpdate(customer); // 交由 session1 管理,且数据库数据有对应唯一标示的数据,转为持久态。
transaction1.commit();
session1.close();

Session session2 = HibernateUtil.openSession();
Transaction transaction2 = session2.beginTransaction();
System.out.println(customer); // 之前托管到的 session1 已关闭,即未被 session1 也未被 session2 管理,但有与数据库数据对应的唯一标识,此时为游离态。
session2.delete(customer); // session2 主动放弃管理,且从数据库中删除与唯一标示对应的记录,转为瞬时态。
transaction2.commit();
session2.close();

/*
总结:
    瞬时态对象
        获得:
            Customer customer = new Customer();
        状态转换:
            ->持久态:
                session.save(customer); 、 session.saveOrUpdate(customer);
            ->游离态:
                customer.setCust_id(1L);
    游离态对象
        获得:
            Customer customer = new Customer();customer.setCust_id(1L);
        状态转换:
            ->持久态
                session.update(customer); 、 session.saveOrUpdate(customer);
            ->瞬时态:
                customer.setCust_id(null);
    持久态对象
        获得:
            session.get(Customer.class,1L); 、 session.load(Customer.class,1L);
        状态转换:
            ->瞬时态:
                session.delete(customer);
            ->游离态:
                session.close(); 、 session.clear(); 、 session1.evict(customer);
*/

代码演示状态转换:瞬时->游离->持久->游离->瞬时

主键生成策略

主键的分类

  • 自然主键

    主键本身就是表中有意义的字段。

    比如有一个人员表,每个人都会有一个身份证号(唯一不可重复),此时使用身份证号作为主键,这个主键就称为是自然主键。
  • 代理主键

    主键本身不是表中必须的字段。

    如有一个人员表,每个人都会有一个身份证号(唯一不可重复),但此时使用一个与这张表逻辑不相关的 PID 字段作为主键,这种主键就称为是代理主键。
在实际开发中,尽量使用代理主键:

一旦自然主键参与到业务逻辑中,后期就有可能修改源代码。

好的程序设计满足 OCP 原则,对程序的扩展是 open 的,对修改源码是 close 的。

主键的生成策略

  • increment

    hibernate 提供的自动增长机制,使用 short 、int 、long 类型的主键,在单线程程序中使用。

    发送一条 sql : select max(id) from 表 查询最大 id,然后用 id+1 作为下一条记录的主键。
  • identity

    适用 short 、int 、long 类型的主键,使用的是数据库底层的自增机制,适用于有自增机制的数据库(MySQL、MSSQL)。

  • sequence

    适用 short 、int 、long 类型的主键,采用的是序列的方式,适用于支持序列机制的数据库(Oracle)。

  • uuid

    适用于字符串类型主键,由 Hibernate 随机生成字符串主键。

  • native

    本地策略,可以在 identity 和 sequence 间自动切换。

  • assigned

    Hibernate 放弃主键的管理,通过手动编码给主键赋值。

  • foreign

    依赖外部主键,适用于一对一关联映射情况下使用。

缓存

缓存概述

一种优化的方式,将数据放入内存中,使用的时候直接从内存中取,不用通过存储源。

Hibernate 提供了两种缓存机制:一级缓存、二级缓存。

Hibernate的一级缓存

是 Session 级别的缓存,一级缓存生命周期与 Session 一致,由 Session 中一系列 Java 集合构成,自带不可卸载。

Hibernate 的一级缓存就是指 Session 缓存,Session 缓存是一块内存空间,用来存放管理的持久化类对象,在使用 Hibernate 查询对象时,首先会使用对象属性的唯一标示 OID 值在一级缓存中进行查找,如果找到匹配的 OID 值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同 OID 值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。

在 Session 接口的实现中包含一系列 Java 集合,这些 Java 集合构成了 Session 缓存。只要 Session 实例未结束生命周期,存放在它缓存中的持久化类对象也不会结束生命周期。所以一级缓存也被称为 Session 级别的缓存。

Hibernate 的一级缓存的作用就是减少对数据库访问的次数。
  • 证明一级缓存的存在

    Session session = HibernateUtil.openSession();
    Customer customer1 = session.get(Customer.class, 1L);// 发出 select 的 SQL 语句
    Customer customer2 = session.get(Customer.class, 1L);// 无 SQL 发出,只是取出上一行查询出的放到一级缓存的 customer1 赋值给 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一个对象
    session.close();

    查询时会将持久化类对象放入一级缓存

    Session session = HibernateUtil.openSession();
    Transaction transaction = session.beginTransaction();
    Customer customer1 = new Customer();
    customer1.setCust_name("郭德纲");
    Serializable id = session.save(customer1); // 在事务提交时发出 insert 的 SQL 语句,且将保存的对象放入一份到一级缓存中
    Customer customer2 = session.get(Customer.class, id); // 无 SQL 语句发出,只是取上一行新增时保存到一级缓存中的 customer1 赋值给 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一个对象
    transaction.commit();
    session.close();

    新增时会将持久化类对象放入一级缓存

    Session session = HibernateUtil.openSession();
    Transaction transaction = session.beginTransaction();
    Customer customer1 = new Customer();
    customer1.setCust_id(1L);
    customer1.setCust_name("郭德纲");
    session.update(customer1); // 在事务提交时发出 update 的 SQL 语句,且将要更新的对象放入一份到一级缓存中
    Customer customer2 = session.get(Customer.class, 1L); // 无 SQL 语句发出,只是取上一行更新时保存到一级缓存中的 customer1 赋值给 customer2
    System.out.println(customer1 == customer2);// true :即 customer1 和 customer2 是同一个对象
    transaction.commit();
    session.close();

    更新时会将持久化类对象放入一级缓存

  • 特殊区域:快照区

    当 Hibernate 将持久化类对象放入一级缓存时,并不仅仅只是放入一份,还拷贝了一份放入了一级缓存中的一个特殊区域-快照区。而 Hibernate 利用这个快照区实现了持久化类对象的一个特性,看如下示例:

    Session session = HibernateUtil.openSession();
    Customer customer = session.get(Customer.class, 1L);
    Transaction transaction = session.beginTransaction();
    customer.setCust_name("郭德纲");
    // session.update(customer); // 即便省略这个更新操作,在事务提交时 Hibernate 仍然会发出更新的 SQL 语句。
    transaction.commit();
    session.close();

    例:

    当我们使持久化类对象的属性发生改变时,一级缓存中对应的持久化对象也会随之发生改变,而快照区对应的持久化对象不变。而当事务提交时,Hibernate 会对比一级缓存中存放的持久化类对象和它对应快照中存放的持久化类对象。如果有差异,Hibernate 则帮我们执行更新操作;如果没有差异,则不会对数据库进行操作。

事务

事务回顾

点击查看

Hibernate中设置事务隔离级别

在核心配置文件 hibernate.cfg.xml 中添加如下属性即可:

<!--
配置事务隔离级别,有如下四个值:
    1 :读未提交 (Read uncommitted)
    2 :读已提交 (Read committed)
    4 :可重复读 (Repeatable read) 默认级别
    8 :串行化 (Serializable)
-->
<property name="hibernate.connection.isolation">4</property>

绑定Session到当前线程

在核心配置文件 hibernate.cfg.xml 中添加如下属性:

<!--
thread : Session 对象的生命周期与本地线程绑定。
jta : Session 对象的生命周期与 JTA 事务绑定。
managed : Hibernate 委托程序来管理 Session 对象的生命周期。
-->
<property name="hibernate.current_session_context_class">thread</property>

然后就可以保证在当前线程中通过 org.hibernate.SessionFactory.getCurrentSession 方法获取到的 Session 是同一个实例。抽取工具类:

package com.zze.util;

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

public class HibernateUtil {
    public static final Configuration cfg;
    public static final SessionFactory sf;

    static {
        cfg = new Configuration().configure();
        sf = cfg.buildSessionFactory();
    }

    public static Session openSession() {
        return sf.openSession();
    }

    public static Session getCurrentSession() {
        return sf.getCurrentSession();
    }
}

com.zze.util.HibernateUtil

Hibernate 内部是通过 ThreadLocal 来实现线程绑定 Session 的。

几种查询方式

HQL

HQL (Hibernate Query Language) ,相对 sql 来说,sql 中的表名在 hql 中用类名替代,sql 中的列名在 hql 中用属性名替代。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer"; // 简单查询
Query query = currentSession.createQuery(hql);
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

简单查询

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer where cust_name like ?";
Query query = currentSession.createQuery(hql);
query.setParameter(0, "张%");
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

条件查询

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
String hql = "from Customer";
Query query = currentSession.createQuery(hql);
query.setFirstResult(2); // 起始索引,从 0 开始
query.setMaxResults(2); // 每页条数
List<Customer> customerList = query.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

分页查询

QBC

QBC (Query By Criteria) API 提供了检索对象的另一种方式,它主要由 Criteria 接口、Criterion 接口和 Expresson 类组成,它支持在运行时动态生成查询语句,是一种更面向对象的查询方式。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

简单查询

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
criteria.add(Restrictions.like("cust_name", "张", MatchMode.END));
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

条件查询

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
Criteria criteria = currentSession.createCriteria(Customer.class);
criteria.setFirstResult(2);
criteria.setMaxResults(2);
List<Customer> customerList = criteria.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
transaction.commit();

分页查询

SQL

Hibernate 也支持我们使用原生 SQL 查询。

Session currentSession = HibernateUtil.getCurrentSession();
Transaction transaction = currentSession.beginTransaction();
SQLQuery sqlQuery = currentSession.createSQLQuery("select * from customer");
// 默认返回一个 Object 数组对象的列表,数组的每一项对应数据库中每一行数据的一列
List<Object[]> customerList = sqlQuery.list();
for (Object[] customer : customerList) {
    System.out.println(String.format("id:%s,cust_name:%s", customer[0], customer[1]));
}
transaction.commit();

简单查询:返回 List<Object[]>

Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
String sql = "select * from customer where cust_name like ?";
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addEntity(Customer.class);
sqlQuery.setParameter(0,"张%");
List<Customer> customerList = sqlQuery.list();
for (Customer customer : customerList) {
    System.out.println(customer);
}
session.getTransaction().commit();

条件查询:返回 List<持久化类>

Session session = HibernateUtil.getCurrentSession();
session.beginTransaction();
String sql = "select cust_id id,cust_name name from customer";
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.addScalar("id", new LongType());
sqlQuery.addScalar("name");
sqlQuery.setResultTransformer(Transformers.aliasToBean(Customer2.class));
List<Customer2> userList = (List<Customer2>)sqlQuery.list();
for (Customer2 customer2 : userList) {
    System.out.println(customer2);
}
session.getTransaction().commit();

简单查询:返回 List<指定类型>

java框架之Hibernate(2)-持久化类&主键生成策略&缓存&事务&查询的更多相关文章

  1. Hibernate之:各种主键生成策略与配置详解

    1.assigned 主键由外部程序负责生成,在 save() 之前必须指定一个.Hibernate不负责维护主键生成.与Hibernate和底层数据库都无关,可以跨数据库.在存储对象前,必须要使用主 ...

  2. 三 Hibernate持久化状态&主键生成策略

    持久化类 持久化:将内存中的一个对象持久化到数据库中的过程 持久化类:Java类+映射文件.Java中一个类与数据库的表建立了映射关系,那么这个类称为持久化类. 持久化类的编写规则: 对持久化类提供一 ...

  3. Hibernate(4)——主键生成策略、CRUD 基础API区别的总结 和 注解的使用

    俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: hibernate的主键生成策略 UUID 配置的补充:hbm2ddl.auto属性用法 注解还是配置文件 h ...

  4. Hibernate的几种主键生成策略

    主键类型: 业务主键(natural key):业务主键的值是来源于一个业务数据. 代理主键(surrogate key):代理主键需要采用一种方式来生成某个唯一值. 代理主键的生成策略: 1.hib ...

  5. Hibernate遇到oracle之主键生成策略

    一直用Hibernate+mysql,感觉Hibernate很好用,也出过什么大问题:这周,公司的产品要部署到Orecle,虽然产品号称支持Oracle但是我自己知道,这个产品压根儿就没在Oracle ...

  6. hibernate(二)主键生成策略

    hibernate主键生成策略主要指的是在实体类orm的配置 <id name=""> <generator class="native"&g ...

  7. (二)JPA实体类主键生成策略

    在JPA中,配置实体类的主键的生成策略使用 @GeneratedValue @Id @Column(name = "id") @GeneratedValue(strategy = ...

  8. 大家一起撸代码之——Hibernate各种主键生成策略与配置详解

    1.assigned 主键由外部程序负责生成,在 save() 之前必须指定一个.Hibernate不负责维护主键生成.与Hibernate和底层数据库都无关,可以跨数据库.在存储对象前,必须要使用主 ...

  9. Hibernate各种主键生成策略与配置详解

    出自:http://www.cnblogs.com/kakafra/archive/2012/09/16/2687569.html 1.assigned 主键由外部程序负责生成,在 save() 之前 ...

随机推荐

  1. 获取硬盘序列号的Fortran程序

    以前写了个获取硬盘序列号的fortran程序,但未经实证 program FortranDemo Use Kernel32 Implicit None Interface SUBROUTINE Get ...

  2. 解决ubuntu系统中firefox无法播放网页版音乐播放器音乐

    Reference: https://blog.csdn.net/h736131708/article/details/80775382 因为网页版的qq音乐或者网易云音乐都把音频换成了AAC格式,这 ...

  3. VMware Workstation 14.1.1 精简特别版

    VMware Workstation 精简特别版,由卡饭网友のcuiplay精简制作,集成许可证密钥安装即永久激活,该特别版最大特色可安装MAC OS X客户操作系统,此外添加了DELL SLIC 2 ...

  4. idea设置条件断点

    只有在指定的条件下才触发断点,在idea中如何设置呢? 方法: 按Ctrl+Shift+F8弹出View Breakpoints 在Condition设置触发条件 结果: 源码 https://git ...

  5. 使用Go语言+Protobuf协议完成一个多人聊天室

    软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...

  6. CreateWindowEx failed (当前程序已使用了 Window 管理器对象的系统允许的所有句柄。)

    我在QT图形场景视图中通过QGraphicsProxyWidget添加代理Widget(实现添加基本的QT Widget,如按钮.复选框.日期时间控件等),当数量超过3500左右的时候,QT应用程序直 ...

  7. Laravel Homestead 离线安装

    一.写在之前,网络不够快想要安装Homestead,也是一个浩大的工程,对于下载一个 1.22G左右的 laravel/homestead box 也是非常的麻烦.那么如何才能离线安装呢? 接着往下看 ...

  8. Android application 和 activity 标签详解

    extends:http://blog.csdn.net/self_study/article/details/54020909 Application 标签 android:allowTaskRep ...

  9. K - Popular Cows

    来源poj2186 Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N ...

  10. laravel的csrf token 的了解及使用

    之前在项目中因为没有弄清楚csrf token的使用,导致发请求的话,一直请求失败,今天就一起来看一下csrf的一些东西. 1.Cross-site request forgery 跨站请求伪造,也被 ...