hibernate_day02

hibernate_day02

1 对象状态与一级缓存

1.1 状态介绍

  • hibernate 规定三种状态:瞬时态、持久态、脱管态
  • 状态
    • 瞬时态:transient,session没有缓存对象,数据库也没有对应记录。
      • OID特点:没有值
    • 持久态:persistent,session缓存对象,数据库最终会有记录。(事务没有提交)
      • OID特点:有值
    • 脱管态:detached,session没有缓存对象,数据库有记录。
      • OID特点:有值
1.1.1 持久化类的定义
  1. 持久化类的定义
    • 持久化:把容易丢失的数据,长期保存的过程,持久化。
    • 参与这个持久化操作的类,就是持久化类。
    • 持久化类的特点:一个类与数据库的表建立了映射关系。
      • 持久化类= java类 + 映射文件(类名.hbm.xml文件)
  2. 持久化类的编写规则
    • 有无参的构造方法。
      • hibernate通过反射创建持久化对象
    • 属性私有化,对属性提供get/set方法。
      • java的规则
    • 属性尽量使用包装类。
      • int与Integer. 默认值不同。int:0, Integer:null。 数据库里面,0值和null值不同。
    • 持久化类要有一个唯一标识OID与表的主键对应。
      • 判断是否同一个java对象,使用的地址;判断是否同一个持久化对象,使用的是oid的值。(缓存的时候,体现)
    • 持久化类,不要使用final修饰符。
      • final修饰持久化类,导致不能被继承。load方法,使用的延迟加载机制,依赖于通过继承产生的代理对象。如果,不能被继承,延迟加载失效。
1.1.2 数据库主键的分类
  1. 数据库主键的分类
    • 自然主键:对象本身的属性,作为表的主键
    • 代理主键:不使用对象本身的属性,而使用没有任何意义的一个字段,作为主键。(推荐)
  2. 主键的生成方式
    • 自然主键:需用户手动输入
    • 代理主键:由系统自动生成。
  3. hibernate提供的主键生成策略
    • increment:适合代理主键,适合int,long等类型,由hibernate来生成。缺陷:在多线程或者集群环境,不能使用。原因:hibnernate会进行一个查询:select max(id) from user, +1,作为新的记录的候选主键。
    • identity:适合代理主键,适合int,long等类型,由底层数据库支持,要求底层数据库支持自增长类型。
    • sequence:适合代理主键,适合int,long等类型,由底层数据库支持,要求底层数据库支持序列类型。
    • native:适合代理主键,适合int,long等类型,根据底层数据库的特色,自动匹配。如果,底层数据库支持identity,native等价于identity; 如果,底层数据库支持sequence,native等价于sequence。(推荐)
    • uuid:适合代理主键,适合字符串类型。
    • assigned:适合自然主键,需要手动制定。(默认值)

1.2 转换

hibernate_day02_第1张图片
状态转换

1.2.1 瞬时态/临时态

瞬时态称为临时态或自由态,不存在持久化表示OID(相当于主键值),与Hibernate Session 不关联.

获得:一般都只直接创建(new)

瞬时态 转换 持久态
一般操作:save方法、saveOrUpdate
瞬时态 转换 脱管态
一般操作:通过setId方法设置数据

例如:

User user = new User();     //瞬时态
user.setUid(1);             //脱管态

1.2.2 持久态

  • 获得:查询操作:get、loat、createQuery、createCriteria 等 获得都是持久态【】
    • 执行save之后持久态
      
    • 执行update之后持久态
      
  • 持久态 转换 瞬时态
    • 官方规定执行delete() --民间:删除态
  • 持久态 转换 脱管态
    • session没有记录
      • session.close () 关闭
      • session.clear() 清除所有
      • session.evict(obj) 清除指定的PO对象

