Hibernate
- hibernate框架
- hibernate实体规则
-
- 实体内创建的注意事项
- 主键类型
- 主键生成策略generator
- hibernate中的对象状态
-
- hibernate进阶
- hibernate中的事务
- hibernate中的批量查询
- hibernate多表操作
- hibernate的多表查询
hibernate框架
1.什么是框架
提高我们的开发效率.可以理解成是一个半成品项目.
2.hibernate框架
dao层框架
操作数据库.
以面向对象的方式操作数据库.
orm 思想. 对象关系映射. 通过映射文件配置对象与数据库中表的关系
O 对象
R 关系型数据库
M 映射文件
3.hibernate框架搭建
-
导包
required+驱动包
- 准备实体类 以及 orm元数据
- 创建主配置文件
- 书写代码测试
4.配置文件详解
- orm元数据(xxx.hbm.xml)
<hibernate-mapping package="">
<class name table>
<id name >
<generator class="">
</id>
<property name="" />
- hibernate.cfg.xml
-
必选配置
4+1 方言
-
<!-- 数据库驱动 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<!-- 数据库url -->
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_32</property>
<!-- 数据库连接用户名 -->
<property name="hibernate.connection.username">root</property>
<!-- 数据库连接密码 -->
<property name="hibernate.connection.password">1234</property>
<!-- 数据库方言
不同的数据库中,sql语法略有区别. 指定方言可以让hibernate框架在生成sql语句时.
针对数据库的方言生成.
sql99标准: DDL 定义语言 库表的增删改查
DCL 控制语言 事务 权限
DML 操纵语言 增删改查
注意: MYSQL在选择方言时,请选择最短的方言.
-->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
-
可选配置
显示sql
格式化sql
自动生成表
<!-- 将hibernate生成的sql语句打印到控制台 -->
<property name="hibernate.show_sql">true</property>
<!-- 将hibernate生成的sql语句格式化(语法缩进) -->
<property name="hibernate.format_sql">true</property>
<!--
## auto schema export 自动导出表结构. 自动建表
#hibernate.hbm2ddl.auto create
自动建表.每次框架运行都会创建新的表.以前表将会被覆盖,表数据会丢失.(开发环境中测试使用)
#hibernate.hbm2ddl.auto create-drop
自动建表.每次框架运行结束都会将所有表删除.(开发环境中测试使用)
#hibernate.hbm2ddl.auto update(推荐使用)
自动生成表.如果已经存在不会再生成.如果表有变动.自动更新表(不会删除任何数据).
#hibernate.hbm2ddl.auto validate
校验.不自动生成表.每次启动会校验数据库中表是否正确.校验失败.
-->
<property name="hibernate.hbm2ddl.auto">update</property>
- 指定隔离级别和orm元数据引入
<!-- 指定hibernate操作数据库时的隔离级别
#hibernate.connection.isolation 1|2|4|8
0001 1 读未提交
0010 2 读已提交
0100 4 可重复读
1000 8 串行化
-->
<property name="hibernate.connection.isolation">4</property>
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
<!-- 引入orm元数据
路径书写: 填写src下的路径
-->
<mapping resource="com/dustdawn/entity/Customer.hbm.xml" />
5.api详解
Configuration 读取配置
sessionFactory 创建session
Session 获得事务操作对象,以及数据增删改查
Transaction 控制事务
hibernate实体规则
实体内创建的注意事项
- 持久化类提供无参数构造
- 成员变量私有,提供共有get/set方法访问,需提供属性
- 持久化类中的属性,因尽量使用包装类型(不会错,且值能为null)
- 持久化类需要提供oid,与数据库中的主键列对应
- 不要用final修饰class(非接口代理,hibernate使用cglib代理生成代理对象,代理对象是继承被代理对象,如果被final修饰将无法生成代理)
主键类型
-
自然主键
表的业务列中,有某业务列符合,必须有,并且不重复的特征时,该列可以作为主键使用
-
代理主键
表的业务列中,没有某业务列符合,必须有,并且不重复的特征时,创建一个没有业务意义的列作为主键
主键生成策略generator
就是每条记录录入时,主键的生成规则(7个)
xxx.hbm.xml
< id name=“cust_id” >
< generator class=“native”>< /generator>
< /id>
- 代理主键
- identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键.
- sequence: Oracle中的主键生成策略.
- increment(了解): 主键自增.由hibernate来维护.每次插入前会先查询表中id最大值.+1作为新主键值.
- hilo(了解): 高低位算法.主键自增.由hibernate来维护.开发时不使用.
- native:hilo+sequence+identity 自动三选一策略.
- uuid: 产生随机字符串作为主键. 主键类型必须为string 类型.
- identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键.
- 自然主键
- assigned:自然主键生成策略. hibernate不会管理主键值.由开发人员自己录入.
hibernate中的对象状态
Session接口是hibernate应用程序提供的操作数据库的接口,提供了基本的保存,更新,删除,和加载java对象的方法。Session具有一个缓冲,位于缓存中的对象称为持久化对象。
- 瞬时状态
- 没有id,没有与session关联(没有在session缓存中):customer.setCust_name("");
- 持久化状态
- 有id,与session关联(在session缓存中):session.save(customer);
- 特点:持久化状态对象的任何变化自动同步到数据库中
- 游离/托管状态
-
有id,没有与session关联(没有在session缓存中):session.close();
Customer c1 = new Customer();
c1.setCust_id(1l);
设置id不存在对应瞬时,存在则游离
-
session的save()方法不能理解成保存,应该理解成将瞬时状态转换成持久状态的方法。
主键自增,执行save方法,为了将对象转换成持久化状态,必须生成id值,所以需要执行insert语句生成
saveOrUpdate方法将任意对象将对象转换成持久化对象
==持久化状态的对象,会在事务提交是,自动同步到数据库中
hibernate使用原则:将我们希望同步到数据库的数据,对应的对象转换成持久化状态
hibernate进阶
一级缓存
- 缓存:提高效率,hibernate中的一级缓存是为了提高操作数据库的效率
-
一级缓存:为了提高效率,session对象中有一个可以存放对象的集合
查询时:第一次查询时,会将对象放入缓存,再次查询时,会返回缓存中的,不再查询数据库
修改时:会使用快照对比修改前和修改后对象的属性区别,只执行一次修改
- 提高效率手段1:提高查询效率
- 提高效率手段2:减少不必要的的修改语句发送(快照)
持久化状态对象其实就是放入session缓存中的对象
hibernate中的事务
Service层
HibernateUtils.getCurrentSession().beginTransaction();
操作事务
HibernateUtils.getCurrentSession().getTransaction().commit();
HibernateUtils.getCurrentSession().getTransaction().rollback();
Dao层
Session session = HibernateUtils.getCurrentSession();
session.get();
- 事务特性
-
A — 原子性
事务可以看做数据库操作的容器,对事务所包裹的操作被认为像物理上的原子,是最小、不可分割的单位,要么全成功,要么全失败
-
C — 一致性
事务在提交前后数据总量不应该发生变化(赖以保证的基础是原子性)
-
I — 隔离性
多个事务在并发产生的时候,可能会产生一些负面问题(脏读、不可重复读、幻读)
提供隔离级别解决
-
D — 持久性
保证事务提交之后,涉及到操作的数据必须被写入持久化介质(如硬盘)中
-
- 事务并发问题
-
脏读
某事务读到了另一个事务正在操作但未提交的数据
-
不可重复读
一次事务中两次连续的读取到的数据内容不一致(过程中其他事务修改数据并提交了)
-
幻/虚读
一次事务中两次连续的读取到的数据数量不一致(过程中其他事务增加数据并提交了)
-
解决方法:
-
事务的隔离级别
1)read uncommitted : 读取尚未提交的数据 :哪个问题都不能解决
2)read committed:读取已经提交的数据 :可以解决脏读 ---- oracle默认的
3)repeatable read:重读读取:可以解决脏读 和 不可重复读 —mysql默认的
4)serializable:串行化:可以解决 脏读 不可重复读 和 虚读—相当于锁表操作数据只允许串行,不允许并发
知识点1. 在Hibernate中配置操作数据库中的隔离级别
<!-- 指定hibernate操作数据库时的隔离级别
#hibernate.connection.isolation 1|2|4|8
0001 1 读未提交
0010 2 读已提交
0100 4 可重复读
1000 8 串行化
-->
<property name="hibernate.connection.isolation">4</property>
知识点2. 在项目中如何管理事务
- 在业务开始之前打开事务
- 业务执行之后提交事务
- 执行过程中出现异常回滚事务
-
在dao层操作数据库需要用到session对象,在service层控制事务也是使用session对象完成,要保证dao层和service层使用同一个session
解决方法
原理:将session对象绑定到ThreadLocal线程
调用sessionfactory.getCurrentSession()方法即可获得与当前线程绑定的session对象
方法:hibernate.cfg.xml中配置
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>
注意1:调用getCurrentSession方法必须配合主配置中的上配置
注意2:通过getCurrentSession方法获得的session对象,当事务提交时,session会自动关闭,不要手动调用close关闭
hibernate中的批量查询
-
HQL查询(多表查询,但不复杂时使用)
Hibernate Query Language(Hibernate独家查询语言,属于面向对象的查询语言)
HQL语句中,不可能出现任何数据库相关的信息的
- 基本查询
- 条件查询
- 分页查询
-
Criteria查询(单表查询)
Hibernate自创的无语句面向对象查询
- 基本查询
- 条件查询
- 分页查询
- 查询总记录数
- 原生SQL查询(复杂的业务查询)
- 基本查询
- 条件查询
- 分页查询
hibernate多表操作
- 多对一/一对多
-
关系表达
表中的表达:外键指向一的一方主键
在对象中的表达:使用集合表达,一的一方持有多个多的一方
使用对象引用一的一方,表达多的一方属于哪个一的一方
- 操作
<set name="linkMens" cascade="save-update" inverse="true"> <!--外键列名 --> <key column="lkm_cust_id"></key> <one-to-many class="LinkMan" /> </set> <many-to-one name="customer" column="lkm_cust_id" class="Customer" > </many-to-one>
- 级联操作
-
关系维护
在保存时,两方都会维护外键关系,关系维护两次造成冗余,多余的维护关系语句显然是客户这一端在维护关系(联系人一端通过外键已经维护关系)
多的一方(LinkMan)不能放弃维护关系,因为外键字段就在多的一方
原则:无论怎么放弃,总有一方必须要维护关系
一对多关系中:只能一的一方放弃维护,多的一方不能放弃维护
( 如果客户放弃维护与联系人的关系. 维护关系的代码可以省略
如果执行删除主表操作时,主表选择维护关系,可以解除与从表的外键约束,从表外键置空,记录不变,级联操作为delete时,会将两表一同删除)
-
- 多对多
-
关系表达
使用中间表,至少两列,都是外键列,分别引用两张表的主键
两方都使用集合来表达拥有多个对方
-
操作
orm元数据xxx.hbm.xml
将来在开发中,如果遇到多对多关系.一定要选择一方放弃维护关系.
一般谁来放弃要看业务方向. 例如录入员工时,需要为员工指定所属角色.
那么业务方向就是由员工维护角色. 角色不需要维护与员工关系.角色放弃维护
- 级联操作
-
hibernate的多表查询
- 查询总结
-
oid查询—get方法
session.get(实体类.class,id)
- 对象属性导航查询
- HQL
- 基础语法
- 排序
- 条件
- 分页
- 聚合
- 投影
-
多表查询
内连接(迫切fetch封装成一个对象) inner join(fetch)
外连接
- 左外(迫切) left join(fetch)
- 右外(迫切)right join(fetch)
- Criteria
- 基本语法
- 离线查询
- 原生SQL
-
- 查询优化
-
类级别查询
session.get()方法:没有任何策略,调用即立即查询数据库加载数据
session.load()方法(true:默认延迟加载):是在执行时,不发送任何sql语句,返回一个对象,使用该对象时,才执行查询(orm元数据文件中class元素上配置lazy属性来控制:
< class name=“Customer” table=“cst_customer” lazy=“true”>)
结论:为了提高效率.建议使用延迟加载(懒加载),优化查询延迟加载:仅仅获得没有使用,不会查询,在使用时才进行查询 lazy(默认值):true,查询类时,会返回代理对象(带$符号)不去查询,而在使用属性时,调用代理对象增强的方法,根据关联的session查询数据库加载数据
-
关联级别查询
set元素中配置
对象属性导航:Customer的属性getLinkMen导航另一个对象
- 集合策略(一的一方Customer,取LinkMan)
-
延迟加载&抓取策略
extra:如果只获得集合的size,只查询集合的size(count语句),不会打印数据
fetch为join时,类级别加载lazy属性失效
-
- 关联属性策略(多的一方LInkMan,加载Customer) 结论:为了提高效率.fetch的选择上应选择select, lazy的取值应选择 true. 全部使用默认值.
-
no-session问题解决
采用上结论懒加载时,返回页面是为代理对象,到达页面时service层执行完毕,session关闭,会出现懒加载初始化缺少session对象的情况
- 集合策略(一的一方Customer,取LinkMan)
-
批量抓取
遍历每个客户都要用sql语句查询联系人
-