Hibernate框架快速入门笔记
2018-04-08 23:42
423 查看
一、Hibernate文件目录结构
二、Hibernate的配置详解
核心配置文件 hibernate.cfg.xml
映射配置文件 xxx.hbm.xml
三、Hibernate的执行原理和常用API
Configuration
SessionFactory
Session
Transaction
Query(重点)
Criteria
四、Hibernate持久化类与主键生成策略
Hibernate持久化类
Hibernate主键生成策略
Hibernate持久化对象状态
五、Hibernate一级缓存
六、Hibernate关联映射–数据对象三种关系
一对多
Hbm映射文件编写
七、Hibernate注解开发
一对多(多对一)
Hibernate关联映射-多对多
Hibernate关联映射-一对一
八、Hibernate检索方式概述
导航对象图检索方式
OID检索方式
HQL
QBC
本地SQL
多表操作
九、Hibernate事务管理
十、Hibernate优化方案
HQL优化
检索策略
抓取策略
Hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成操作,就是与数据库的交互操作。它是在dao层去使用的。
对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping)。简单说,我们使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作。
![](https://img-blog.csdn.net/20180408205153201?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
documentation目录:存放hibernate的相关文件与API
lib目录:存放hibernate编译和运行所依赖的jar包,其中required子目录下包含了运行hibernate项目必须的jar包
project目录:存放hibernate各种相关的源代码与资源.
在lib/required目录中,包含必需的jar包
快速搭建项目需要导入的包
导入lib/required下所有的jar
导入数据库的驱动jar包
日志相关jar包 3
将hibernate/project/etc/log4j.properties文件导入到工程src下
hibernate.cfg.xml
hibernate.properties
我们在开发中使用比较多的是hibernate.cfg.xml这种方式,原因它的配置能力更强,易于修改
我们主要学习的是hibernate.cfg.xml配置
1. 可以加载数据库相关信息
2. hibernate相关配置
3. 加载映射配置文件
对于hibernate.cfg.xml配置文件中的内容可以参考hibernate/project/etc/hibernate.properties的配置
4. 配置
Create-drop 每次都会创建一个新的表,执行完成后删除。一般在测试中使用
Create 每次都会创建一个新的表,一般是在测试中使用
update 如果数据库中有表,不创建,没有表创建,如果映射不匹配,会自动更新表结构(只能添加)
validate 只会使用存在的表,并且会对映射关系进行校验.
统一声明包名,这样在
关于
name属性:类的全名称
table 表的名称,可以省略,这时表的名称就与类名一致
catalog属性:数据库名称 可以省略.如果省略,参考核心配置文件中url路径中的库名称
关于
首先它必须存在。
name 类中的属性名称
column 表中的主键名称 column它也可以省略,这时列名就与类中属性名称一致
length 字段长度
type属性 指定类型
关于
它是描述类中属性与表中非主键的映射关系
关于hibernate的映射文件中类型问题
对于type属性它的取值,可以有三种:
1. java中的数据类型
2. hibernate中的数据类型
3. SQL的数据类型
![](https://img-blog.csdn.net/20180408205240466?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
由hibernate.cfg.xml中的
通过config.buildSessionFactory();//得到sessionFactory。
sessionFactory.openSession();//得到session。
session.beginTransaction();//开启事务。
persistent operate;
session.getTransaction().commit();//提交事务
关闭session;
关闭sessionFactory;
Hibernate的核心类和接口一共有6个
分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。这6个核心类和接口在任何开发中都会用到
Configuration config=new Configuration().config(); 主要加载src下的hibernate.cfg.xml
Configuration config=new Configuration();主要加载的src下的hibernate.properties
Configuration config=new Configuration().config(核心配置文件名称);加载指定的名称的配置文件
问题:我们是在hibernate.cfg.xml文件中有xxx.hbm.xml文件的位置。如果我们使用的是hibernate.properties这种核心配置,它如何加载映射配置?
SessionFactory它不是轻量级的,不要频繁创建关闭它。在一个项目中有一个SessionFactory就可以,通过SessionFactory来获取Session进行操作。
SessionFactory内部还维护了一个连接池,如果我们要想使用c3p0连接池,应该怎样处理?
我们要导入c3p0的相关jar包
在hibernate/lib/options下有关于c3p0连接池jar包
在hibernate.cfg.xml文件中配置c3p0连接
可以查看etc/hibernate.properties中关于c3p0的配置
问题:我们如何解决session的安全问题?
我们只需要在方法内部来使用Session就可以。
问题:Session如何获取到?
SessionFactory.openSession() ; 相当于直接通过SessionFactory创建一个新的Session,使用完成后要手动调用close来关闭。
SessionFactory.getCurrentSession(); 获取一个与线程绑定的Session,当我们提交或事务回滚后会自动关闭。
Session常用的方法
save 保存对象
update 修改操作
delete删除
get/load 根据id进行查询
savenOrUpdate 执行save或update操作
createQuery()获取一个Query对象
createSQLQUery()获取一个可以操作sql的SQLQuery对象
createCriteria() 获取一个Criteria它可以完成条件查询
commit 事务提交
rollback 事务回滚
问题:如果在程序中没有开启事务,是否存在事务?
有事务,session的每一个操作就会开启一个事务。
默认情况下事务是不会自动提交的。
通过Query主要完成查询操作.
我们通过Query可以执行hql语句.
Query query=Session.createQuery(hql);
下面这个可以执行sql语句
SQLQUery sqlQuery=Session.createSQLQuery(sql);
SQLQuery是Query的子.
使用hql完成查询所有操作
分页查询
查询指定列信息
Select name ,address from Customer; 得到的是
要想得到List结果
1. 在Customer类中生成以name,address为参数的构造,注意,无参数构造也要有。
2. Select new Customer(name,address) from Customer;
条件查询
无名称参数 from Customer where name=?
对其进行赋值 query.setParameter(0,”张三”)
有名称参数 from Customer where name=:myname;
对其进行赋值 query.setParameter(“myname”,”李四”);
如果查询结果可以保证就是唯一 的,我们可以使用
query. uniqueResult()来得到一个单独对象.
执行本地SQL
要想执行本地sql
SQLQuery sqlQuery=session.createSqlQuery(String sql);
使用addEntity方法来将结果封装到指定的对象中,如果不封装,得到的是
如果sql中有参数,我们使用setParameter方法完成参数传递。
如果结果就是一个可以使用uniqueResult()来得到一个单独对象。
首先我想使用Criteria,必须得到Criteria
Criteria criteria=Session.createCriteria()
查询所有操作
Session.createCriteria(实体类.class)得到一个Criteria对象,调用list查询所有
分页操作与query的方法一样
setFirstResult() setMaxResults()
条件查询
criteria.add(Restrictions.eq(“name”,”xxxx”));
criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))
我们使用Criteria可以更加面向对象去操作,它非常适合进行多条件组合查询。
持久化类Persistent Object (PO)其实就相当于PO=POJO+hbm映射配置。
hibernate中的PO编写规则
必须提供一个无参数的public构造方法
所有属性要private ,对外提供public 的get/set方法
在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID
PO类中的属性尽量使用基本数据类型的包装类.
Int-Integer double–Double float-Float
PO类它不能使用final修饰符
OID作用
OID指的是与数据库中表的主键对应的属性。
Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate认为它们是同一个对象。
为什么PO类属性它要使用包装类型
使用基本数据类型是没有办法去描述不存在概念,如果使用包装类型,它就是一个对象,对于对象它的默认值是null。
PO类不可以使用final修饰?(hibernate中的get/load方法的区别)
get直接得到了一个持久化类型对象,它就是立即查询操作。
load它得到的是持久化类开的代理类型对象(子类对象)。它默认采用了一种延迟策略来查询数据。
get方法在查询时,如果不存在返回null。
load方法在查询时,如果 不存在,会产生异常ObjectNotFoundException。
自然主键:具有业务含义 字段 作为主键,比如:学号、身份证号。
代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:mysql自增主键,oracle序列生成的主键、uuid()方法生成的唯一序列串。
瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录。它使用完成后,会被jvm直接回收掉,它只是用于信息携带。
简单说:无OID 与数据库中的信息无关联,不在session管理范围内。
持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。
简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。
托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数据库中有可能存在,也有可能不存在。
对于托管态对象,它发生改变时hibernet不能检测到。
持久化类三种状态切换
判断持久化类对象三种状态:
1. 是否有OID
2. 判断是否与session关联
![](https://img-blog.csdn.net/20180408205407571?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
瞬时态(new 出来的)
瞬时——持久 save saveOrUpdate
瞬时—–脱管(游离) 手动设置oid
.持久态 它是由session管理
持久——-瞬时 delete() 被删除后持久化对象不在建议使用
持久—–脱管 注意:session它的缓存就是所说的一级缓存
evict(清除一级缓存 中指定的一个对象)
clear(清空一级缓存)
close(关闭,清空一级缓存)
.脱管态 (它是无法直接获取)
脱管—–瞬时 直接将oid删除
脱管—-持久 update saveOrUpdate lock(过时)
actionQueue它是一个行列队列,它主要记录crud操作的相关信息。
persistenceContext它是持久化上下文,它其实是真正缓存。
在session中定义了一系列的集合来存储数据,它们构成session缓存。只要session没有关闭,它就会一直存在。当我们通过hibernate中的session提供的一些API例如 save get update等进行操作时,就会将持久化对象保存到session中,当下一次在去查询缓存中具有的对象(OID值来判断),就不会去从数据库查询,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库访问。
持久化对象具有自动更新数据库能力
![](https://img-blog.csdn.net/20180408210408385?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
一级缓存常用API
一级缓存特点
当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
当调用session的close方法时,session缓存清空。
clear 清空一级缓存.
evict 清空一级缓存中指定的一个对象。
refresh重新查询数据库,用数据库中信息来更新一级缓存与快照
Hibernate常用API-Session补充
update
udpate操作它主要是针对于脱管对象,持久对象具有自动更新能力。
Update操作时,如果对象是一个脱管对象,可以操作,它会将脱管对象转换成持久对象在操作。
如果在session中出现相同的oid两个对象,会产生异常。
脱管对象的oid如果在数据表中不存在,会报异常。
saveOrUpdate
如果对象是一个瞬时对象 ——–执行save操作
如果对象是一个脱管对象———执行update
如果是一个持久对象——-直接返回
delete
删除一个脱管对象,与session关联,在删除。
使用级联操作,设置
我们在开发中要配置双向关联配置。———可以通过任意一方来操作对方
在操作代码,尽量来要进行单向关联。——可以尽量资源浪费。
在双向关联中,会存在多余的update语句。我们可以使用
Inverse它的值如果为true代表,由对方来维护外键。
Inverse它的值如果为false代表,由本方来维护外键。
原则:inverse设置在主表的一方,外键在哪一个表中,我们就让哪一方来维护外键。
对象导航
![](https://img-blog.csdn.net/20180408212659911?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
cascade总结
使用cascade可以完成级联操作
它可常用取值
none这是一个默认值
save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.
delete 级联删除
delete-ophan 删除与当前对象解除关系的对象。
all 它包含了save-update delete操作
all-delete-orphan 它包信了delete-orphan与all操作
cascade与inverse有什么区别
cascade它是完成级联操作
Inverse它只有在双向关联情况下有作用,它来指定由哪一方维护外键。
@Entity 声明一个实体
@Table来描述类与表对应
@Id来声明一个主键
@GenerateValue 用它来声明一个主键生成策略
默认情况下相当于native
可以选择的主键生成策略 AUTO IDENTITY SEQUENCE
@Column来定义列
注意:对于PO类中所有属性,如果你不写注解,默认情况下也会在表中生成对应的列。
列的名称就是属性的名称
@Temporal来声明日期类型
可以选择
TemporalType.DATA 只有年月日
TemporalType.TIME 只有小时分钟秒
TemporalType.TIMESTAMP 有年月日小时分钟秒
@Transient设定类的属性不在表中映射
如果我们主键生成策略想使用UUID类型。
对于我们以上讲解的关于属性配置的注解,我们也可以在其对应的getXxx方法去使用。我们最终需要在hibernate.cfg.xml文件中将我们类中的注解配置引用生效。
对于这个示例我们需要在Customer中配置cascade操作,save-update
第一种方式,可以使用JPA提供的注解。在@OneToMany中
第二种方式:可以使用hibernate提供的注解.如上代码。
扩展:关于hibernate注解@Cascade中的DELETE_ORPHAN过时,我们在@OneToMany中添加
创建PO类
描述学生与老师.
![](https://img-blog.csdn.net/20180408215058764?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NtbHhzX2xiYg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
1. 在任意一方添加外键
2. 主键映射
创建PO类
以人与身份证号为例
导航对象图检索方式,根据已加载的对象导航到其它对象
OID检索方式,按照对象的OID来检索对象
HQL检索方式,使用面向对象的HQL查询语言
QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
本地SQL检索方式,使用本地数据库的SQL查询语句
c.getOrders().size()
通过在hibernate中进行映射关系,在hibernate操作时,可以通过导航方式得到
其关联的持久化对象信息。
Session.load(Order.class,1);
Hibernate中通过get/load方法查询指定的对象,要通过OID来查询。
HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力
因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语 句的查询方式,同时也提供了更加面向对象的封装。完整的HQL语句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。
基本步骤:
1. 得到Session
2. 编写HQL语句
3. 通过session.createQuery(hql)创建一个Query对象
4. 为Query对象设置条件参数
5. 执行list查询所有,它反胃的是List集合 uniqueResut()返回一个查询结果。
PO类
QBC步骤
通过Session得到一个Criteria对象 session.createCriteria()
设定条件 Criterion实例 它的获取可以通过Restrictions类提供静态。
Criteria的add方法用于添加查询条件
调用list进行查询 criterfia.list.
Hql多表操作分类
交叉连接
内连接
a) 显示内连接
b) 隐式内连接
外连接
左外连接
右外连接
在hibernate.cfg.xml文件中配置
它可取的值有 1 2 4 8
1.代表的事务隔离级别为READ UNCOMMITTED
2.代表的事务隔离级别为READ COMMITTED
4.代表的事务隔离级别为 REPEATABLE READ
8.代表的事务隔离级别为 SERIALIZABLE
Hibernate中session管理
Hibernate提供了三种管理session的方式,在实际开发中我们一般使用的是前两种。
Session对象的生命周期与本地线程绑定(ThreadLocal)
Session对象的生命周期与JTA事务绑定(分布式事务管理)
Hibernate委托程序来管理Session的生命周期
本地线程绑定Session
需要在hibernate.cfg.xml文件配置
在获取session时不要在使用openSession而是使用getCurrentSession()方法。
使用getCurrentSession获取的与线程绑定的session对象,在事务关闭时,session对象也会close,简单说,就不需要我们在手动close。
2.尽量少使用NOT
3.尽量使用where来替换having
4.减少对表的查询
5.使用表的别名
6.实体的更新与删除
延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
load方法采用的策略延迟加载、
get方法采用的策略立即加载。
检索策略分为两种:
1. 类级别检索
2. 关联级别检索
类级别检索
类级别检索是通过session直接检索某一类对应的数据,例如
Customer c=session.load(Customer.class,1)
Session.createQuery(“from Order”)
类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过
关联级别检索
查询到某个对象,获得其关联的对象或属性,这种称为关联级别检索,例如
c.getOrders().size()
c.getName()
对于关联级别检索我们就要研究其检索策略(抓取策略)
指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。
注解配置抓取策略
set上的fetch与lazy
set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。根据一的一方去查询多的一方
Fetch可取值有:
SELECT 多条简单的sql (默认值)
JOIN 采用迫切左外连接。如果fetch选择的是join方案,那么lazy它会失效。
SUBSELECT 将生成子查询的SQL
lazy可取值有:
TURE 延迟检索 (默认值)
FALSE 立即检索
EXTRA 加强延迟检索(及其懒惰)
One的上的fetch与lazy
在多的一方如何查询一的主方信息。例如:获取到一个订单对象,要查询客户信息。
Fetch可取值
select 默认值,代表发送一条或多条简单的select语句
join 发送一条迫切左外连接。如果fetch值为join,那么lazy失效。
lazy可取值
false 不采用延迟加载
proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定
no-proxy 不用研究
批量抓取
我们在查询多个对象的关联对象时,可以采用批量抓取方式来对程序进行优化。
要想实现批量抓取,可以在配置文件中 batch-size属性来设置 ,可以使用注解 @BatchSize(size=4)。
无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置,如果是要查询子信息,那么我们是在上来设置batch-size,如果是从子方来查询父方, 也是在父方设置在设置batch-size。
二、Hibernate的配置详解
核心配置文件 hibernate.cfg.xml
映射配置文件 xxx.hbm.xml
三、Hibernate的执行原理和常用API
Configuration
SessionFactory
Session
Transaction
Query(重点)
Criteria
四、Hibernate持久化类与主键生成策略
Hibernate持久化类
Hibernate主键生成策略
Hibernate持久化对象状态
五、Hibernate一级缓存
六、Hibernate关联映射–数据对象三种关系
一对多
Hbm映射文件编写
七、Hibernate注解开发
一对多(多对一)
Hibernate关联映射-多对多
Hibernate关联映射-一对一
八、Hibernate检索方式概述
导航对象图检索方式
OID检索方式
HQL
QBC
本地SQL
多表操作
九、Hibernate事务管理
十、Hibernate优化方案
HQL优化
检索策略
抓取策略
Hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成操作,就是与数据库的交互操作。它是在dao层去使用的。
对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping)。简单说,我们使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作。
一、Hibernate文件目录结构
documentation目录:存放hibernate的相关文件与API
lib目录:存放hibernate编译和运行所依赖的jar包,其中required子目录下包含了运行hibernate项目必须的jar包
project目录:存放hibernate各种相关的源代码与资源.
在lib/required目录中,包含必需的jar包
快速搭建项目需要导入的包
导入lib/required下所有的jar
导入数据库的驱动jar包
日志相关jar包 3
将hibernate/project/etc/log4j.properties文件导入到工程src下
二、Hibernate的配置详解
Hibernate中我们使用时主要有两种配置文件核心配置文件 hibernate.cfg.xml
对于hibernate的核心配置文件它有两种方式:hibernate.cfg.xml
hibernate.properties
我们在开发中使用比较多的是hibernate.cfg.xml这种方式,原因它的配置能力更强,易于修改
我们主要学习的是hibernate.cfg.xml配置
1. 可以加载数据库相关信息
2. hibernate相关配置
3. 加载映射配置文件
<session-factory> <!-- 配置关于数据库连接的四个项 driverClass url username password --> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3366/hibernateTest</property> <property name="hibernate.connection.username">root</property> <property name="hibernate.connection.password">123</property> <!-- 可以将向数据库发送的sql显示出来 --> <property name="hibernate.show_sql">true</property> <!-- 格式化sql --> <property name="hibernate.format_sql">true</property> <!-- hibernate的方言 --> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <!-- 自动创建表 --> <property name="hibernate.hbm2ddl.auto">update</property> <!-- 用于设置事务提交方式 --> <property name="hibernate.connection.autocommit">false</property> <!-- 配置hibernate的映射文件所在位置 --> <mapping resource="com/lbb/hibernate/domain/Customer.hbm.xml" /> </session-factory>
对于hibernate.cfg.xml配置文件中的内容可以参考hibernate/project/etc/hibernate.properties的配置
4. 配置
<property name="hibernate.hbm2ddl.auto">update</property>属性后,我们可以进行表的自动创建
Create-drop 每次都会创建一个新的表,执行完成后删除。一般在测试中使用
Create 每次都会创建一个新的表,一般是在测试中使用
update 如果数据库中有表,不创建,没有表创建,如果映射不匹配,会自动更新表结构(只能添加)
validate 只会使用存在的表,并且会对映射关系进行校验.
映射配置文件 xxx.hbm.xml
映射配置文件它的名称是类名.hbm.xml,它一般放置在实体类所在的包下。 这个配置文件的主要作用是建立表与类的映射关系。<hibernate-mapping package="cn.itheima.domain"> <!-- name属性它是实体类的全名 table 表的名称 catalog 数据库名称 --> <class name="Customer" table="t_customer" catalog="hibernateTest"> <!-- id它是用于描述主键 --> <id name="id" column="id" type="int"> <!-- java数据类型 --> <!-- 主键生成策略 --> <generator class="nati 2288b ve"></generator> </id> <!-- 使用property来描述属性与字段的对应关系 --> <property name="name" column="name" length="20" type="string"></property> <!-- hibernate数据类型 --> <property name="address"> <column name="address" length="50" sql-type="varchar(50)"></column> <!-- sql数据类型 --> </property> <property name="sex" column="sex" length="20"></property> </class> </hibernate-mapping>
统一声明包名,这样在
<class>中就不需要写类的全名.
<hibernate-mapping package="cn.itheima.domain">
关于
<class>标签配置
name属性:类的全名称
table 表的名称,可以省略,这时表的名称就与类名一致
catalog属性:数据库名称 可以省略.如果省略,参考核心配置文件中url路径中的库名称
关于
<id>标签
首先它必须存在。
<id>是用于建立类中的属性与表中的主键映射。
name 类中的属性名称
column 表中的主键名称 column它也可以省略,这时列名就与类中属性名称一致
length 字段长度
type属性 指定类型
<generator>它主要是描述主键生成策略.
关于
<property>标签
它是描述类中属性与表中非主键的映射关系
关于hibernate的映射文件中类型问题
对于type属性它的取值,可以有三种:
1. java中的数据类型
2. hibernate中的数据类型
3. SQL的数据类型
三、Hibernate的执行原理和常用API
hibernate工作原理通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
由hibernate.cfg.xml中的
<mappingresource="com/xx/User.hbm.xml"/>读取解析映射信息。
通过config.buildSessionFactory();//得到sessionFactory。
sessionFactory.openSession();//得到session。
session.beginTransaction();//开启事务。
persistent operate;
session.getTransaction().commit();//提交事务
关闭session;
关闭sessionFactory;
Hibernate的核心类和接口一共有6个
分别为:Session、SessionFactory、Transaction、Query、Criteria和Configuration。这6个核心类和接口在任何开发中都会用到
// 保存一个Customer @Test public void saveCustomerTest() { // 使用hibernate的api来完成将customer信息保存到mysql中操作 Configuration config = new Configuration().configure(); // 加载hibernate.cfg.xml SessionFactory sessionFactory = config.buildSessionFactory(); Session session = sessionFactory.openSession(); // 相当于得到一个Connection。 // 开启事务 Transaction transaction = session.beginTransaction(); // 操作 Customer c = new Customer(); c.setName("张三"); c.setAddress("北京"); c.setSex("男"); session.save(c); // 事务提交 transaction.commit(); session.close(); sessionFactory.close(); }
Configuration
它主要是用于加载hibernate配置Configuration config=new Configuration().config(); 主要加载src下的hibernate.cfg.xml
Configuration config=new Configuration();主要加载的src下的hibernate.properties
Configuration config=new Configuration().config(核心配置文件名称);加载指定的名称的配置文件
问题:我们是在hibernate.cfg.xml文件中有xxx.hbm.xml文件的位置。如果我们使用的是hibernate.properties这种核心配置,它如何加载映射配置?
Configuration config=new Configuration();//主要加载的src下的hibernate.properties // 手动加载映射 // config.addResource("cn/itheima/domain/Customer.hbm.xml"); 直接加载映射配置文件 // config.addClass(Customer.class); //这种方式它会直接在实体类所在包下查找规范映射配置文件
SessionFactory
SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。SessionFactory它不是轻量级的,不要频繁创建关闭它。在一个项目中有一个SessionFactory就可以,通过SessionFactory来获取Session进行操作。
public class HibernateUtils { private static Configuration config; private static SessionFactory sessionFactory; static{ config=new Configuration().configure(); sessionFactory=config.buildSessionFactory(); } // 是从连接池中获取一个连接 public static Session openSession(){ return sessionFactory.openSession(); } // 获取一个与线程绑定的Session public static Session getCurrentSession(){ return sessionFactory.getCurrentSession(); } }
SessionFactory内部还维护了一个连接池,如果我们要想使用c3p0连接池,应该怎样处理?
我们要导入c3p0的相关jar包
在hibernate/lib/options下有关于c3p0连接池jar包
在hibernate.cfg.xml文件中配置c3p0连接
可以查看etc/hibernate.properties中关于c3p0的配置
<!-- 设置连接提供者 --> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <!-- c3p0连接池的配置 --> <property name="hibernate.c3p0.max_size">20</property> <!-- 最大连接池 --> <property name="hibernate.c3p0.min_size">5</property> <!-- 最小连接数 --> <property name="hibernate.c3p0.timeout">120</property> <!-- 超时 --> <property name="hibernate.c3p0.idle_test_period">3000</property> <!-- 空闲连接 -->
Session
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。但需要注意的是Session对象是非线程安全的。问题:我们如何解决session的安全问题?
我们只需要在方法内部来使用Session就可以。
问题:Session如何获取到?
SessionFactory.openSession() ; 相当于直接通过SessionFactory创建一个新的Session,使用完成后要手动调用close来关闭。
SessionFactory.getCurrentSession(); 获取一个与线程绑定的Session,当我们提交或事务回滚后会自动关闭。
Session常用的方法
save 保存对象
update 修改操作
delete删除
get/load 根据id进行查询
savenOrUpdate 执行save或update操作
createQuery()获取一个Query对象
createSQLQUery()获取一个可以操作sql的SQLQuery对象
createCriteria() 获取一个Criteria它可以完成条件查询
Transaction
Transaction接口主要用于管理事务,它是hibernate的事务接口,对底层的事务进行了封装。使用它可以进行事务操作。commit 事务提交
rollback 事务回滚
问题:如果在程序中没有开启事务,是否存在事务?
有事务,session的每一个操作就会开启一个事务。
默认情况下事务是不会自动提交的。
<!-- 用于设置事务提交方式 --> <property name="hibernate.connection.autocommit">false</property>
Query(重点)
Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数、限制查询记录数量,并最终执行查询操作。通过Query主要完成查询操作.
我们通过Query可以执行hql语句.
Query query=Session.createQuery(hql);
下面这个可以执行sql语句
SQLQUery sqlQuery=Session.createSQLQuery(sql);
SQLQuery是Query的子.
使用hql完成查询所有操作
@Test public void test1() { Session session = HibernateUtils.openSession(); Query query = session.createQuery("from Customer");// from后面是类名 List<Customer> list = query.list(); System.out.println(list); session.close(); }
分页查询
// 分页查询 一页显示10条 要得到第二页数据 @Test public void test3() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Query query = session.createQuery("from Customer"); query.setFirstResult(10); query.setMaxResults(10); List list = query.list(); System.out.println(list); session.getTransaction().commit(); session.close(); }
查询指定列信息
Select name ,address from Customer; 得到的是
List<Object[]>结果
要想得到List结果
1. 在Customer类中生成以name,address为参数的构造,注意,无参数构造也要有。
2. Select new Customer(name,address) from Customer;
// 查询指定列信息 @Test public void test4() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Query query = session.createQuery("select name,address from Customer"); List<Object[]> list = query.list(); System.out.println(list); Query query2 = session.createQuery("select new Customer(name,address) from Customer"); List list2 = query2.list(); System.out.println(list2); session.getTransaction().commit(); session.close(); }
条件查询
无名称参数 from Customer where name=?
对其进行赋值 query.setParameter(0,”张三”)
有名称参数 from Customer where name=:myname;
对其进行赋值 query.setParameter(“myname”,”李四”);
如果查询结果可以保证就是唯一 的,我们可以使用
query. uniqueResult()来得到一个单独对象.
// 条件查询 @Test public void test5() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // Query query = session.createQuery("select new Customer(name,address) from Customer where name = ?"); Query query = session.createQuery("from Customer where name = ?"); query.setParameter(0, "姓名0"); Customer customer = (Customer) query.uniqueResult(); System.out.println(customer); Query query2 = session.createQuery("from Customer where name = :myname"); query2.setParameter("myname", "姓名0"); Customer customer2 = (Customer) query.uniqueResult(); System.out.println(customer2); session.getTransaction().commit(); session.close(); }
执行本地SQL
要想执行本地sql
SQLQuery sqlQuery=session.createSqlQuery(String sql);
使用addEntity方法来将结果封装到指定的对象中,如果不封装,得到的是
List<Object[]>
如果sql中有参数,我们使用setParameter方法完成参数传递。
如果结果就是一个可以使用uniqueResult()来得到一个单独对象。
// 执行本地sql----查询全部 @Test public void test6() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 执行select * from t_customer; SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer"); // List<Object[]> list = sqlQuery.list(); // System.out.println(list); // 想要将结果封装到Customer对象中 sqlQuery.addEntity(Customer.class); List<Customer> list = sqlQuery.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 执行本地sql----条件查询 @Test public void test7() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 执行select * from t_customer where name=?; SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer where name=?"); // 对参数进行赋值 sqlQuery.setParameter(0, "姓名1"); // 想要将结果封装到Customer对象中 sqlQuery.addEntity(Customer.class); // List<Customer> list = sqlQuery.list(); // System.out.println(list); Customer c = (Customer) sqlQuery.uniqueResult(); System.out.println(c); session.getTransaction().commit(); session.close(); }
Criteria
Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。值得注意的是Criteria接口也是轻量级的,它不能在Session之外使用。首先我想使用Criteria,必须得到Criteria
Criteria criteria=Session.createCriteria()
查询所有操作
Session.createCriteria(实体类.class)得到一个Criteria对象,调用list查询所有
分页操作与query的方法一样
setFirstResult() setMaxResults()
条件查询
criteria.add(Restrictions.eq(“name”,”xxxx”));
criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))
我们使用Criteria可以更加面向对象去操作,它非常适合进行多条件组合查询。
@Test public void test8() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Criteria criteria = session.createCriteria(Customer.class); // 查询所有 // List list = criteria.list(); // System.out.println(list); // 分页查询 // criteria.setFirstResult(10); // criteria.setMaxResults(10); // List list2 = criteria.list(); // System.out.println(list2); // 多条件查询 // 1.查询name='姓名1' // criteria.add(Restrictions.eq("name","姓名1")); // Customer customer = (Customer) criteria.uniqueResult(); // System.out.println(customer); // 2.查询address='上海' // criteria.add(Restrictions.eq("address", "上海")); // Customer customer = (Customer) criteria.uniqueResult(); // System.out.println(customer); // criteria.add(Restrictions.eq("name","姓名1")); // criteria.add(Restrictions.eq("address", "上海")); // Customer customer = (Customer) criteria.uniqueResult(); // System.out.println(customer); // List list = criteria.list(); // System.out.println(list); //查询name='姓名1' 或者 address='上海' criteria.add(Restrictions.or(Restrictions.eq("name", "姓名1"),Restrictions.eq("address","上海"))); List<Customer> list = criteria.list(); System.out.println(list); session.getTransaction().commit(); session.close(); }
四、Hibernate持久化类与主键生成策略
Hibernate持久化类
什么是持久化类?持久化类Persistent Object (PO)其实就相当于PO=POJO+hbm映射配置。
hibernate中的PO编写规则
必须提供一个无参数的public构造方法
所有属性要private ,对外提供public 的get/set方法
在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID
PO类中的属性尽量使用基本数据类型的包装类.
Int-Integer double–Double float-Float
PO类它不能使用final修饰符
OID作用
OID指的是与数据库中表的主键对应的属性。
Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate认为它们是同一个对象。
为什么PO类属性它要使用包装类型
使用基本数据类型是没有办法去描述不存在概念,如果使用包装类型,它就是一个对象,对于对象它的默认值是null。
PO类不可以使用final修饰?(hibernate中的get/load方法的区别)
get直接得到了一个持久化类型对象,它就是立即查询操作。
load它得到的是持久化类开的代理类型对象(子类对象)。它默认采用了一种延迟策略来查询数据。
get方法在查询时,如果不存在返回null。
load方法在查询时,如果 不存在,会产生异常ObjectNotFoundException。
Hibernate主键生成策略
Hibernate中定义的主键类型包括:自然主键和代理主键。自然主键:具有业务含义 字段 作为主键,比如:学号、身份证号。
代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:mysql自增主键,oracle序列生成的主键、uuid()方法生成的唯一序列串。
建议:企业开发中使用代理主键!
主键生成器 | 描述 |
---|---|
increment | 代理主键。由hibernate维护一个变量,每次生成主键时自动以递增。问题:如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突。建议不采用。优点:可以方便跨平台。缺点:不适合高并发访问 |
identity | 代理主键。由底层数据库生成表识符。条件是数据库支持自动增长数据类型。比如:mysql的自增主键,oracle不支持主键自动生成。如果数据库支持自增建议采用。优点:由底层数据库维护,和hibernate无关。缺点:只能对支持自动增长的数据库有效,例如mysql |
sequence | 代理主键。Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。比如oracle的序列。如果数据库支持序列建议采用。优点:由底层数据库维护,和hibernate无关。缺点:数据库必须支持sequence方案例如oracle。 |
native | 代理主键。根据底层数据库对自动来选择identity、sequence、hilo.由于生成主键策略的控制权由hibernate控制,所以不建议采用。优点:在项目中如果存在多个数据库时使用。缺点:效率比较低。 |
uuid | 代理主键。Hibernate采用128bit位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符。此策略可以保证生成主键的唯一性,并且提供了最好的数据库插入性能和数据库平台的无关性。建议采用。优点:与数据库无关,方便数据库移植,效率高,不访问数据库就可以直接生成主键值,并且它能保证唯一性。缺点:uuid长度大(32位),占用空间比较大,对应数据库中类型 char varchar |
assigned | 自然主键。由java程序负责生成标识符。不建议采用。尽量在操作中避免手动对主键操作 |
Hibernate持久化对象状态
持久化对象三种状态瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录。它使用完成后,会被jvm直接回收掉,它只是用于信息携带。
简单说:无OID 与数据库中的信息无关联,不在session管理范围内。
持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。
简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。
托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数据库中有可能存在,也有可能不存在。
对于托管态对象,它发生改变时hibernet不能检测到。
持久化类三种状态切换
判断持久化类对象三种状态:
1. 是否有OID
2. 判断是否与session关联
瞬时态(new 出来的)
瞬时——持久 save saveOrUpdate
瞬时—–脱管(游离) 手动设置oid
.持久态 它是由session管理
持久——-瞬时 delete() 被删除后持久化对象不在建议使用
持久—–脱管 注意:session它的缓存就是所说的一级缓存
evict(清除一级缓存 中指定的一个对象)
clear(清空一级缓存)
close(关闭,清空一级缓存)
.脱管态 (它是无法直接获取)
脱管—–瞬时 直接将oid删除
脱管—-持久 update saveOrUpdate lock(过时)
五、Hibernate一级缓存
Hibernate的一级缓存就是指session缓存。在Session——EventSource——SessionImpl实现类中,有如下属性private transient ActionQueue actionQueue; private transient StatefulPersistenceContext persistenceContext;
actionQueue它是一个行列队列,它主要记录crud操作的相关信息。
persistenceContext它是持久化上下文,它其实是真正缓存。
在session中定义了一系列的集合来存储数据,它们构成session缓存。只要session没有关闭,它就会一直存在。当我们通过hibernate中的session提供的一些API例如 save get update等进行操作时,就会将持久化对象保存到session中,当下一次在去查询缓存中具有的对象(OID值来判断),就不会去从数据库查询,而是直接从缓存中获取。Hibernate的一级缓存存在的目的就是为了减少对数据库访问。
持久化对象具有自动更新数据库能力
一级缓存常用API
一级缓存特点
当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
当调用session的close方法时,session缓存清空。
clear 清空一级缓存.
evict 清空一级缓存中指定的一个对象。
refresh重新查询数据库,用数据库中信息来更新一级缓存与快照
Hibernate常用API-Session补充
update
udpate操作它主要是针对于脱管对象,持久对象具有自动更新能力。
Update操作时,如果对象是一个脱管对象,可以操作,它会将脱管对象转换成持久对象在操作。
如果在session中出现相同的oid两个对象,会产生异常。
脱管对象的oid如果在数据表中不存在,会报异常。
所以:在操作中,建议我们通过持久化对象来直接修改其操作。
saveOrUpdate
如果对象是一个瞬时对象 ——–执行save操作
如果对象是一个脱管对象———执行update
如果是一个持久对象——-直接返回
delete
删除一个脱管对象,与session关联,在删除。
注意:如果执行delete操作,先删除一级缓存,在删除数据库中的数据。删除后对象id变为空,对象变为瞬时态
六、Hibernate关联映射–数据对象三种关系
Hibernate框架基于ORM设计思想,它将关系型数据库中的表与我们java中的类进行映射,一个对象就对应着表中的一条记录,而表中的字段对应着类中的属性。数据库中表与表之间存在着三种关系,也就是系统设计中的三种实体关系。一对多
实体类创建public class Customer { private Integer id; // 主键 private String name; // 姓名 // 描述客户可以有多个订单 private Set<Order> orders = new HashSet<Order>(); public class Order { private Integer id; private Double money; private String receiverInfo; // 收货地址 // 订单与客户关联 private Customer c; // 描述订单属于某一个客户
Hbm映射文件编写
<!-- 一个客户关联多个订单 --> <set name="orders" inverse="false" cascade="all"> <key column="cid" /> <one-to-many class="cn.itheima.oneToMany.Order" /> </set> <!-- 使用set来描述在一的一方中关联的多 Set<Order>, 它的name属性就是set集合的名称 key:它主要描述关联的多的一方产生的外键名称,注意要与多的一方定义的外键名称相同 one-to-many 描述集合中的类型 --> <!-- 多对一 --> <many-to-one fetch="join" lazy="false" name="c" class="cn.itheima.oneToMany.Customer" column="cid" cascade="save-update"> </many-to-one> <!-- name属性它描述的是Order类中的一的一方的属性名称 Customer c; class 代表一的一方的类型 column 描述的是一对多,在多的一方产生的外键的名称 c_customer_id -->
使用级联操作,设置
cascade=save-update那么在保存一方的同时可以同时保存另一方。
我们在开发中要配置双向关联配置。———可以通过任意一方来操作对方
在操作代码,尽量来要进行单向关联。——可以尽量资源浪费。
在双向关联中,会存在多余的update语句。我们可以使用
inverse属性来设置,双向关联时由哪一方来维护表与表之间的关系。
Inverse它的值如果为true代表,由对方来维护外键。
Inverse它的值如果为false代表,由本方来维护外键。
原则:inverse设置在主表的一方,外键在哪一个表中,我们就让哪一方来维护外键。
对象导航
cascade总结
使用cascade可以完成级联操作
它可常用取值
none这是一个默认值
save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.
delete 级联删除
delete-ophan 删除与当前对象解除关系的对象。
all 它包含了save-update delete操作
all-delete-orphan 它包信了delete-orphan与all操作
cascade与inverse有什么区别
cascade它是完成级联操作
Inverse它只有在双向关联情况下有作用,它来指定由哪一方维护外键。
七、Hibernate注解开发
PO类注解配置@Entity 声明一个实体
@Table来描述类与表对应
@Id来声明一个主键
@GenerateValue 用它来声明一个主键生成策略
默认情况下相当于native
可以选择的主键生成策略 AUTO IDENTITY SEQUENCE
@Column来定义列
注意:对于PO类中所有属性,如果你不写注解,默认情况下也会在表中生成对应的列。
列的名称就是属性的名称
@Temporal来声明日期类型
可以选择
TemporalType.DATA 只有年月日
TemporalType.TIME 只有小时分钟秒
TemporalType.TIMESTAMP 有年月日小时分钟秒
@Transient设定类的属性不在表中映射
@Entity // 定义了一个实体 @Table(name = "t_book", catalog = "hibernateTest") public class Book { @Id // 主键 // @GeneratedValue //native @GeneratedValue(strategy = GenerationType.IDENTITY) // identity private Integer id; // 主键 @Column(name = "c_name", length = 30, nullable = true) private String name; @Temporal(TemporalType.TIMESTAMP) // 是用来定义日期类型 private Date publicationDate; // 出版日期 @Type(type="double") private Double price; // 价格 如果没有添加注解,也会自动的生成在表中 @Transient private String msg; // 现在这个属性不想生成在表中
如果我们主键生成策略想使用UUID类型。
@Id @GenericGenerator(name = "myuuid", strategy = "uuid") @GeneratedValue(generator = "myuuid") private String id;
对于我们以上讲解的关于属性配置的注解,我们也可以在其对应的getXxx方法去使用。我们最终需要在hibernate.cfg.xml文件中将我们类中的注解配置引用生效。
<mapping class="cn.itheima.oneToMany.Customer" /> <mapping class="cn.itheima.oneToMany.Order" />
一对多(多对一)
// 描述客户可以有多个订单 /* * targetEntity相当于<one-to-many class=""> * mappedBy相当于inverse=true */ @OneToMany(targetEntity=Order.class,mappedBy="c",orphanRemoval=true) @Cascade(org.hibernate.annotations.CascadeType.DELETE) private Set<Order> orders = new HashSet<Order>(); // 订单与客户关联 @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "c_customer_id") // 指定外键列 @Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE) private Customer c; // 描述订单属于某一个客户
对于这个示例我们需要在Customer中配置cascade操作,save-update
第一种方式,可以使用JPA提供的注解。在@OneToMany中
cascade=CascadeType.ALL
第二种方式:可以使用hibernate提供的注解.如上代码。
扩展:关于hibernate注解@Cascade中的DELETE_ORPHAN过时,我们在@OneToMany中添加
orphanRemoval=true解决
Hibernate关联映射-多对多
使用@ManyToMany来配置多对多,只需要在一端配置中间表,另一端使用mappedBy表示放置外键维护权。创建PO类
描述学生与老师.
@ManyToMany(targetEntity = Student.class, mappedBy = "teachers") // 代表由对方来维护外键 @Cascade(CascadeType.ALL) private Set<Student> students = new HashSet<Student>(); @ManyToMany(targetEntity = Teacher.class) // 使用JoinTabl来描述中间表,并描述中间表中外键与Student,Teacher的映射关系 // joinColumns它是用来描述Student与中间表中的映射关系 // inverseJoinColums它是用来描述Teacher与中间表中的映射关系 @JoinTable(name = "s_t", joinColumns = { @JoinColumn(name = "c_student_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "c_teacher_id", referencedColumnName = "id") }) @Cascade(CascadeType.ALL) private Set<Teacher> teachers = new HashSet<Teacher>();
Hibernate关联映射-一对一
一对一操作有两种映射方式:1. 在任意一方添加外键
2. 主键映射
创建PO类
以人与身份证号为例
@OneToOne(targetEntity = IDCard.class, mappedBy = "user") private IDCard idCard; @OneToOne(/*targetEntity = User.class*/) @JoinColumn(name = "c_user_id") @Cascade(CascadeType.SAVE_UPDATE) private User user;
八、Hibernate检索方式概述
对数据库操作中,最常用的是select.使用hibernate如何select操作。导航对象图检索方式,根据已加载的对象导航到其它对象
OID检索方式,按照对象的OID来检索对象
HQL检索方式,使用面向对象的HQL查询语言
QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
本地SQL检索方式,使用本地数据库的SQL查询语句
导航对象图检索方式
Customer c=session.get(Customer.class,2);c.getOrders().size()
通过在hibernate中进行映射关系,在hibernate操作时,可以通过导航方式得到
其关联的持久化对象信息。
OID检索方式
Session.get(Customer.class,3);Session.load(Order.class,1);
Hibernate中通过get/load方法查询指定的对象,要通过OID来查询。
HQL
HQL是我们在hibernate中是常用的一种检索方式。HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力
因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语 句的查询方式,同时也提供了更加面向对象的封装。完整的HQL语句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。
基本步骤:
1. 得到Session
2. 编写HQL语句
3. 通过session.createQuery(hql)创建一个Query对象
4. 为Query对象设置条件参数
5. 执行list查询所有,它反胃的是List集合 uniqueResut()返回一个查询结果。
PO类
@Entity @Table(name = "t_customer") @NamedQuery(name = "myHql", query = "from Customer") @SqlResultSetMapping(name = "customerSetMapping", entities = { @EntityResult(entityClass = Customer.class, fields = { @FieldResult(name = "id", column = "id"), @FieldResult(name = "name", column = "name") }) }) @NamedNativeQuery(name = "findCustomer", query = "select * from t_customer", resultSetMapping = "customerSetMapping") @Proxy(lazy = true) public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 主键 private String name; // 姓名 @OneToMany(targetEntity = Order.class, mappedBy = "c") private Set<Order> orders = new HashSet<Order>(); @Entity @Table(name = "t_order") @NamedQuery(name="findOrderByCustomer",query="from Order where c=:c") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Double money; private String receiverInfo; // 收货地址 // 订单与客户关联 @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "c_customer_id") @Cascade(CascadeType.SAVE_UPDATE) private Customer c;
public class HQLTest { // 命名查询 @Test public void test9() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.我要查询张龙这个客户的订单 Customer c = session.get(Customer.class, 1); Query query = session.getNamedQuery("findOrderByCustomer"); // from Order where c=:c // 2.现在hql它的参数是一个实体 List<Order> list = query.setEntity("c", c).list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 命名查询 @Test public void test8() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Query query = session.getNamedQuery("myHql"); List<Customer> list = query.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 投影查询 @Test public void test7() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.查询出所有的Customer的name // String hql = "select name from Customer"; // List list = session.createQuery(hql).list(); // System.out.println(list); // [张龙, 张三丰] // 如果只查询一个列,得到的结果List<Object> // 2.查询所有的Customer的id,name // String hql = "select id,name from Customer"; // List<Object[]> list = session.createQuery(hql).list(); // for(Object[] objs:list){ // for(Object obj:objs){ // System.out.print(obj+" "); // } // System.out.println(); // } // 如果是查询多列,得到的结果是List<Object[]> // 3.使用投影将查询的结果封装到Customer对象 String hql = "select new Customer(id,name) from Customer"; // 必须在PO类中提供对应的构造方法 List<Customer> cs = session.createQuery(hql).list(); System.out.println(cs); session.getTransaction().commit(); session.close(); } // 分组统计操作 @Test public void test6() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 统计操作----统计一共有多少订单 count // String hql="select count(*) from Order"; // Object count = session.createQuery(hql).uniqueResult(); // System.out.println(count); // 分组统计----每一个人的订单总价 String hql = "select sum(money) from Order group by c"; List list = session.createQuery(hql).list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 分页检索 @Test public void test5() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Query query = session.createQuery("from Order"); // 每页显示6条件 ,我们要得到第二页数据 query.setFirstResult((2 - 1) * 6); // 设定开始位置 query.setMaxResults(6); // 设置条数 List<Order> list = query.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 条件查询 @Test public void test4() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.根据位置来绑定参数 // 1.1创建hql // String hql = "from Order where money>? "; // 1.2.执行hql // List<Order> list = session.createQuery(hql).setParameter(0, // 2000d).list(); // 可以使用例如setString() setDouble这样的方法去添加参数,参数的序号是从0开始. // 2.根据名称来绑定 // 1.1创建hql String hql = "from Order where money>:mymoney "; // 1.2.执行hql List<Order> list = session.createQuery(hql).setParameter("mymoney", 2000d).list(); // 可以使用例如setString() setDouble这样的方法去添加参数 System.out.println(list); session.getTransaction().commit(); session.close(); } // 排序检索--//查询订单,根据订单的价格进行排序 @Test public void test3() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.定义hql String hql = "from Order order by money desc"; // desc 降序 默认是asc 升序 // 2.执行hql查询订单,根据价格进行排序 List<Order> list = session.createQuery(hql).list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 基本检索 @Test public void test2() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.编写HQL String hql = "from Customer"; // from是关键字,后面是类名,关键字是不区分大小写,但是类名是区分 // 2.通过session.createQuery(hql) // Query query = session.createQuery(hql); // 3.通过list方法得到数据 // List<Customer> list = query.list(); List<Customer> list = session.createQuery(hql).list(); System.out.println(list.get(0)); session.getTransaction().commit(); session.close(); } // 准备数据(2个Customer,每一个Customer有10个Order) @Test public void test1() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 操作 Customer c = new Customer(); c.setName("张三丰"); for (int i = 0; i < 10; i++) { Order order = new Order(); order.setMoney(2000d + i * 10); order.setReceiverInfo("上海"); order.setC(c); session.save(order); } session.getTransaction().commit(); session.close(); } }
QBC
QBC(query by criteria),它是一种更加面向对象的检索方式。QBC步骤
通过Session得到一个Criteria对象 session.createCriteria()
设定条件 Criterion实例 它的获取可以通过Restrictions类提供静态。
Criteria的add方法用于添加查询条件
调用list进行查询 criterfia.list.
public class QBCTest { // 离线的检索 @Test public void test6() { // 1.得到一个DetachedCriteria DetachedCriteria dc = DetachedCriteria.forClass(Customer.class); dc.add(Restrictions.like("name", "张_")); // 2.生成Criteria执行操作 Session session = HibernateUtils.openSession(); session.beginTransaction(); Criteria criteria = dc.getExecutableCriteria(session); List<Customer> list = criteria.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 统计检索 @Test public void test5() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.统计订单总数 Criteria criteria = session.createCriteria(Order.class); // Object obj = // criteria.setProjection(Projections.rowCount()).uniqueResult(); // //统计总行数 count(id) // System.out.println(obj); // 2.订单的总价格----分组统计根据客户 // criteria.setProjection(Projections.sum("money")); //统计总金额 criteria.setProjection( Projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c"))); List<Object[]> list = criteria.list(); // 在这个集合中保存的是Object[money的统计信息,客户信息] for (Object[] objs : list) { for (Object obj : objs) { System.out.println(obj); } } session.getTransaction().commit(); session.close(); } // 分页检索 @Test public void test4() { Session session = HibernateUtils.openSession(); session.beginTransaction(); Criteria criteria = session.createCriteria(Order.class); criteria.setFirstResult((2 - 1) * 6); criteria.setMaxResults(6); List<Order> list = criteria.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 条件检索 @Test public void test3() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.查询名称叫张某客户 张_ Criteria criteria = session.createCriteria(Customer.class); Criterion like = Restrictions.like("name", "张_"); // 其它的条件 lt < gt > le // <= ge>= eq== criteria.add(like);// 添加条件 Customer c = (Customer) criteria.uniqueResult(); System.out.println(c); // 2.查询订单价格在1050以上的,并且它的客户是张某 Criteria cri = session.createCriteria(Order.class); SimpleExpression lt = Restrictions.gt("money", 1050d); // >1050 SimpleExpression eq = Restrictions.eq("c", c); // 与c客户相同 LogicalExpression and = Restrictions.and(lt, eq); cri.add(and); // List<Order> orders = // cri.add(Restrictions.and(Restrictions.gt("money", 1050d), // Restrictions.eq("c", c))).list(); List<Order> orders = cri.list(); System.out.println(orders); session.getTransaction().commit(); session.close(); } // 排序检索 @Test public void test2() { // 查询订单信息 根据订单的价格进行排序 Session session = HibernateUtils.openSession(); session.beginTransaction(); Criteria criteria = session.createCriteria(Order.class); // 指定排序 // criteria.addOrder(org.hibernate.criterion.Order.desc("money")); // 降序 criteria.addOrder(org.hibernate.criterion.Order.asc("money")); // 升序 List<Order> list = criteria.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } // 基本检索 @Test public void test1() { // 查询所有Customer Session session = HibernateUtils.openSession(); session.beginTransaction(); // 1.得到一个Criteria对象 Criteria criteria = session.createCriteria(Customer.class); // 2.调用list方法 List<Customer> list = criteria.list(); System.out.println(list); session.getTransaction().commit(); session.close(); } }
本地SQL
//测试本地sql命名查询 @Test public void test2(){ Session session = HibernateUtils.openSession(); session.beginTransaction(); Query query = session.getNamedQuery("findCustomer"); List list = query.list(); System.out.println(list); session.getTransaction().commit(); session.close(); }
多表操作
HQL多表操作Hql多表操作分类
交叉连接
内连接
a) 显示内连接
b) 隐式内连接
c) 迫切内连接
外连接
左外连接
迫切左外连接
右外连接
注意:在hibernate中有迫切连接的概念,而sql中没有。
//hql多表查询 public class HQLJoinTest { // 演示迫切左外连接 @Test public void test5() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 注意:fetch不可以与单独条件的with一起使用 List<Customer> list = session .createQuery("select distinct c from Customer c left outer join fetch c.orders where c.id=1").list(); // 左外连接 for (Customer c : list) { System.out.println(c); } session.getTransaction().commit(); session.close(); } // 演示外连接 @Test public void test4() { Session session = HibernateUtils.openSession(); session.beginTransaction(); List<Object[]> list = session.createQuery("from Customer c left outer join c.orders").list(); // 左外连接 for (Object[] objs : list) { for (Object obj : objs) { System.out.print(obj + "\t"); } System.out.println(); } session.getTransaction().commit(); session.close(); } // 迫切内连接 @Test public void test3() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // 迫切内连接 inner join fetch 注意:使用迫切连接结果可能出现重复,所以要使用distinct来去重复 String hql = "select distinct c from Customer c inner join fetch c.orders"; // 底层也是执行的inner join 只不过结果封装到对象中。 Query query = session.createQuery(hql); List<Customer> list = query.list(); // 结果是List<>,集合中装入的From后面的对象。 for (Customer o : list) { System.out.println(o); } session.getTransaction().commit(); session.close(); } // 测试隐式内连接 @Test public void test2() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // sql连接 select * from t_customer,t_order where 条件 String hql = "from Order o where o.c.id=1"; Query query = session.createQuery(hql); List list = query.list(); System.out.println(list); /* String hql = "from Customer c,Order o where c.id=1 and c.id = o.customer.id"; Query query = session.createQuery(hql); List<Object[]> list = query.list(); for (Object[] objs : list) { for (Object obj : objs) { System.out.print(obj + "\t"); } System.out.println(); } */ session.getTransaction().commit(); session.close(); } // 测试显示内连接 @Test public void test1() { Session session = HibernateUtils.openSession(); session.beginTransaction(); // sql连接 select * from t_customer inner join t_order on 条件 String hql = "from Customer c inner join c.orders with c.id=1"; Query query = session.createQuery(hql); List<Object[]> list = query.list(); // 结果是List<Object[]> // 而Object[]中装入的是Customer与Order对象。 for (Object[] objs : list) { for (Object obj : objs) { System.out.print(obj + "\t"); } System.out.println(); } session.getTransaction().commit(); session.close(); } }
九、Hibernate事务管理
Hibernate中设置事务隔离级别在hibernate.cfg.xml文件中配置
<!-- 设置事务隔离级别 --> <property name="hibernate.connection.isolation ">4</property>
它可取的值有 1 2 4 8
1.代表的事务隔离级别为READ UNCOMMITTED
2.代表的事务隔离级别为READ COMMITTED
4.代表的事务隔离级别为 REPEATABLE READ
8.代表的事务隔离级别为 SERIALIZABLE
Hibernate中session管理
Hibernate提供了三种管理session的方式,在实际开发中我们一般使用的是前两种。
Session对象的生命周期与本地线程绑定(ThreadLocal)
Session对象的生命周期与JTA事务绑定(分布式事务管理)
Hibernate委托程序来管理Session的生命周期
本地线程绑定Session
需要在hibernate.cfg.xml文件配置
在获取session时不要在使用openSession而是使用getCurrentSession()方法。
<property name="hibernate.current_session_context_class">thread</property> Session currentSession = sessionFactory.getCurrentSession();
使用getCurrentSession获取的与线程绑定的session对象,在事务关闭时,session对象也会close,简单说,就不需要我们在手动close。
十、Hibernate优化方案
HQL优化
1.使用参数绑定2.尽量少使用NOT
3.尽量使用where来替换having
4.减少对表的查询
5.使用表的别名
6.实体的更新与删除
检索策略
延迟加载延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
load方法采用的策略延迟加载、
get方法采用的策略立即加载。
检索策略分为两种:
1. 类级别检索
2. 关联级别检索
类级别检索
类级别检索是通过session直接检索某一类对应的数据,例如
Customer c=session.load(Customer.class,1)
Session.createQuery(“from Order”)
类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过
<class>元素的lazy属性来设置 ,默认值是true。如果将lazy设置为false,代表类级别检索也使用立即检索。这时load与get就一样,都是立即检索。
关联级别检索
查询到某个对象,获得其关联的对象或属性,这种称为关联级别检索,例如
c.getOrders().size()
c.getName()
对于关联级别检索我们就要研究其检索策略(抓取策略)
抓取策略
抓取策略介绍指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。
注解配置抓取策略
@Entity @Table(name = "t_customer") @Proxy(lazy = false) @BatchSize(size=3) public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; // 主键 private String name; // 姓名 @OneToMany(targetEntity = Order.class, mappedBy = "c") @Fetch(FetchMode.SELECT) @LazyCollection(LazyCollectionOption.TRUE) @BatchSize(size=3) private Set<Order> orders = new HashSet<Order>(); @Entity @Table(name = "t_order") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Double money; private String receiverInfo; // 收货地址 // 订单与客户关联 @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "c_customer_id") @Cascade(CascadeType.SAVE_UPDATE) @Fetch(FetchMode.JOIN) @LazyToOne(LazyToOneOption.FALSE) private Customer c;
set上的fetch与lazy
set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。根据一的一方去查询多的一方
Fetch可取值有:
SELECT 多条简单的sql (默认值)
JOIN 采用迫切左外连接。如果fetch选择的是join方案,那么lazy它会失效。
SUBSELECT 将生成子查询的SQL
lazy可取值有:
TURE 延迟检索 (默认值)
FALSE 立即检索
EXTRA 加强延迟检索(及其懒惰)
One的上的fetch与lazy
在多的一方如何查询一的主方信息。例如:获取到一个订单对象,要查询客户信息。
Fetch可取值
select 默认值,代表发送一条或多条简单的select语句
join 发送一条迫切左外连接。如果fetch值为join,那么lazy失效。
lazy可取值
false 不采用延迟加载
proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定
no-proxy 不用研究
批量抓取
我们在查询多个对象的关联对象时,可以采用批量抓取方式来对程序进行优化。
要想实现批量抓取,可以在配置文件中 batch-size属性来设置 ,可以使用注解 @BatchSize(size=4)。
无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置,如果是要查询子信息,那么我们是在上来设置batch-size,如果是从子方来查询父方, 也是在父方设置在设置batch-size。
相关文章推荐
- Hibernate(5.2.10)快速入门 (一) 初识Hibernate、框架搭建、xml基本配置
- JAVA框架三剑客 - Struts+Spring+Hibernate快速入门
- Hibernate框架快速入门
- 流行Java开源数据库框架项目 - Hibernate快速简单入门
- Hibernate框架快速入门
- 架构师入门笔记七 并发框架Disruptor快速入门
- Hibernate框架入门(学习笔记)
- 【菜鸟学框架】——hibernate入门Demo
- Eclipse快速上手Hibernate--1. 入门实例
- 《Java从入门到放弃》框架入门篇:hibernate基本配置
- 【SSH】Struts2(2.5.14)、Spring(5.0.3)、Hibernate(5.2.13)三大框架的整合项目入门示例
- Hibernate框架第一天(入门)
- Hibernate入门笔记 1, 2
- Struts+Spring+Hibernate快速入门2(转)
- Eclipse快速上手Hibernate之入门实例
- Dojo QuickStart 快速入门教程 (4) 简单的测试框架
- Quartz 框架快速入门(四)
- Entity Framework快速入门笔记—增删改查
- Spring学习笔记(3)----------Spring快速入门
- 架构师入门笔记八 并发框架Disruptor场景应用