目录 start

  1. Hibernate
  2. Hibernate基础配置
    1. JDBC和Hibernate比较
      1. 配置流程
    2. Hibernate必需JAR
    3. 编写数据库表对应框架持久层的对象
    4. hibernate.cfg.xml文件
    5. SessionFactory和Session比较
    6. OID的作用:
    7. id生成策略
    8. 非普通类型
  3. Hibernate实体关联配置
    1. 一对多的配置
      1. 注意
    2. 多对多的配置
      1. 学生方配置
      2. 课程方配置
    3. 一对一的配置
    4. 使用多对一的技巧
      1. 添加记录
      2. 删除记录
    5. 继承关系的配置
  4. Hibernate对象的状态
    1. Session的方法
    2. 特别注意
  5. 常见Hibernate异常
    1. could not find a getter

目录 end|2020-05-17 16:13|


Hibernate

Hibernate基础配置

JDBC和Hibernate比较

  • JDBC
    • 使用其简洁精悍,最快,但是使用时反序列化数据麻烦,手动管理容易出内存泄漏
  • Hibernate
    • 单表操作是很便捷的,但是涉及到多表复杂操作时比较复杂,不便调优

配置流程

如果后续需要添加表的话,就这个顺序

  1. 先有数据库和表,建立cfg.xml文件配置好数据库的基本参数
  2. 使用工具建立POJO持久类
  3. 导入Hibernate所必需JAR包,最好使用Myeclipse的配置,自己导包总有一堆错误
  4. 使用MyEclipse自动创建hbm.xml文件,还有各种文件。配置好hbm文件里关于表间关系的映射,或者在Myeclipse配置时手动选择
  5. 配置好DAO类中事务开启和关闭,以及各种所必需的配置,若表没有设立主键,那么POJO类需要继承自动生成的抽象类(含有主键)
  6. 调用DAO或者自己的Utils类,通过Hibernate来操作数据库

Hibernate必需JAR

Hibernate 3.6

  • required目录下所有JAR都要导入
  • jpa的JAR包(做注解用)
  • 日志包:
    • slf4j-api-* .jar 该包是一个日志接口,需要一个JAR包的实现:
    • slf4j-log4j12.jar 该包是转换的JAR包
    • log4j-1.2.11.jar 实现SLF4J接口的JAR包
  • 数据库驱动包 mysql-connector-java-5.1.7-bin.jar
  • 在src同级目录下新建一个lib目录,把JAR包复制进去,然后右击将jar文件 Add to build path 加入到类搜索路径里

编写数据库表对应框架持久层的对象

  • 使用自己的工具类创建到对应的包下,或者用相关工具生成,类型要自己多加注意

hibernate.cfg.xml文件

作为默认的主配置文件

  • 数据库连接属性 驱动,url,用户名,密码
  • 数据库方言
  • 辅助配置
  • POJO类配置文件的映射
  • etc/hibernate.properties里可以看到更多配置,数据库连接池,SQL优化等

SessionFactory和Session比较

  • SessionFactory

重量级容器:消耗大量资源,不能有太多实例,二级缓存 通常将该工厂类是单例模式,一个工厂类实例表示一个数据库
所以Hibernate一般是不能跨数据库来做事务操作。但是EJB和JPA可以实现 如下配置选项:

  • hibernate.hbm2ddl.auto create-drop 在一个数据库中创建,然后使用完关闭实例时就删除所有建立的表
  • hibernate.hbm2ddl.auto create 清除数据库的表及数据,重新创建表
  • hibernate.hbm2ddl.auto update 更改配置文件,能够在数据库进行操作(更新,建立)
  • hibernate.hbm2ddl.auto validate
  • session

轻量级的容器,一级缓存 是非线程安全的对象

OID的作用:

在Hibernate中唯一标识对象的属性,每个实体都是必须要有OID的

id生成策略

  • assigned:要求用户去手动指定对象的OID;该对象ID的类型可以是任意的
  • identity:MySQL的自动生成
  • native:数据类型是数值型,id的生成策略为数据库底层自增长(数据库自己去决定使用哪种方式,MySQL用identity,Oracle用序列等)
  • sequence:Oracle数据库推荐,数值型(Long)
  • seqhilo oracle :推荐使用的策略,使用序列来搭配高低机制
  • uuid.hex :32位字符
  • uuid:
  • hilo:类型为数值型(long) [实际开发中推荐使用]

id = hi+lo (高位和低位进行组合) sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+lo;