1.2.3 脱管态/游离态

  • 获得:创建、并设置OID
    • 通过api获得
  • 脱管态 转换 瞬时态
    • 手动去除OID,设置成默认值
  • 脱管态 转换 持久态
    • 一般操作:update()、saveOrUpdate
代码:案例
@Test
public void demo01(){
    User user = new User();     //瞬时态
    user.setUsername("jack");
    user.setPassword("1234");   //瞬时态(与oid没有关系)
    Session session = factory.openSession();
    session.beginTransaction();
    session.save(user);         //持久态
    //---- 持久态就应该有持久态的行为(特性)
    //user.setUsername("rose");  
    //持久态对象 被修改后,hibernate将自动生成update语句
    //session.flush();
    session.getTransaction().commit();
    session.close();
    System.out.println(user);  //脱管态
}

2 一级缓存

2.1 介绍

  • 缓存 : 作用降低应用程序直接读写永久性数据存储的频率,提高运行性能
    • 物理介质 : 内存
    • 存储的是数据库数据备份
  • 一级缓存 : session级别的缓存.
    • 当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

2.2 一级缓存操作

2.2.1 证明一级缓存
@Test
public void demo02(){
    //证明一级缓存
    Session session = factory.openSession();
    session.beginTransaction();
    
    //1 查询 id = 1
    User user = (User) session.get(User.class, 1);
    System.out.println(user);
    //2 再查询 -- 不执行select语句,将从一级缓存获得
    User user2 = (User) session.get(User.class, 1);
    System.out.println(user2);
    
    session.getTransaction().commit();
    session.close();
}
2.2.2 移除
@Test
public void demo03(){
    //清除缓存
    Session session = factory.openSession();
    session.beginTransaction();
    
    User user = (User) session.get(User.class, 1);  //--select
    System.out.println(user);
    
    //清除
    //session.clear();
    session.evict(user);
    
    // 一级缓存没有缓存对象,从数据库直接查询
    User user2 = (User) session.get(User.class, 1);  //--select
    System.out.println(user2);
    
    session.getTransaction().commit();
    session.close();
}
2.2.3 一级缓存快照--内部结构【掌握】
  • 快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。
    • 如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。
2.2.4 refresh 刷新
  • refresh 保证 一级缓存的数据 与 数据库的数据 保持一致。将执行select语句查询数据库,将一级缓存中的数据覆盖掉。只要执行refresh都将执行select语句。

代码:

@Test
public void demo04(){
    //刷新
    Session session = factory.openSession();
    session.beginTransaction();
    
    User user = (User) session.get(User.class, 1);  //--select
    System.out.println(user);
    
    session.refresh(user);
    
    session.getTransaction().commit();
    session.close();
}

2.3 PO对象操作

2.3.1 save & persist
  • save方法:瞬时态 转换 持久态 ,会初始化OID

    1.执行save方法,立即触发insert语句,从数据库获得主键的值(OID值)
    2.执行save方法前,设置OID将忽略。
    3.如果执行查询,session缓存移除了,在执行save方法,将执行insert
    

2.3.2 update

  • update:脱管态 转换 持久态

    如果OID在数据存放的,将执行update语句
    如果OID不存在将抛异常
    

案例:

// update: 两种方式:a.直接更新 b.先查询后更新(推荐)
@Test
public void test_update1() {
    Session session = HibernateUtils.openSession();
    Transaction transaction = session.beginTransaction();
    // 自身的业务逻辑 ---a.直接更新
    User user = new User();
    user.setId(5);
    user.setName("刘德华");
    session.update(user);
    transaction.commit();
    session.close();
}

@Test 
public void test_update2() {
    Session session = HibernateUtils.openSession();
    Transaction transaction = session.beginTransaction();
    // 自身的业务逻辑 ----b.先查询后更新(推荐)
    User user = session.get(User.class, 6);
    user.setName("郭富城");
    session.update(user);
    transaction.commit();
    session.close();
}
2.3.3 saveOrUpdate
  • 代理主键:
    • 判断是否有OID
      • 如果没有OID,将执行insert语句
      • 如果有OID,将执行update语句。

