【石家庄校区】Hibernate框架知识详解
Hibernate介绍
Hibernate:开源的对象关系映射(ORM)框架,对jdbc进行轻量级对象封装,将POJO与数据库表建立映射关系。在dao层使用
ORM(Object Relation Mapping,O/RM,O/R mapping)对象关系映射:使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作
环境搭建
下载:hibernate-5.0.7.Final.zip
下载包目录结构
documentation:存放hibernate相关文件与API
lib:运行jar包,其中子目录required下jar包必须导入
project:存放hibernate相关的源代码与资源
导入hibernate必须jar包(子目录required下jar包)
导入数据库驱动jar包
导入日志相关jar包:log4j-xxx.jar,slf4j-api-xxx.jar,slf4j-log4j12-xxx.jar
将hibernate/project/etc/log4j.properties文件导入到工程src下
基本开发
//使用hibernate的api完成数据操作
Configuration config=new Configuration().configure();
SessionFactory sessionFactory=config.buildSessionFactory();
Session session=sessionFactory.openSession(); //相当于得到一个Connection
//开启事务
session.beginTransaction();
//操作...
//事务提交
session.getTransaction().commit();
session.close();
sessionFactory.close();
hibernate执行原理(重点)
1.通过new Configuration().configure();读取解析hibernate.cfg.xml配置文件
2.通过hibernate.cfg.xml中<mapping resource="">读取解析映射信息
3.config.buildSessionFactory();得到sessionFactory
4.sessionFactory.openSession();得到session
5.session.beginTransaction();开启事务。
6.persistent operate;增删改查操作
7.session.getTransaction().commit();提交事务
8.关闭session;
9.关闭sessionFactory;
Hibernate配置文件
1.映射配置文件
建立表与类的映射关系
类名.hbm.xml
位置:与实体类在同一个包下创建
约束:在核心包hibernate-core-5.0.7.Fianl.jar/org.hibernate/hibernate-mapping-3.0.dtd
<hibernate-mapping>
package="cn.itheima.domain" //统一声明包名,在<class>中就不需写类全名,缺点:实体类都在统一包中
<class name="实体全类名"> //如果统一声明包名,则可直接写类名
table="表名" //不写表的名称与类名一致
catalog="数据库名" //不写使用核心配置文件中url路径中的库名称
<id name="类属性名"> //描述主键标签,与表中主键映射
column="表主键名" //不写与类属性名一致
length="字段长度"
type="" //指定字段类型。对于SQL数据类型,需写子标签<column ... sql-type="varchar(50)" ></column>
<generator class="native"></generator> //主键生成策略,native:自动
<property name="类属性名"> //描述类属性与表非主键字段的映射关系,标签属性同id
</class>
</hibernate-mapping>
关于hibernate映射文件中类型问题
对于type属性取值,有三种(参考图片)
1.java数据类型
2.hibernate数据类型(默认)
3.SQL数据类型
2.框架核心配置文件
两种方式:hibernate.properties和hibernate.cfg.xml(常用)
hibernate.cfg.xml
位置:src下创建
约束:在核心包hibernate-core-5.0.7.Fianl.jar/org.hibernate/hibernate-configuration-3.0.dtd
配置参考:hibernate\project\etc\hibernate.properties文件
<hibernate-configuration>
<session-factory>
//属性配置
<property name="key">value</property>
//配置关于数据库连接的四个项,如:MySQL
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql:///test
hibernate.connection.username=root
hibernate.connection.password=123
//配置此项,可以将数据库发送的SQL语句显示到控制台
hibernate.show_sql=true
//格式化sql
hibernate.format_sql=true
//hibernate方言,告诉hibernate使用的是什么数据库,如:使用MySQL数据库
hibernate.dialect=org.hibernate.dialect.MySQLDialect
//表的自动创建
hibernate.hbm2ddl.auto=create-drop 每次都创建一个新表,执行完成后删除。一般测试中使用
create 每次都会创建一个新表,一般在测试使用
update 如果数据库中有表不创建,没有表则创建,如果映射不匹配,会自动更新表结构(只能添加)
validate 只会使用存在的表,并且会对映射关系进行校验。表不存在则报错
//配置hibernate映射文件所在位置,可多个,如:
<mapping resource="cn/itheima/domain/xxx.hbm.xml" />
</session-factory>
</hibernate-configuration>
Hibernate常用API
Configuration 用于加载hibernate配置文件
() //加载的src下的hibernate.properties
configure(); //加载src下的hibernate.cfg.xml
configure("核心配置文件名称"); //加载指定的名称的配置文件
//手动加载映射配置
addResource("xxx/xxx.hbm.xml") //直接加载映射配置文件
addClass(实体类名.class) //直接在实体类所在包下查找规范映射配置文件
SessionFactory 接口,负责初始化Hibernate,充当数据存储源的代理,并负责创建Session对象,这里用到工厂模式
注意:不是轻量级,一个项目通常一个SessionFactory即可,多个数据库可对应多个SessionFactory
获取通过Configuration对象.buildSessionFactory()
//获取Session
Session openSession() //从连接池中获取一个连接
Session getCurrentSession() //获取一个与线程绑定的Session
内部维护了连接池,如果使用c3p0:
1.导入 hibernate/lib/options下关于c3p0的jar包
2.在hibernate.cfg.xml中<property>配置c3p0连接(参考etc/hibernate.properties)
hibernate.c3p0.max_size=20 最大连接数
hibernate.c3p0.min_size=5 最小连接数
hibernate.c3p0.timeout=120 超时,单位s
hibernate.c3p0.idle_test_period=3000 空闲连接,单位s
Session 接口,负责执行被持久化对象的CRUD,操作Session对象是非线程安全的
Transaction beginTransaction() //获取Transaction对象
save(实体类对象) //保存对象
update(实体类对象) //针对于脱管对象,操作时会将脱管对象转换成持久对象再操作
delete(实体类对象) //删除一个脱管对象,与session关联再删除
saveOrUpdate(实体类对象)
//查询操作,获取查询对象
实体类对象 get/load(实体类.class,id) //根据id进行查询
Query getNameQuery("命名检索名") //通过命名检索语句,获取查询对象
Query createQuery("hql") //获取执行HQL的对象
SQLQuery CreateSQLQUery("sql") //获取执行SQL的对象
Criteria createCriteria(实体类.class) //获取一个Criteria它可以完成条件查询
解决session安全问题:只需在方法内部使用session即可
通过load形式加载的延迟加载的对象,如何进行初始化
1.对象.方法() //使用以下该对象
2.hibernate.initilize(对象)
3.openSessionInView() //filter,Spring框架
Transaction 接口,事务管理封装
commit()
rollback()
设置自动提交
问题:如果在程序中没有开启事务,session的每一个操作就会开启一个事务
默认事务不自动提交,设置自动提交:hibernate.cfg.xml中设置属性:
hibernate.connection.autocommit=true
查询两种表达方式:HQL语言(操作的全是类中属性)或SQL语句(SQLQuery是Query的子类)
Query 接口,执行HQL语言,方便地对数据库及持久对象进行查询
//分页查询方法
setFirstResult(10) //开始位置,公式:(当前页-1)*每页显示
setMaxResult(10) //设置每页显示条数
//查询指定列,投影查询
查询一个字段 得到的结果是List<Object[]>
查询多个字段 得到的结果是List<实体类> //实体类需再添加相对应查询列的构造方法
//参数赋值方法
setParameter(0,"value") //对参数赋值
setParameter("myname","value") //对有名称参数赋值
//执行查询,返回结果
List<实体类> list() //返回多个数据时使用
Object uniqueResult() //如果保证结果唯一,使用这个
SQLQuery //本地SQL检索
//执行查询
List<T> list()
T uniqueResult()
//将结果封装到指定对象,如果不封装,得到是List<Object>
addEntity(实体类.class)
//设置参数
setParameter(0,"value")
//对于命名检索,直接执行会抛异常,因为hibernate不知道执行的语句该如何封装数据
需注解配置:PO类上
@SqlResultSetMapping(name="setMappingName",
entities={@EntityResult(entityClass=类.class,fields={
@FieldResult(name="类属性",column="表字段"),@FieldResult(name="name",column="name")
})}
)
@NameNativeQuery(name="myHql",query="hql语句",resultSetMapping="setMappingName")
QBC检索方式(query by criteria):更加面向对象的检索方式,是轻量级,不能再session外使用
Criteria 类似查询
List<T> list() //查询数据
//排序
addOrder(org.hibernate.criterion.Order.desc("属性名")) //降序,asc升序,参数是hibernate中的对象
//添加条件
add(Criterion)
//分页,设置两个方法
//分组 count sum avg max min
setProjection(Projections.projectionList().add(Projections.sum("属性名")).add(Projections.groupProperty("属性名")))
离线条件查询
//web端,封装条件
DetachedCriteria dc=DetachedCriteria.forClass(类.class);
dc.add(Restrictions.like("name","张_"))
//传递dc对象到dao
Criteria criteria=dc.getExecutableCriteria(session);
Hibernate持久化类PO
PO(Persistent Object)持久化类:PO=POJO+hbm映射配置
PO编写规则
提供无参public构造
所有属性private,提供public的get/set方法
提供一个标识属性,与数据库中主键对应,这个属性叫OID
属性尽量使用基本数据类型的包装类
PO类不能使用final修饰符
OID作用:Hibernate框架通过OID区分不同的PO对象,如果内存中有两个相同OID对象,那么hibernate认为他们是同一对象
数据类型使用包装类原因:使用基本数据类型是没有办法去描述不存在概念,对于包装类对象默认值是null
不能使用final修饰符原因(hibernate中get/load区别)
Get/load方法它们都是根据id去查询对象。
1.get直接得到了一个持久化类型对象,它就是立即查询操作
load它得到的是持久化类开的代理类型对象(子类对象)。它采用了一种延迟策略来查询数据。
2.get方法在查询时,如果不存在返回null
load方法在查询时,如果不存在抛ObjectNotFoundException.
持久化对象三种状态
1.瞬时态:也叫临时态或自由态,没有OID和session,一般指new出来的对象,与数据库中信息无关联
2.持久态:有OID,有session,在数据库中有可能有,也有可有没有。
特点:在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。
3.脱管态:也叫游离态或离线态,有OID,没有session
特点:对于托管态对象,它发生改变时hibernet不能检测到
判断持久化类对象三种状态:
1.是否有OID
2.是否与session关联
三种状态切换
1.瞬时态(new 出来的)
瞬时 -》 持久 save saveOrUpdate
瞬时 -》 脱管 手动设置oid
2.持久态 由session管理
持久 -》 瞬时 delete() 被删除后持久化对象不在建议使用
持久 -》 脱管 注意:session它的缓存就是所说的一级缓存
evict(清除一级缓存中指定的一个对象)
clear(清空一级缓存)
close(关闭,清空一级缓存)
3.脱管态 无法直接获取
脱管 -》 瞬时 直接将oid删除
脱管 -》 持久 update saveOrUpdate lock(过时)
Hibernate主键生成策略
Hibernate中定义主键类型
自然主键:具有业务含义的字段作为主键,如:学号、身份证号
代理主键:(推荐)不具有业务含义的字段作为主键,如:MySQL自增id,Oracle序列生成的主键,uuid方法生成的唯一序列串
主键生成器/描述
代理主键
increment 由hibernate维护一个变量,每次生成主键时自动递增
优点:方便跨平台
缺点:不适合高并发访问,如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突
identity 由底层 支持自动增长数据类型的数据库生成表标识符,如MySQL自增主键
优点:由底层数据库维护,和hibernate无关
sequence 由底层 支持序列的数据库生成标识符,如Oracle
优点:同上
native (不建议采用)根据底层数据库地总选择 identity,sequence,hilo,
优点:在项目中如果存在多个数据库时使用
缺点:每次判断,效率低
uuid (建议采用)采用128bit位的UUID算法来生成标识符
优点:与数据库无关,方便数据库移植,效率高
缺点:uuid长度大,占用空间大
自然主键
assigned (不建议)由java程序负责生成标识符
尽量在操作中避免手动对主键操作
Hibernate一级缓存
一级缓存:session级别缓存,一次会话
作用:减少对数据库访问
在session中定义了一系列的集合来存储数据,它们构成session缓存。如:
actionQueue它是一个行列队列,主要记录crud操作的相关信息
persistenceContext持久化上下文,真正缓存
二级缓存:SessionFactory级别缓存,整个系统
持久化对象具有自动更新数据库能力
原因:当事务提交,session关闭,向数据库发送请求时,会判断一级缓存区中数据是否与快照区一致,如果不一样,就会发送update语句
一级缓存常用API
clear() 清空一级缓存.
evict(obj) 清空一级缓存中指定的一个对象。
refresh(obj) 重新查询数据库,用数据库中信息来更新一级缓存与快照
一级缓存特点:
1.当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
2.当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
3.当调用session的close方法时,session缓存清空。
Hibernate注解开发
PO类基本注解配置(大部分注解都是javax.persistence包下,JPA规范)
实体类上添加
@Entity //声明一个实体类
@Table(name="表名",catalog="数据库名") //描述与表对应关系
主键属性上|get方法上
@Id //声明主键
@GeneratedValue(strategy=GenerationType.IDENTITY) //声明主键生成策略,不写默认相当于native
策略 AUTO,IDENTITY,SEQUENCE
//主键生成策略使用uuid类型
@GenericGenerator(name="myuuid",stratrgy="uuid") //自定义生成策略,hibernate中的包
@GeneratedValue(generator="myuuid") //引用自定义生成策略
其他属性上|get方法上
@Column(name="表列名",length=20,nullable=true) //不写默认列的名称就是属性的名称,字符串类型才写length
@Temporal(TemporalType.TIMESTAMP) //声明日期类型
DATA年月日 TIME时分秒 TIMESTAMP年月日时分秒
设定类的属性不在表中映射
属性上添加 @Transient
在hibernate.cfg.xml中将PO类中的注解引用生效
<mapping class="实体类全类名">
一对多映射关系配置
//1实体类
在关联集合对象属性上
@OneToMany(targetEntity=对方类.class,mappedBy="对方持有对象")
//多实体类
在关联对象属性上
@ManyToOne(targetEntity=对方类.class)
@JoinColumn(name="表外键列名") //指定外键列名
多对多映射关系配置
只需在一端配置中间表,另一端使用mappedBy放弃外键维护权即可
//一端配置中间表:在关联集合对象属性上
@ManyToMany(targetEntity=对方类.class)
@JoinTable(name="中间表名",
joinColumns={@JoinColumn(name="列名")}, //本类与中间表的映射关系
inverseJoinColumns={@JoinColumn(name="列名")} //对方类与中间表的映射关系
)
//一端放弃维护外键
@ManyToMany(targetEntity=对方类.class,mappedBy="对方持有集合名")
一对一配置
方式一:任意一方添加外键
//一端维护外键
@OneToOne(targetEntity=对方类.class)
@JoinColumn(name="外键列名")
//一端泛起维护外键
@OneToOne(targetEntity=对方类.class,mappedBy="对方持有属性")
方式二:主键映射方式
//一端
@OneToOne
@PrimaryKeyJoinColumn //说明两个表是使用主键映射
//一端
@OneToOne
@PrimaryKeyJoinColumn
主键属性上
@GenericGenerator(name="myForeignKey",strategy="foreign",
parameters={@Parameter(name="property",value="本方持有属性名")}) //主键设置成指定参考类的主键方式
@GenerateValue(generator="myForeignKey")
配置级联操作
对于多方不配置级联删除操作
方式一:使用JPA提供的注解
在@OneToMany中添加 cascade=CascadeType.All
方式二:使用hibernate提供的注解
在关联集合属性对象上添加 @Cascade(CascadeType.SAVE_UPDATE)
扩展:DELETE_ORPHAN过时问题,代替:在@OneToMany中添加 orphanRemoval=true
检索方式
五种检索方式
1.导航对象图检索方式,根据已加载的对象导航到其关联对象
2.OID检索方式,按照对象的OID检索对象,get/load方法
3.HQL检索方式,使用面向对象的HQL查询语言
4.QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
5.本地SQL检索方式,使用本地数据库的SQL语言查询语句
HQL检索
HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力
格式: Select/update/delete …… from …… where …… group by …… having …… order by …… asc/desc
基本检索 from 类名
排序检索 from 类名 order by 属性名 desc
条件检索 from 类名 where 属性名>:name //有名称参数
from 类名 where 属性名>?
分页检索 设置两个方法
分组统计检索 select sum(属性名) from 类名 group by 属性名
count max min avg sum
* 只能用来做数字统计
投影检索 select new 类(属性名...) from 类 //将结果封装到类对象,需提供对应构造
命名检索
hbm配置:<hibernate-mapping><query name="myHql">hql语句<>
注解配置:PO类上 @NameQuery(name="myHql",query="hql语句")
使用:session.getNameQuery("myHql") 返回 Query对象
如果有参数,可以通过命名参数赋值
HQL多表操作(自动消除笛卡尔积)
迫切:得到的结果是直接封装到PO类中的List<T>,而内连接/左外连接得到的是List<Object[]>
交叉连接
内连接
显示内连接 from 类名 别名 inner join 别名.关联属性 with 条件
隐式内连接 from 类名 别名 where 条件 //条件可以为 别名.关联属性.属性
迫切内连接 join后加 fetch //如果类名是1的一方,则结果可能重复,from前加:select distinct 别名
外连接
左外连接 from 类名 别名 left outer join 别名.关联属性
迫切左外连接 join后加 fetch
右外连接
事务管理
设置事务隔离级别
核心配置文件配置属性 hibernate.connection.isolation = 1|2|4|8
1 隔离级别为 read uncommitted
2 read committed
4 repeatable read
8 serializable
session管理
三种管理session的方式(开发使用前2种):
1.本地线程绑定session(ThreadLocal)
2.JTA事务绑定session(分布式事务管理)
3.程序管理Session
本地线程绑定使用
需在hibernate.cfg.xml配置属性:hibernate.current_session_context_class=thread
代码中获取session:sessionFactory.getCurrentSession();
注意:获取的session在事务关闭时,session对象会close。所以不需要手动close
应用:一个请求对应一个ThreadLocal,将session保存到ThreadLocal中,只要是同一个线程,就可以在任意层去创建session,并可以保证使用的是同一个session
优化方案
HQL优化
1.使用占位符参数绑定方式,可以节省CPU时间和内存,避免SQL注入
2.避免使用 not in 关键字
3.尽量使用where替换having
4.尽量减少对表的查询
5.尽量使用表别名
6.实体的更新与删除,hibernate3后可以使用 update ...
7.一级缓存,在大批量的增加更新操作,需手动清除一级缓存,session.evict()和session.clear()
检索策略/抓取策略
类级别检索:直接通过session检索某一类对应的数据
分为:延迟检索(默认)、立即检索
xml设置: hbm下<class>标签添加属性:lazy="true" //true默认,false延迟加载
注解设置: 类上:@Proxy(lazy=true)
关联级别检索:通过对象获得其关联的对象或属性,称为关联级别检索
抓取策略:值得是找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略
fetch 描述SQL语句的格式(如:多条,子查询,多表联查)
lazy 控制SQL语句何时发送
注解配置:关联属性上
//一方
@Fetch(FetchMode.SELECT)
SELECT 多条简单SQL(默认)
JOIN 采用迫切左外连接,lazy失效,所以只能立即查询
SUBBSELECT 生成子查询的SQL
@LazyCollection(LazyCollectionOption.xxx)
TRUE 延迟检索(默认),在真正使用该对象时才会发送SQL语句
FALSE 立即检索
EXTRA 加强延迟检索,只查询需要的数据,不全部查询信息
7种结论:查询类信息使用 [fetch] 语句方式,关联信息加载时机为 [lazy]
//多方
@Fetch(FetchMode.SELECT)
SELECT 多条简单SQL(默认)
JOIN 采用迫切左外连接,lazy失效,所以只能立即查询
@LazyToOne(LazyToOneOption.TRUE)
false 立即检索
proxy (默认) 是否采用延迟,根据1方类级别检索决定
批量抓取
在查询对象的多个关联对象时,可以采取批量抓取方式对程序优化
可以解决N+1问题
//查询1方,查询多方
1方(主表)上配置:
xml配置:hbm.xml/<set> 设置属性 batch-size=4
注解配置:关联属性上 @BatchSize(size=4)
//查询多方,查询1方
1方(主表)上配置:
xml配置:hbm.xml/<class> 设置属性 batch-size=4
注解配置:类上 @BatchSize(size=4)