持久化类

概述

持久化:将内存中的对象持久化到数据库中的过程就是持久化。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. 应用间共享文件 FileProvider

    应用间共享文件 FileProvider 7.0及以上版本,分析文件给其他进程访问的时候,需要使用FileProvider,否则会出现崩溃: 例如:用系统下载器下载apk,然后通过Intent安装. ...

  2. django聚合查询

    聚合¶ Django 数据库抽象API 描述了使用Django 查询来增删查改单个对象的方法.然而,有时候你需要获取的值需要根据一组对象聚合后才能得到.这份指南描述通过Django 查询来生成和返回聚 ...

  3. 解决“Comparison method violates its general contract!”

    The ONE跑MaxProp.Prophet可能(取决于你JDK的版本)会报“java.lang.IllegalArgumentException: Comparison method violat ...

  4. myeclipse中的classpath .

    博客分类: java基础   myeclipse中的classpath是一个很重要的问题 myeclipse的在查找的时候都是按照其查找,而且myeclipse有一个专门的文件来保存classpath ...

  5. laravel5.8笔记八:数据库(单库和多库)

    数据库配置:根目录下/.env, 单个数据库 .env配置 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT= DB_DATABASE=shop DB_USE ...

  6. composer lavarel 安装

    一:packagist库:https://packagist.org/packages/laravel/laravel 二:composer安装 // 安装到laravel文件夹 composer c ...

  7. makefile编译错误情况整理

    错误情况1:makefile:5: *** 遗漏分隔符 . 停止 原因:具体的编译动作,开头不可以有空格,留白是由 按tab键形成的. 解决方法:去掉空格,改为tab键后,再执行make命令,成功. ...

  8. 解决org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)错误

    我调这个bug调了一天多,在网上搜索的检查namespace,package等,都没有错.错误提示是没有找到xml文件,我就纳闷了,为什么找不到呢?后来才发现,原来是resource中奇怪的目录为题, ...

  9. spring boot中的jave注解学习

    在spring中,不仅框架作者会使用java注解,开发者也常使用. 可以随手给个例子:在org.springframework.boot.autoconfigure.jdbc.DataSourcePr ...

  10. css 获取从第n个开始,之后的所有元素

    <div id="box"> <div></div> <div>等待获取</div> <div>等待获取&l ...