实例:

@Test
public void demo02(){
    // 代理 native
    User user = new User(); 
    // user.setUid(2);
    user.setUsername("jack2");
    user.setPassword("12345");
    Session session = factory.openSession();
    session.beginTransaction();
    session.saveOrUpdate(user);
    session.getTransaction().commit();
    session.close();
}
  • 自然主键:
    • 先执行select语句,查询是否存放
      • 如果不存在,将执行insert
      • 如果存在,将执行update

实例:

@Test
public void demo02(){
    // 自然 assigned
    User user = new User(); 
    user.setUid(2);
    user.setUsername("jack2333");
    user.setPassword("12345333");
    Session session = factory.openSession();
    session.beginTransaction();
    session.saveOrUpdate(user);
    session.getTransaction().commit();
    session.close();
}

2.3.4 delete

@Test
// 删除---直接指定id对象
public void test_delete(){
    Session session =HibernateUtils.openSession();
    Transaction transaction = session.beginTransaction();
    // 自身的业务逻辑
    User user = new User();
    user.setId(5);
    session.delete(user);
    transaction.commit();
    session.close();
}

@Test
// 删除---先通过id查询该对象,再删除
public void test_delete2(){
    Session session =HibernateUtils.openSession();
    Transaction transaction = session.beginTransaction();
    // 自身的业务逻辑
    User user = session.get(User.class, 6);
    session.delete(user);
    transaction.commit();
    session.close();
}

3 事务

3.1 概念

在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的SQL 语句组成的一个不可分割的工作单元。当事务中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务会被回滚。

3.1.1 事务的四个特性
  • ACID
    • Atomic(原子性) : 事务内的所有操作不可分割,视作一个整体
    • Consistency(一致性) : 事务状态保持一致
    • Isolation(隔离性) : 事务与事务间是独立的,互不相干
    • Durability(持久性) : 事务提交后,对数据做的操作是持久性的,在数据库中已经保存

3.2 事务的并发问题

  1. 脏读:一个事务读取到另一个事务未提交的数据
  2. 不可重复读:一个事务读取到了另一个事务已经提交的update的数据,导致在同一个事务中多次查询结果不一致
  3. 虚读/幻读:一个事务读取到了另一个事务已经提交的insert的数据,导致在同一个事务中多次查询结果不一致

【总结】 在多个事务同时使用相同的数据的时候,可能会出现并发问题。

3.3 事务的隔离级别

  1. 为了平衡事务并发问题的解决与数据库的效率,设计了多种隔离级别:

  2. 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(重复读)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。

     隔离级别            脏读   不可重复读   虚读(幻读)
     Read uncommitted    √       √          √
     Read committed      ×       √          √
     Repeatable read     ×       ×          √
     Serializable        ×       ×          ×
     备注: √: 可能出现    ×: 不会出现
    
  3. 每个数据库连接都有默认的隔离级别,通常是读已提交或可重复读.可以通过数据库配置设置,也可在应用程序中设置。例如Hibernate:在hibernate.cfg.xml中设置隔离级别:

     1—Read uncommitted isolation:读未提交
     2—Read committed isolation:读提交
     4—Repeatable read isolation:重复读
     8—Serializable isolation:序列化
    
     
     2
    
  4. hibernate管理session的三种方式

    通过上面的分析,肯定使用ThreadLocal将业务层的session绑定当前线程是最佳方案。其实,hibernate已经给我们准备好了。我们只需要进行一段配置即可。

    • Hibernate 5 自身提供了三种管理 Session 对象的方法
      • Session对象的生命周期与本地线程绑定
      • Session对象的生命周期与JTA事务绑定
      • Session对象的生命周期由Hibernate委托程序管理

你可能感兴趣的:(hibernate_day02)