Hibernate基础知识(3)

一、hbm 映射文件配置 (持久化配置和操作)

1、持久化对象(Persistent Object)

      PO = POJO + hbm映射配置

      编写规则:

      1)必须提供无参数public构造器;

      2)所有属性都为private,提供public的getter和setter方法;

      3)必须提供标识属性,与数据表中主键对应,例如Customer类id属性;

      4)PO类的属性应该尽量使用基本数据类型的包装类型。

      5)不要用final修饰(将无法生成代理对象进行优化)


      问题:Session的get方法和load方法的区别?

      session.get 方法, 查询立即执行 , 返回Customer类对象

      session.load 方法,默认采用延迟加载数据方式,不会立即查询,返回 Customer类子类对象 (动态生成代理对象)

      * 如果 PO类使用final修饰,load无法创建代理对象,返回目标对象本身 (load效果和 get效果 相同 )

      分析图解如下:



2、OID

      Java按地址区分同一个类的不同对象。

      关系型数据库用主键区分一条记录。

      Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系。

      对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate为OID赋值。


      OID指与数据表中主键对应PO类中属性,例如:Customer类中的id属性。

      Hibernate框架使用OID来区分不同PO对象。

      *例如:内存中有两个PO对象,只要具有相同OID,Hibernate会认为它们是同一个对象。

      *Hibernate不允许缓存同样OID的两个不同对象。


3、自然主键和代理主键

      使用具有业务含义字段作为主键 ----- 自然主键 ,例如:身份证号, 用户名
      随机生成,不具备业务含义的字段 ----- 代理主键 , 例如 流水号 ,UUID 
      *** 现在企业系统,大多使用 代理主键


4、基本数据类型和包装类型

      使用基本类型 ,无法区分 0 和 null , 使用int类型分数,如果学生分数为0 可以没有考试, 也可能考试得了0分 
      使用包装类型,如果不设置数据,数据表存放null ,而不是默认值 0
      * 以后开发中,PO类属性 都使用包装类型


5、hbm文件配置

      配置类到指定数据库中表的映射。

      <class name="lsq.hibernate.domain.Customer" table="customer" catalog="hibernate3day1">
      * 省略 catalog, 表会创建到 jdbcurl 指定数据库中 

      配置类中OID属性到表主键映射:

      <id name="id" column="id" type="int">

            * 配置主键生成策略

            <generator class="identity"></generator>

      配置普通属性的映射 
      <property name="name" column="name" type="java.lang.String"></property>

      * 在配置过程中,省略column 和 type ,如果不写column 生成列名就是属性名, 不写type将按照 类中属性类型自动映射 
      * 配置列其它属性 unique="true" 唯一  not-null="true" 非空  length="20" 长度 


6、主键生成策略

      在<id>元素中 通过 <generator class="生成策略"></generator> 指定数据表主键生成策略

      常用六种主键生成策略:

      1) increment
      increment 标识符生成器由 Hibernate 以递增的方式为代理主键赋值
      原理:select max(id) , insert max(id)+1 
      * 使用 increment 创建数据表 没有主键自增长, 通过hibernate在程序内部完成自增 
      * 好处跨平台 ,缺点 高并发访问时,可以出现主键冲突问题

      主键冲突报错如下:



      2)identity(抢占主键,无线程问题)---Mysql

      identity标识符生成器由底层数据库来负责生成标识符,它要求底层数据库把主键定义为自动增长字段类型。

      原理: 依赖数据库内部自增长,和hibernate无关 
      * 创建数据表 `id` int(11) NOT NULL AUTO_INCREMENT
      * 优点,无需程序处理,数据库自己完成主键增长,缺点 Mysql支持 自增主键, oracle 不支持


      3)sequence--- Oracle

      sequence  标识符生成器利用底层数据库提供的序列来生成标识符
      原理: 依赖数据库序列支持 ,和hibernate程序无关
      * Oracle 支持序列, Mysql 不支持序列
      * 序列原理 
            create sequence customer_seq;
            insert into customer(id) values(customer_seq.nextval); 自动序列+1 


      4)native(本地策略)
      native 标识符生成器依据底层数据库对自动生成标识符的支持能力, 来选择使用 identity, sequence 或 hilo 标识符生成器. 
      * Mysql 自动选择 identity , oracle 自动选择 sequence


      5)uuid

      uuid 的主键生成,采用String 类型主键 
      随机生成32位字符串


      6)assigned

      前五种策略,都是代理主键生成策略
      assigned 是自然主键生成策略 ---- 必须用户在程序中指定 (无法自动生成)


      * 复合主键 (联合主键),一个数据表中 多列共同作为主键 --------- 复合主键是一种特殊 assigned 策略
      <composite-id>
                  <!-- 配置多列 -->
                  <key-property name="firstname"></key-property>
                  <key-property name="secondname"></key-property>
      </composite-id>
      错误:Caused by: org.hibernate.MappingException: composite-id class must implement Serializable: cn.itcast.domain.Person
      复合主键类 必须实现序列化接口