1
2
3
4
5
6
7
8
    <generator class="hilo" >
    <param name="table">stu_hilo</param>
    <!-- 放高值的表名 最好是一个对象对应于一个高低值的表避免了并发-->
    <param name="cloumn">next_hi</param>
    <!-- 高的值放在表的哪个字段 -->
    <param name="max_lo">100</ param>
    <!-- 每个轮回值的上限是多少 虚拟机启动频繁就设小一些,避免编码的浪费-->
    </generator>

非普通类型

  • Set集合:
1
2
3
4
    <set name="Nos" table="表">
        <key column="外码"></key><!-- 外码 是必须的 -->
        <element column="号码" type="string"/>
    </set>
  • List集合:
1
2
3
4
5
    <list>
        <key></key>
        <index></index>
        <element></element>
    </list>
  • 查询列 属性: <property name="" formula="(select sum() from 选修表 as u where u.id=id)"></property>

Hibernate实体关联配置

一对多的配置

  • 注意:一定要两个都有oid的情况才能配置一对多的映射,不能是依赖于另一个主键类
  • 一方:
1
2
3
4
    <set name="" [cascade=""]> 
        <key column="这是外键"></key>
        <one-to-many class="多方的类"></one-to-many>
    </set>
  • 多方: <many-to-one name="" class="一方的类" column="外键,key要一致" />
  • 双向的关联,会有update的SQL语句的执行来维护关系,影响效率
  • 多方维护:一方中set标签加inverse=“true"一方就不会维护,代码一定要多方执行set**(*)
  • 一方维护:一方代码一定要执行**.add*()

注意

  • 1.在一的一方,修改xml文件,添加一个set 属性,表示 多方 的一个集合
1
2
3
4
    <set name="类中属性名(集合)" inverse="true">
        <key>< column name="数据库列名"/></key>
        <one-to-many class="多方类路径"/>
    </set>
  • 2.在一的一方,修改POJO持久类文件,添加一个hashset,用来存储多方,添加setget方法,名字就是配置文件里添加的那个名字 注意修改构造器

  • 3.在多的一方,修改xml文件,置换掉那个外键,换成many-to-one标签,里面写上外键的列 <many-to-one name="类中属性名(对象)" class="一方的类路径" column="数据库中列名"></many-to-one>

  • 4.在多的一方,修改POJO持久类文件,添加一个一方的对象添加setget方法,名字就是配置文件里添加的那个名字 注意修改构造器

    • 一方的set集合中有inverse属性,多方是没有的,Hibernate中inverse是和外键对应的,一方配置了inverse是false,一方就不会维护关系(外键),一般是给多方维护,因为效率高
    • cascade是对象和对象之间的操作,和外键没有关系
    • 处于持久化状态的对象在Session中,客户端不需要做Session的save/update 操作,Hibernate会自动的去检查处于持久化的对象的状态的属性是否发生改变,改变了就发送update语句。
      • 如果该对象是一方,在一的一方映射文件中有cascade=all时,Hibernate内部还会检查该持久化对象关联的集合,对此集合进行update操作,但是该操作和外键没有关系,只有当通过多方建立关系后,才能使外键有值。

多对多的配置

  • 关系在第三方表中,和两张表本身没有关系
  • 多对多维护关系,谁都能维护关系(效率是一样的)维护一般是在页面上进行的
  • table 是多对多的中间表(存放了一个关系)
  • key中的column一般是填当前配置文件中的id
  • 多对多的配置是需要两个外键的
  • 如果使用了反转并使用了级联,就只会保存实体,但是关系是没有维护的(就是不会插入到第三方表),和一对多一样的(一对多是外键列没有值)。
  • !!如果双方都级联了,必须要有一方inverse,不然会有重复维护的错误发生

学生方配置

1
2
3
4
    <set name="students" table="student_course">
        <key column="cid"></key>
        <many-to-many class="Student" column="stu_id"></many-to-many>
    </set>

课程方配置

1
2
3
4
    <set name="courses" table="student_course">
        <key column="stu_id"></key>
        <many-to-many class="Course" column="cid"></many-to-many>
    </set>

一对一的配置

  • 单向 只要配置单向的配置文件添加: <many-to-one name=""class="映射的类" column="数据库字段" unique="true"></many-to-one>
  • 双向
    • 一方 甲: <many-to-one name="" class="乙方类"column="数据库字段" unique="true"></many-to-one>
    • 一方 乙: <one-to-one name="" class="甲方类" property-ref="甲方配置的标签的name"></one-to-one>

