Hibernate 学习(二)
一、Hibernate 核心 API
1、Configuration 对象(配置)
配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
- 数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。
- 类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。
2、SessionFactory 对象
配置对象被用于创造一个 SessionFactory 对象,使用提供的配置文件为应用程序依次配置 Hibernate,并允许实例化一个会话对象。
SessionFactory 是一个线程安全对象并由应用程序所有的线程所使用。
SessionFactory 是一个重量级对象所以通常它都是在应用程序启动时创造然后留存为以后使用。每个数据库需要一个 SessionFactory 对象使用一个单独的配置文件。所以如果你使用多种数据库那么你要创造多种 SessionFactory 对象。
主要作用:负责创建 Session 对象。
概念:SessionFactory 对象中保存了当前的数据库配置信息和所有映射关系以及预定义的SQL语句。同时,SessionFactory 还负责维护 Hibernate 的二级缓存,查询缓存。
注意:
1、SessionFactory 对象的创建会有较大的开销,因为 SessionFactory 内部采取了线程安全的设计方式,因此在实际中 SessionFactory 对象可以尽量的共享,在大多数情况下,一个应用中针对一个数据库可以共享一个 SessionFactory 实例。
2、一个请求:一个 Session 对象(包含一个连接对象,一个事务对象,一个一级缓存)。
重要的方法:
1、openSession,这个方法代表,开启一个全新的Session
- 全新的连接
- 全新的事务
- 全新的一级缓存
2、getCurrentSession:得到当前上下文中的session
- 如果当前上下文中存在 session,则使用该session;
- 如果当前上下文中不存在 session,则使用opensession创建一个新的 session ;放到当前线程
- 要使用 getCurrentSessoin,必须在hibernate.cfg.xml中配置 thread <property name="current_session_context_class">thread</property>
- getCurrentSession得到的session是和事务绑定的;
- 无论是 DML (数据操作语言)还是 DQL (数据查询语言) ,都必须开启事务
- 提交事务的时候就是关闭 session
所以可以把创建 SessionFactory 对象提取为一个工具类,使用静态单例模式:
package com.hibernate.util; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; /** * @author zt1994 2018/3/7 10:13 */ public class HibernateUtil { private static SessionFactory sessionFactory; static { try { //1.读取并解析配置文件 Configuration configuration = new Configuration(); //2.加载配置文件,如果不设置加载默认配置文件hibernate.cfg.xml configuration.configure("hibernate.cfg.xml"); //3.生成会话工厂 sessionFactory = configuration.buildSessionFactory(); } catch (Exception e){ e.printStackTrace(); } } public static Session getSession(){ //4.获取session return sessionFactory.openSession(); } }
3、Session 对象
一个被用于获取与数据库的物理连接的会话。Session 对象是轻量级的,被设计为每次实例化都需要与数据库交互。使用最广泛,也被称为持久化管理器,它提供和持久化相关的操作。增、删、改、查等。
Session 对象不应该长时间保持开启状态因为它们不是线程安全,它们应该按照所需创造和销毁,因为Session 对象是轻量级的,所以创建和销毁不需要消耗太多资源。
因此,一个 Session 对象只可以由一个线程使用。避免多个线程共享。
主要功能:Session 的主要功能是为映射实体类的实例提供创建,读取和删除操作。
1、一级缓存
Session中有一个缓存,称为一级缓存。存放当前工作单元加载的对象。
测试一级缓存:
/** * 测试缓存 */ @Test public void testCache(){ //1.获取session Session session = HibernateUtil.getSession(); Transaction transaction = null; try { //2.开启事务 transaction = session.getTransaction(); transaction.begin(); //3.操作CRUD Product product1 = (Product) session.get(Product.class, 70); System.out.println(product1.hashCode()); Product product2 = (Product) session.get(Product.class, 70); System.out.println(product2.hashCode()); Product product3 = (Product) session.get(Product.class, 71); System.out.println(product3.hashCode()); transaction.commit(); } catch (Exception e) { if (transaction != null) transaction.rollback(); //回滚 e.printStackTrace(); } finally { //4.关闭资源 session.close(); } }
输出:
结论:先从一级缓存取,取不到,发出sql获取,放入一级缓存
一级缓存命中:只会发出一条sql
一级缓存命中条件:同一个 session 的OID是否相同
一级缓存内部的结构:
OID:Object ID,hibernate 里面定义
操作的持久对象全限定类名+"#"+数据库主键的值
com.hibernate.day01.model.LoginUser#1
存放在 Map<String,Object> cacheMap
2、Hibernate 状态:
瞬时状态(transient):瞬时状态刚刚用 new 语句创建,没有和 session 发生关系没有被持久化,不处于 session 中。该对象成为瞬时对象。
持久化状态(persistent):托管状态和 session 发生关系已经被持久化,加入到 session 的一级缓存中。该状态的对象为持久化对象。
游离状态(detached):脱管状态已经被持久化,但不处于 session 中。该状态的对象为游离对象。
删除状态(removed):从jpa出现,才有的状态只有调用了session.delete(domain 对象)方法对象有关联的ID,并且在 Session 管理下,但是已经被计划删除(真正删除是提交事务的时候)。
3、把持久状态的对象变成游离状态
session.close():把一级缓存清空,并关闭。
session.clear():把一级缓存清空。
session.evict(object持久对象):清空一级缓存里面指定的持久对象。
4、Transaction 对象
一个事务代表了与数据库工作的一个单元并且大部分 RDBMS 支持事务功能。在 Hibernate 中事务由底层事务管理器和事务(来自 JDBC 或者 JTA)处理。这是一个选择性对象,Hibernate 应用程序可能不选择使用这个接口,而是在自己应用程序代码中管理事务。
5、Query 对象
Query 对象使用 SQL 或者 Hibernate 查询语言(HQL)字符串在数据库中来检索数据并创造对象。一个查询的实例被用于连结查询参数,限制由查询返回的结果数量,并最终执行查询。
6、Criteria 对象
Criteria 对象被用于创造和执行面向规则查询的对象来检索对象。
二、脏数据更新
一个持久状态对象在事务管理内,一但改变原来的数据(非主键)(出现脏数据),事务提交的时候自动发出update去修改。
/** * 测试脏读 * 原因: * 1、通过Session拿到的对象是持久化对象,这个对象会放到一级缓存里面; * Hibernate会为当前对象准备一个快照(备份); * 2、提交事务时,会把快照和现在的对象进行对比, * 如果相同,就不需要修改,也不会发生SQL语句; * 如果不同,Hibernate就会认为现在这个数据是脏数据,会把它进行数据库同步(发生SQL语句) */ @Test public void test1(){ //1.获取session Session session = HibernateUtil.getSession(); Transaction transaction = null; try { //2.开启事务 transaction = session.getTransaction(); transaction.begin(); //3.操作CRUD Product product = (Product) session.get(Product.class, 70); product.setName("脏读测试"); //4.提交事务 transaction.commit(); } catch (Exception e) { if (transaction != null) transaction.rollback(); //回滚 e.printStackTrace(); } finally { //5.关闭资源 session.close(); } }
输出:
可以从输出结果看出,自动调用了 update 方法进行数据库同步。
三、延迟加载
延迟加载(懒加载):真正需要非主键属性,才发出sql,获取非主键属性值提高性能,但是如果把 session 提前关闭,会出现(延迟加载)延迟初始化异常。
注意:
1、没有对应主键,报错
2、提前关闭session,报错
/** * 测试延迟加载 */ @Test public void testLoad(){ //1.获取session Session session = HibernateUtil.getSession(); Transaction transaction = null; try { //2.开启事务 transaction = session.getTransaction(); transaction.begin(); //3.操作CRUD Product product = (Product) session.load(Product.class, 1); //只是生成SQL,并没有发送 System.out.println(product.getName()); //在这里才发送SQL语句 //4.提交事务 transaction.commit(); } catch (Exception e) { if (transaction != null) transaction.rollback(); //回滚 e.printStackTrace(); } finally { //5.关闭资源 session.close(); } }
get、load 的区别: