Hibernate
Contents
目录 start
目录 end|2020-05-17 16:13|
Hibernate
Hibernate基础配置
JDBC和Hibernate比较
- JDBC
- 使用其简洁精悍,最快,但是使用时反序列化数据麻烦,手动管理容易出内存泄漏
- Hibernate
- 单表操作是很便捷的,但是涉及到多表复杂操作时比较复杂,不便调优
配置流程
如果后续需要添加表的话,就这个顺序
- 先有数据库和表,建立cfg.xml文件配置好数据库的基本参数
- 使用工具建立POJO持久类
- 导入Hibernate所必需JAR包,最好使用Myeclipse的配置,自己导包总有一堆错误
- 使用MyEclipse自动创建hbm.xml文件,还有各种文件。配置好hbm文件里关于表间关系的映射,或者在Myeclipse配置时手动选择
- 配置好DAO类中事务开启和关闭,以及各种所必需的配置,若表没有设立主键,那么POJO类需要继承自动生成的抽象类(含有主键)
- 调用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;
|
|
非普通类型
- Set集合:
|
|
- List集合:
|
|
- 查询列 属性:
<property name="" formula="(select sum() from 选修表 as u where u.id=id)"></property>
Hibernate实体关联配置
一对多的配置
- 注意:一定要两个都有oid的情况才能配置一对多的映射,不能是依赖于另一个主键类
- 一方:
|
|
- 多方:
<many-to-one name="" class="一方的类" column="外键,key要一致" />
- 双向的关联,会有update的SQL语句的执行来维护关系,影响效率
- 多方维护:一方中set标签加inverse=“true"一方就不会维护,代码一定要多方执行set**(*)
- 一方维护:一方代码一定要执行**.add*()
注意
- 1.在一的一方,修改xml文件,添加一个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,不然会有重复维护的错误发生
学生方配置
|
|
课程方配置
|
|
一对一的配置
- 单向
只要配置单向的配置文件添加:
<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>
- 一方 甲:
使用多对一的技巧
添加记录
- 当需要添加一个多方时,一看成课程,多看成成绩。当然的首先得有相关课程,再添加成绩记录。
- 那就先实例化一个课程对象,配置好信息
- 实例化多个成绩实例,再 课程对象.get**Set().add(成绩对象); 将成绩对象添加到集合中,
- session.save(课程对象);
注意:既然实现了这样的操作,那就说明了在实例化成绩的时候,不需要指定课程的值,那就需要添加一个构造器
删除记录
- 如果删除一方,那就会将一删除,如果没有配置级联,就会将多方的外键置空,不会删除多方表
- 如何通过一方修改多方的一条, 把一方的set中的要修改的一条,(查找之前需要对象 = session.load(对象.class,主键名)将多方的数据加载进来)
- 注意多方不能有空列必须指定一个默认值(是和构造器有关么?)
- 再查找出来,修改再update,新增也是如此增加多的一方的时候,就是在一方的set中新增一条记录,多方的操作都体现在了一方那里
继承关系的配置
两种方式,一般采用前者
|
|
Hibernate对象的状态
主要是对象内存和Session中的状态区别,而不是Session和数据库
临时态
:刚实例化对象。对象在数据库中不存在,Session中也不存在游离态
:刚实例化的对象,但是该对象手动指定了OID并且OID在数据库中已经存在,并且是没有绑定Session的(特殊的临时态)- 保存两个有关联关系的对象,update时,如果配置文件中配置了级联,就会一起保存,一般建议在一方级联
持久态
:该对象在数据库中存在,该对象绑定在Session(一级缓存)中删除态
:session.delete(对象),删除后对象从数据库和Session中都移除了,但是OID还在内存中。- 游离态delete后就成了删除态
- 持久态delete后就成了删除态
- 分析: 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
原因:
- 可能真的没写get方法,或者get方法不合规范 setget方法中不允许两个连续大写字母
- *.hmb.xml文件中的属性名和pojo持久类中属性名不一致(一定不能在表名中添加下划线)
Author Kuangcp
LastMod 2018-11-21