使用多对一的技巧

添加记录

  1. 当需要添加一个多方时,一看成课程,多看成成绩。当然的首先得有相关课程,再添加成绩记录。
  2. 那就先实例化一个课程对象,配置好信息
  3. 实例化多个成绩实例,再 课程对象.get**Set().add(成绩对象); 将成绩对象添加到集合中,
  4. session.save(课程对象);

注意:既然实现了这样的操作,那就说明了在实例化成绩的时候,不需要指定课程的值,那就需要添加一个构造器

删除记录

  1. 如果删除一方,那就会将一删除,如果没有配置级联,就会将多方的外键置空,不会删除多方表
  2. 如何通过一方修改多方的一条, 把一方的set中的要修改的一条,(查找之前需要对象 = session.load(对象.class,主键名)将多方的数据加载进来)
    • 注意多方不能有空列必须指定一个默认值(是和构造器有关么?)
    • 再查找出来,修改再update,新增也是如此增加多的一方的时候,就是在一方的set中新增一条记录,多方的操作都体现在了一方那里

继承关系的配置

两种方式,一般采用前者

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    <!-- 将子类插入到父类的配置文件 需要使用key来关联的-->
        <joined-subclass name="cn.hibernate.extend.Student" table="extend_student">
            <key column="id"></key>
            <property name="sru_id" type="long"></property>
        </joined-subclass>
    <!--
        union是相当于将父类的所有属性复制到子类里,是共享父类的OID,
        所以父类的OID是不能和子类的OID重复的
        不然 查询的时候就会报错,
        所以就需要改父类的主键生成策略是高低值(或者是手动set),可以手动配置高低值的表的生成
    -->
        <union-subclass name="cn.hibernate.extend.Student" table="union_student">
            <property name="sru_id" type="long"></property>
        </union-subclass>

Hibernate对象的状态

主要是对象内存和Session中的状态区别,而不是Session和数据库

  • 临时态:刚实例化对象。对象在数据库中不存在,Session中也不存在
  • 游离态:刚实例化的对象,但是该对象手动指定了OID并且OID在数据库中已经存在,并且是没有绑定Session的(特殊的临时态)
    • 保存两个有关联关系的对象,update时,如果配置文件中配置了级联,就会一起保存,一般建议在一方级联
  • 持久态:该对象在数据库中存在,该对象绑定在Session(一级缓存)中
  • 删除态:session.delete(对象),删除后对象从数据库和Session中都移除了,但是OID还在内存中。
    • 游离态delete后就成了删除态
    • 持久态delete后就成了删除态

参考: merge attachDirty attachClean用法

  • 分析: JDK源码 DefaultMergeEventListener中的onMerge方法

Session的方法

  • save
  • update
  • delete
  • saveOrUpdate 由入参的OID来自动选择是要save还是update
  • merge 形参:临时态的对象。形参和Session没有任何关系,返回对象Object2(持久化对象),所以在Session关闭的时候Object2的更改会同步到数据库中
  • get 将数据库中指定对象获取为持久态,查不到就是返回null
  • load 懒加载。使用代理对象,延迟加载。用到了别的属性就去查数据库。查不到就抛异常
  • flush 刷新Session
  • evict 定点清除 将指定的对象从Session中移除,变成游离态
  • clear 全部清除
  • close

特别注意

  • 一个对象(内存)不能存在于多个Session中,一个存,一个改的情况是会错误的

  • 但是数据库中同一条记录可以实例化为多个对象(内存),那么这些对象(内存)放在不同的Session中是可以的

  • get:

    • 只延迟加载有外键关联的那部分属性,没有使用就不会查询,只有用到了才会查询
    • 多方:立即加载就要在配置文件中将对应的属性中添加 fetch=“join”
    • 一方:配置文件中set标签 加上lazy=“false”(两条SQL),再添加 fetch=“join"后就只有一条SQL语句,但是这个一方是不能做分页查询的
  • load:

    • 在你确定OID是一定有的时候使用load提高效率,但是实际开发过程中用的少,因为实际上没有这么确定。
  • 懒加载如果Session关闭了或者是对象游离态。就会有懒加载初始化的异常


常见Hibernate异常

could not find a getter

原因:

  1. 可能真的没写get方法,或者get方法不合规范 setget方法中不允许两个连续大写字母
  2. *.hmb.xml文件中的属性名和pojo持久类中属性名不一致(一定不能在表名中添加下划线)