二、Hibernate持久化对象状态

1、持久化对象的三种状态

      *transient瞬时态(临时态、自由态):不存在持久化OID,尚未与Hibernate Session关联对象,被认为处于瞬时状态,失去引用将被JVM回收

      *persist持久态:存在持久化标识OID,与当前session有关联,并且相关联的session没有关闭,并且事务未提交。

      *detached 脱管态(离线态、游离态) : 存在持久化标识OID,但没有与当前session关联,脱管状态改变hibernate不能检测到

      区分三种状态, 判断对象是否有OID,判断对象是否与Session关联(被一级缓存引用 )

package lsq.hibernate.firstcache;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import lsq.hibernate.utils.HibernateUtils;

/**
 * 测试持久化对象操作(一级缓存)
 * @author Daniel Li
 *
 */
public class HibernateTest {
	//区分持久化对象三种状态
	@Test
	public void demo1(){
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Book book = new Book();//瞬时态(没有OID,未与Session关联)
		book.setName("Hibernate入门");
		book.setPrice(23d);
		
		session.save(book);//持久态(具有OID,与Session关联)
		
		//提交事务关闭session
		transaction.commit();
		session.close();
		
		System.out.println(book.getId());//脱管态(具有OID,与Session断开连接)
	}
}


2、 持久化对象状态转换 


      1) 瞬时态对象  通过new获得 
            瞬时--持久  save 、 saveOrUpdate (都是Session)  
            瞬时--脱管  book.setId(1); 为瞬时对象设置OID

      2) 持久态对象  get/load 、Query查询获得
            持久--瞬时  delete  (被删除持久化对象 不建议再次使用 )
            持久--脱管  evict(清除一级缓存中某一个对象)、close(关闭Session,清除一级缓存)、clear(清除一级缓存所有对象 ) 

      3) 脱管态对象 无法直接获得
            脱管--瞬时 book.setId(null); 删除对象OID 
            脱管--持久 update、saveOrUpdate、 lock(过时)


3、 Session中一级缓存 
      Hibernate框架共有两级缓存, 一级缓存(Session级别缓存) 、 二级缓存 (SessionFactory级别缓存)

      Hibernate Session接口 实现类 SessionImpl 类 
      * private transient ActionQueue actionQueue;  ---- 行动队列
      * private transient StatefulPersistenceContext persistenceContext; ---- 持久化上下文

      持久化对象保存Session 一级缓存中 (一级缓存 引用 持久化对象 地址 ), 只要Session不关闭,一级缓存存在,缓存中对象 也不会被回收

      Session会在一些特定时间点,将缓存中数据flush 到数据库

      *  Transaction 的 commit()
      *  应用程序执行一些查询操作时
      *  调用 Session 的 flush() 方法

案例一: 证明一级缓存是存在的

package lsq.hibernate.firstcache;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import lsq.hibernate.utils.HibernateUtils;

/**
 * 测试持久化对象操作(一级缓存)
 * @author Daniel Li
 *
 */
