2.1 什么是持久化类
Hibernate是持久层的ORM映射框架,用于数据的持久化工作。数据持久化,就是将内存中的数据永久存储到关系型数据库中。那么持久化类就是一个Java类与数据库表建立了映射关系,这个类就是持久化类。
2.2 持久化类的编写规则
- 提供无参构造,因为在Hibernate的底层需要使用反射生成类的实例。
- 属性私有,对私有的属性提供get和set方法。因为在Hibernate底层会将查询到数据进行封装。
- 属性尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同。例如: 工资为0和工资为空。如果用基本数据类型默认值是0,如果录入数据的人员忘记录入,该员工的工资就为0。而包装类型默认值为null,就不会存在此现象。
- 要有一个唯一标识OID与表的主键对应。因为在Hibernate中是通过这个唯一标识OID区分在内存中是否是同一个持久化类。
- 持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象是用的是字节码的增强技术完成的,其实就是产生了一个当前类的一个子类对象实现的。
2.3 Hibernate主键生成策略
2.3.1 主键的类型
- 自然主键: 把具有业务含义的字段作为主键。
- 代理主键: 把不具备业务含义的字段作为主键
2.3.2 Hibernate的主键生成策略
- increment : 由Hibernate自动递增的方式生成唯一标识符
- identity : 采用底层数据库本身的主键生成标识符。
- sequence : Hibernate根据底层数据库序列生成标识符。
- native : 根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo三种生成器的一种。(推荐使用)
- uuid : Hibernate采用128位的UUID算法来生成标识符。不建议使用,因为占空间较大。
- assigned : 由java程序负责生成标识符,如果不指定id元素的generator属性,则默认使用该主键生成策略。适用于自然主键。
2.4 Hibernate的持久化对象的三种状态
2.4.1 持久化对象三种状态的概述
- 瞬时态 : 也称临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID,尚未与Hibernate Session关联,在数据库中也没有记录,失去引用后将被JVM回收。
- 持久化态:存在持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久化对象是在事务还未提交前变成持久态的。
- 脱管态 : 也曾离线态或者游离态,当某个持久化状态的实例与Session的关联被关闭是就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库的数据存在关联,只是失去了当前Session的关联,脱管状态对象发生改变是Hibernate不能检测到。
2.4.2 三种状态的区别
// 测试三种状态
@Test
public void testHibernate01() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer(); //瞬时态,没有持久化标识OID,没有被session管理
customer.setCust_name("小王");
Serializable id = session.save(customer); // 持久态对象,有持久化标识OID,被session管理
tx.commit();
session.close();
}
2.4.3 持久态对象能够自动更新数据库
// 测试持久化类的持久化对象有自动更新数据库的能力
@Test
public void testHibernate02() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 获得持久化态的对象
Customer customer = session.get(Customer.class, 1l);
customer.setCust_name("xx");
// session.update(customer); // 不再需要手动调用update方法就可以更新
tx.commit();
session.close();
}
2.5 Hibernate的一级缓存
缓存其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。
Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据库数据的备份。其中第一集缓存位Hibernate的内置缓存,不能被卸载。
2.5.1 什么是Hibernaet的一级缓存
Hibernate的一级缓存指的是Session缓存, Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配的OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询导所需数据时,该数据信息也会放置导一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
Hibernate的一级缓存有如下特点:
- 当应用程序调用Session接口的Save()、update()、saveOrUpdate时,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询导的相应对象信息加入到一级缓存中去。
- 当调用Session接口的load()、get()方法,以及Query接口的list()、iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,再去数据库中查询对应对象,并添加到一级缓存中。
- 当调用Session的close()方法时,Session缓存会被清空。
2.5.2 测试一级缓存
// 测试Hibernate的一级缓存的存在
@Test
public void testHibernate03() {
Session session = HibernateUtils.openSession();
Customer customer = session.get(Customer.class, 1l); // 马上执行一条sql语句查询一号客户,并将数据存入了一级缓存
Customer customer2 = session.get(Customer.class, 1l); // 没有执行sql语句,从一级缓存中获取数据
System.out.println(customer == customer2);
session.close();
}
2.6 Hibernate的事务控制
2.6.1 什么时事务
一项事务时由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元。当事务中的所以有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被回滚。
2.6.2 事务的四个特性
- 原子性 : 表示将事务中所做的操作捆绑成一个不可分割的单元,即对事务所进行的数据修改等操作,要么全部执行,要么全部不执行。
- 一致性 : 表示事务完成时,必须所有的数据都保持一致状态。
- 隔离性 : 指一个事务的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其它事务是隔离的,并发执行的各个事务之间不能相互干扰。
- 持久性 : 事务一旦提交,它对数据库中数据的改变就应该是永久性的。
2.6.3 事务的并发问题
- 脏读 : 一个事务读取到另一个事务未提交的数据。
- 不可重复读 : 一个事务读到了另一个事务已经提交的update的数据,导致再同一个事务中的多次查询结果不一样。
- 虚读/幻读 : 一个事务读到了另一个事务已经提交的insert的数据,导致再同一个事务中的多次查询结果不一样。
2.6.4 事务的隔离级别
在标准SQL规范中,定义了4个事务隔离级别,不同的隔离级别对事务的处理不同。
- 读未提交(1级): 可以读到未提交的数据。
- 已提交读(2级) : 可以读到已提交的数据。
- 可重复读(4级) : 保证两次查到的数据一致。
- 串行化(8级): 事务只能一个接着一个的执行。
2.6.5 Hibernate配置隔离级别
<!--
事务隔离级别
1 -- 读未提交
2 -- 已提交读
3 -- 可重复读
4 -- 串行化 -->
<property name="hibernate.connection.isolation">4</property>
2.6.6 Hibernate的事务管理
- 业务开始之前打开事务,业务执行之后提交事务. 执行过程中出现异常.回滚事务.
- 在dao层操作数据库需要用到session对象.在service控制事务也是使用session对象完成. 我们要确保dao层和service层使用的使用同一个session对象
- 在hibernate中,确保使用同一个session的问题,hibernate已经帮我们解决了. 我们开发人员只需要调用sf.getCurrentSession()方法即可获得与当前线程绑定的session对象
- 调用getCurrentSession方法必须配合主配置中的一段配置
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
- 通过getCurrentSession方法获得的session对象.当事务提交时,session会自动关闭.不要手动调用close关闭.
2.7 Hibernate的HQL查询(多表查询,但不复杂时使用)
HQL查询-hibernate Query Language
Hibernate独家查询语言,属于面向对象的查询语言
2.7.1 基本查询
// 1. 查询所有记录
@Test
public void testHibernate04() {
// 1. 获得Hibernate的Session对象
Session session = HibernateUtils.openSession();
// 2. 编写HQL语句
String hql = "from Customer";
// 3. 调用session.createQuery创建查询对象
Query query = session.createQuery(hql);
// 4. 调用Query对象的list()方法或uniqueResult()方法执行查询
List<Customer> list = query.list();
// Customer customer = (Customer) query.uniqueResult(); 接受唯一的查询结果
System.out.println(list);
}
2.7.2 条件查询 ?占位符查询
// HQL条件查询 ?占位符
@Test
public void testHibernate05() {
// 1. 获得Hibernate的Session对象
Session session = HibernateUtils.openSession();
// 2. 编写HQL语句
String hql = "from Customer where cust_name = ?";
// 3. 创建查询对象
Query query = session.createQuery(hql);
// 4. 设置参数
query.setString(0, "xx");
// 5. 调用list方法或uniqueResult()方法执行查询
List<Customer> list = query.list();
System.out.println(list);
}
2.7.3 条件查询 命名占位符查询
// HQL命名占位符
@Test
public void testHibernate06() {
// 1. 获得Hibernate的Session对象
Session session = HibernateUtils.openSession();
// 2. 编写HQL
String hql = "from Customer where cust_name = :name and cust_id = :id";
// 3. 创建查询对象
Query query = session.createQuery(hql);
// 4. 根据名字设置参数
query.setString("name", "xx");
query.setLong("id", 1l);
// 5. 调用uniqueResult()方法执行查询
Customer customer = (Customer)query.uniqueResult();
System.out.println(customer);
}
2.7.4 分页查询
// 分页查询
@Test
public void testHibernate07() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 编写hql语句
String hql = "from Customer";
// 3. 创建查询对象
Query query = session.createQuery(hql);
// 4. 设置分页查询
query.setFirstResult(3); // 从第几行开始查 0 代表第一条
query.setMaxResults(3); // 每次查几条数据
// 5. 执行查询
List<Customer> list = query.list();
System.out.println(list.get(0).getCust_name());
}
2.8 Criteria查询(单表条件查询)
Hibernate自创的无语句面向对象查询
2.8.1 基本查询
// Criteria 基本查询
@Test
public void testHibernate08() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 通过Session获得Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
// 3. 执行查询
List<Customer> list = criteria.list();
System.out.println(list);
}
2.8.2 条件查询
// Criteria 条件查询
@Test
public void testHibernate09() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 通过Session获得Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
// 3. 用add方法加入查询条件
criteria.add(Restrictions.eq("cust_name", "xx"));
// 4. 执行查询
List<Customer> list = criteria.list();
}
2.8.3 分页查询
// 分页查询
@Test
public void testHibernate10() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
// 3. 设置分页参数
criteria.setFirstResult(3);
criteria.setMaxResults(3);
// 4. 执行查询
List<Customer> list = criteria.list();
System.out.println(list);
}
2.8.4 设置查询总记录数
// 设置查询总记录数
@Test
public void testHibernate11() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 创建Criteria对象
Criteria criteria = session.createCriteria(Customer.class);
// 3. 设置查询的聚合函数 => 总行数
criteria.setProjection(Projections.rowCount());
// 4. 执行查询
List<Customer> list = criteria.list();
System.out.println(list);
}
2.9 原生SQL查询(复杂的业务逻辑)
2.9.1 基本查询 返回List数组
// 原生SQL 基本查询 返回List数组
@Test
public void testHibernate12() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 编写SQL语句
String sql = "select * from cst_customer";
// 3. 创建SQLQuery查询对象
SQLQuery sqlQuery = session.createSQLQuery(sql);
List<Object[]> list = sqlQuery.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
}
2.9.2 基本查询 返回List对象
// 原生SQL 基本查询 返回List对象
@Test
public void testHibernate13() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 编写SQL语句
String sql = "select * from cst_customer";
// 3. 创建SQLQuery对象
SQLQuery sqlQuery = session.createSQLQuery(sql);
// 4. 封装到对象中
sqlQuery.addEntity(Customer.class);
// 5. 执行查询
List<Customer> list = sqlQuery.list();
for (Customer customer : list) {
System.out.println(customer);
}
}
2.9.3 条件查询
// 原生SQL 条件查询
@Test
public void testHibernate14() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 编写SQL语句
String sql = "select * from cst_customer where cust_name = ?";
// 3. 创建SQLQuery对象
SQLQuery sqlQuery = session.createSQLQuery(sql);
// 4. 设置参数
sqlQuery.setParameter(0, "xx");
// 5. 指定将结果封装到哪个对象中
sqlQuery.addEntity(Customer.class);
// 6. 执行查询
List<Customer> list = sqlQuery.list();
System.out.println(list);
}
2.9.4 分页查询
// 原生SQL 分页查询
@Test
public void testHibernate15() {
// 1. 获得Session对象
Session session = HibernateUtils.openSession();
// 2. 编写SQL语句
String sql = "select * from cst_customer limit ?, ?";
// 3. 创建SQLQuery对象
SQLQuery sqlQuery = session.createSQLQuery(sql);
// 4. 设置分页参数
sqlQuery.setParameter(0, 0);
sqlQuery.setParameter(1, 1);
// 5. 指定将结果封装到哪个对象中
sqlQuery.addEntity(Customer.class);
// 6. 执行查询
List<Customer> list = sqlQuery.list();
System.out.println(list);
}