持久化类
概述
持久化:将内存中的对象持久化到数据库中的过程就是持久化。Hibernate 就是用来进行持久化的框架。
持久化类:一个 Java 对象与数据库的表建立了映射关系,那么这个类在 Hibernate 中称为是持久化类。
编写规则
1、对持久化类提供一个无参构造方法。
2、属性需要私有,对私有属性提供 public 的 get 和 set 方法。
3、对持久化类提供一个唯一标识 OID 与数据库表主键对应。
Java 中通过对象的地址区分是否是同一个对象,数据库中通过主键区分是否是同一条记录,而在 Hibernate 中是通过持久化类的 OID 属性区分是否是同一个对象。
4、持久化类中属性尽量使用包装类类型。
5、持久化类不要用 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 级别的缓存。
-
证明一级缓存的存在
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
几种查询方式
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<指定类型>