public class HibernateTest {
	//案例一: 证明一级缓存是存在的
	@Test
	public void demo2(){
		Session session = HibernateUtils.openSession();
		Transaction transaction = session.beginTransaction();
		
		Book book = (Book) session.get(Book.class, 1);
		System.out.println(book);
		
		Book book2 = (Book) session.get(Book.class, 1);
		System.out.println(book2);
		
		transaction.commit();
		session.close();
	}
}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">      Book book = (Book) session.get(Book.class, 1); // 第一次查询,缓存中没有</span>
      System.out.println(book);

      Book book2 = (Book) session.get(Book.class, 1);// 因为第一次查询,对象已经被放入1级缓存,不会查询数据
      System.out.println(book2);
      *  生成一条SQL语句,返回同一个对象 ,第一次查询生成SQL,查询对象,将对象放入一级缓存,第二次查询,直接从一级缓存获得

案例二 : 测试Hibernate快照 (深入理解一级缓存 内存结构原理 )

      hibernate 向一级缓存放入数据时,同时保存快照数据(数据库备份),当修改一级缓存数据,在flush操作时,对比缓存和快照,

      如果不一致,自动更新 (将缓存的内容 同步到数据库, 更新快照)

      *  快照区使用,在Session 保存一份 与数据库 相同的数据 ,在session的 flush时, 通过快照区 比较得知 一级缓存数据是否改变,如果改变执行对应 操作(update)
      *  Hibernate中 持久态 对象具有自动更新数据库能力 (持久态对象 才保存在 Session中,才有快照 )



4、一级缓存常见操作

      1) flush : 修改一级缓存数据 针对内存操作, 需要在session执行flush操作时,将缓存变化同步到数据库 

            * 只有在 缓存数据 与 快照区 不同时,生成update 语句
      2) clear : 清除所有对象 一级缓存 
      3) evict : 清除一级缓存 指定对象

      4) refresh :重新查询数据库,更新快照和一级缓存


5、 一级缓存 刷出时间点 设置 (FlushMode)
      ALWAYS 在每次查询时,session都会flush  (不要求 )
      AUTO : 在有些查询时,session会flush  (默认)  ---------- 查询、commit 、session.flush 
      COMMIT : 在事务提交时,session会flush   ------- commit 、session.flush
      MANUAL :只有手动调用 session.flush 才会刷出  ----  session.flush

      刷出条件(时间点严格程度 )
            MANUAL > COMMIT> AUTO> ALWAYS


6、session持久化对象 操作方法

      1) save 将数据保存到数据库 , 将瞬时对象 转换 持久对象 
            持久化对象,不允许随便修改 OID 
      2) update 更新数据 ,主要用于脱管对象的更新 (持久对象,可以根据快照自动更新 ), 将脱管对象 转换 持久对象 
            问题一: 调用update,默认直接生成update语句,如果数据没有改变,不希望生成update 
                  在hbm文件 <class>元素 添加 select-before-update 
            问题二: 当update,脱管对象变为持久对象, 一级缓存不允许出现相同OID 两个持久对象 
                  org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [lsq.hibernate.firstcache.Book#1]
            问题三 : 脱管对象 OID 在数据表中 不存在,update时,发生异常
                  org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect):                               [lsq.hibernate.firstcache.Book#20]
      3) saveOrUpdate , 如果参数是一个瞬时对象 执行save, 如果参数 是一个脱管对象 执行update, 如果参数是持久对象 直接返回
            判断对象是瞬时对象 : OID为null , 在hbm文件中为 <id>元素指定 unsaved-value属性,如果PO对象OID为 unsaved-value 也是瞬时对象 
            <id name="id" unsaved-value="-1">  如果对象 OID为-1 也是瞬时对象 
      4)  get/load
            如果查询OID 不存在, get方法 返回 null , load 方法返回代理对象 (代理对象初始化时 抛出 ObjectNotFoundException )
      5)  delete 方法
            方法既可以删除一个脱管对象, 也可以删除一个持久化对象
            如果删除脱管,先将脱管对象 与 Session 关联,然后再删除 
            执行delete,先删除一级缓存数据,在session.flush 操作时,删除数据表中数据



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值