Hiberante的了解
框架的概述
框架(framework)是一个框子——指其约束性,也是一个架子——指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。
什么是框架
框架:指的是软件的半成品,已经完成了部分功能。
EE的三层架构
EE的经典三层结构
Hibernate的概述
Hibernate 是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。
什么是Hibernate
Hibernate是一种ORM框架,在Java对象与关系数据库之间建立某种映射,以实现直接存取Java对象!
什么是ORM
ORM:Object Relational Mapping(对象关系映射)。指的是将一个Java中的对象与关系型数据库中的表建立一种映射关系,从而操作对象就可以操作数据库中的表。
为什么要学习Hibernate
与其他操作数据库的技术相比,Hibernate具有以下几点优势
- Hibernate对 JBDC 访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
- Hibernate 是一个基于 JDBC 的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化 DAO(Date Access Object,数据访问对象)层编码工作。
- Hibernate 的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
- 可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。
Hibernate的入门
下载Hibernate开发环境
Hibernate3.x Hibernate4.x Hibernate5.x
https://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/
解压 Hibernate
解压完成会看到三个文件夹:
- documentation :Hibernate开发的文档
- lib :Hibernate开发包
- required :Hibernate开发的必须的依赖包
- optional :Hibernate开发的可选的jar包
- project :Hibernate提供的项目
创建一个项目,引入jar包
- 数据库驱动包
- Hibernate开发的必须的jar包
- Hibernate引入日志记录包
创建表
1 | CREATE TABLE `cst_customer` ( |
创建实体类
1 | public class Customer { |
创建映射
映射需要通过XML的配置文件来完成,这个配置文件可以任意命名。尽量统一命名规范(类名.hbm.xml)
1 |
|
创建一个Hibernate的核心配置文件
Hibernate的核心配置文件的名称:hibernate.cfg.xml
1 |
|
编写测试代码
1 | public class HibernateDemo1 { |
Hibernate的常见配置
Hibernate的映射的配置
映射的配置
【class标签的配置】
- 标签用来建立类与表的映射关系
- 属性:
- name :类的全路径
- table :表名(类名与表名一致,table可以省略)
- catalog :数据库名
【id标签的配置】
- 标签用来建立类中的属性与表中的主键的对应关系
- 属性:
- name :类中的属性名
- column :表中的字段名(类中的属性名和表中的字段名如果一致,column可以省略)
- length :长度
- type :类型
【property标签的配置】
- 标签用来建立类中的普通属性与表的字段的对应关系
- 属性:
- name :类中的属性名
- column :表中的字段名
- length :长度
- type :类型
- not-null :设置非空
- unique :设置唯一
Hibernate的核心的配置
Hibernate的核心配置方式(了解)
一种方式:属性文件的方式
- hibernate.properties
- hibernate.connection.driver_class=com.mysql.jdbc.Driver
- …
- hibernate.show_sql=true
- 属性文件的方式不能引入映射文件(手动编写代码加载映射文件)
- hibernate.properties
二种方式:XML文件的方式
- hibernate.cfg.xml
核心的配置
- 必须的配置
- 连接数据库的基本的参数
- 驱动类
- url路径
- 用户名
- 密码
- 方言
- 连接数据库的基本的参数
可选的配置
- 显示SQL :hibernate.show_sql
- 格式化SQL :hibernate.format_sql
- 自动建表 :hibernate.hbm2ddl.auto
- none :不使用hibernate的自动建表
- create :如果数据库中已经有表,删除原有表,重新创建,如果没有表,新建表。(测试)
- create-drop :如果数据库中已经有表,删除原有表,执行操作,删除这个表。如果没有表,新建一个,使用完了删除该表。(测试)
- update :如果数据库中有表,使用原有表,如果没有表,创建新表(更新表结构)
- validate :如果没有表,不会创建表。只会使用数据库中原有的表。(校验映射和表结构)。
映射文件的引入
- 引入映射文件的位置
Hibernate的核心API
Hibernate的API
Configuration:Hibernate的配置对象
Configuration
configuration类的作用是对Hibernate进行配置,以及对它进行启动,在启动过程中,Configuration类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象,虽然Configuration类在整个Hibernate项目中只扮演着一个很小的角色,但它是启动hibernate时所遇到的第一个对象
- 作用:
- 加载核心配置文件
- hibernate.properties
Configuration cfg = new Configuration();
- hibernate.cfg.xml
Configuration cfg = new Configuration().configure();
- hibernate.properties
- 加载映射文件
1
2// 手动加载映射
configuration.addResource("com/itheima/hibernate/demo1/ Customer.hbm.xml");
- 加载核心配置文件
SessionFactory:Session工厂
SessionFactory
SessionFactory接口负责初始化Hibernate,它充当数据存储源的代理,并负责创建Session对象,这里用到了工厂模式。需要注意的事SessionFactory并不是轻量级的,因为在一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个库指定一个SessionFactory。
SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存。是线程安全的对象。一个项目创建一个对象即可。
配置连接池:(了解)
1
2
3
4
5
6
7
8
9
10
11<!-- 配置C3P0连接池 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>抽取工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class HibernateUtils {
public static final Configuration cfg;
public static final SessionFactory sf;
static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}
public static Session openSession(){
return sf.openSession();
}
}
Session:类似Connection对象是连接对象
Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含很多藏剑的SQL语句)。但需要注意的事Session对象是非线程安全的。同时,Hibernate的Session不同于JSP应用中的HttpSession。这里当使用Session这个术语时,其实指的是Hibernate中的Session,而以后会将HttpSession对象称为用户Session。
Session代表的是Hibernate与数据库的链接对象。不是线程安全的。与数据库交互桥梁。
- Session中的API
- 保存方法:
- Serializable save(Object obj);
查询方法:
- T get(Class c,Serializable id);
- T load(Class c,Serializable id);
- get方法和load方法的区别?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public void demo2(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
/**
* get方法
* * 采用的是立即加载,执行到这行代码的时候,就会马上发送SQL语句去查询。
* * 查询后返回是真实对象本身。
* * 查询一个找不到的对象的时候,返回null
*
* load方法
* * 采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句。
* * 查询后返回的是代理对象。javassist-3.18.1-GA.jar 利用javassist技术产生的代理。
* * 查询一个找不到的对象的时候,返回ObjectNotFoundException
*/
// 使用get方法查询
/*Customer customer = session.get(Customer.class, 100l); // 发送SQL语句
System.out.println(customer);*/
// 使用load方法查询
Customer customer = session.load(Customer.class, 200l);
System.out.println(customer);
tx.commit();
session.close();
}
修改方法
- void update(Object obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public void demo3(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 直接创建对象,进行修改
/*Customer customer = new Customer();
customer.setCust_id(1l);
customer.setCust_name("王聪");
session.update(customer);*/
// 先查询,再修改(推荐)
Customer customer = session.get(Customer.class, 1l);
customer.setCust_name("王小贱");
session.update(customer);
tx.commit();
session.close();
}
- void update(Object obj);
删除方法
- void delete(Object obj);
1 | // 删除操作 |
- 保存或更新
- void saveOrUpdate(Object obj)
1 | public void demo5(){ |
- 查询所有
1 | // 查询所有 |
Transaction:事务对象
Hibernate中管理事务的对象。
- commit();
- rollback();
主键生成策略&一级缓存&事物管理
Hibernate持久化类的编写规则
什么是持久化类
Hibernate 是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。那么知道了什么是持久化,什么又是持久化类呢?其实所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为持久化类。其实你可以简单的理解为持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。
持久化类的编写规则
我们在编写持久化类的时候需要有以下几点需要注意:
- 持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
- 持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。
- 持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。
- 持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中式通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个ODI相同的持久化对象的。
- 持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么久不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。
Hibernate主键生成策略
主键的类型
在讲解Hibernate的逐渐生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:
- 自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把那么字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管着也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。
- 代理主键:把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。
Hibernate的主键生成策略:
Hibernate中,提供了几个内置的主键生成策略,其常用主键生成策略的名称和描述如下
名称 | 描述 |
---|---|
increment | 用于long、short、或int类型,由Hibernate自动以递增的方式生成唯一标识符,每次增量为1。只有当没有用其它进程想同一张表中插入数据时才可以使用,不能在集群环境下使用。适用于代理主键。 |
identity | 采用底层数据库本身提供的主键生成标识符,条件是数据库支持自动增长数据类型。在DB2、MySQL、MS SQL Server、Sybase和HypersonicSQL数据库中可以使用该生成器,该生成器要求在数据库中把主键定义成为自增长类型。适用于代理主键。 |
sequence | Hiberante根据底层数据库序列生成标识符。条件是数据库支持序列。适用于代理主键。 |
native | 根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo三种生成器中的一种,适合跨数据库平台开发。适用于代理主键。 |
uuid | Hibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符,其UUID被编码为一个长度为32为的十六进制字符串。这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。适用于代理主键。 |
assigned | 由java程序负责生成标识符,如果不指定id元素的generator属性,则默认使用该主键策略。适用于自然主键。 |
Hibernate的持久化对象的三种状态
持久化对象三种状态的概述
了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态详细介绍如下。
- 瞬时态(transient)也被称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hiberante Session关联,在数据库中也米有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中式孤立存在的,与数据库中的数据无如何关联,仅是一个信息携带的载体。
- 持久态(persistent)的对象存在持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
- 脱管态(detached)也被称为离线态或者游离态,当某个持久化状态的实例与Session的管理被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。
区分对象的三种状态
为了帮助大家更好的理解持久化对象的三种状态,接下来通过具体的案例来演示持久化对象的三种状态。
1 | //测试对象的三种状态 |
c
对象由new
关键字创建,此时还未与Session进行关联,它的状态称为瞬时态;在执行了session.save(c)
操作后,c
对象纳入了Session的管理范围,这时的 c
对象变成了持久态对象,此时Session的事务还没有被提交;程序执行完commit()操作并关闭了Session后,c
对象与Session的关联被关闭,此时 c
对象就变成脱管态。
Hibernate持久化对象的三种状态转换
从图中可以看出,当一个对象被执行new
关键字创建后,该对象处于瞬时态;当对瞬时态对象执行Session的save()
或saveOrUpdate()
方法后,该对象将被放入Session的以及缓存,对象进入持久态;当对持久态对象执行evict()
、close()
或clear()
操作后,对象进入托管态;当直接执行Session的get()、load()、find()
或iterate()
等方法从数据库里查询对象时,查询到的对象也处于持久态;当对数据库中的记录进行update()
、saveOrUpdate()
以及lock()
等操作后,此时脱管态的对象就过渡到持久态;由于瞬时态和脱管态的对象不在session的管理范围,所以会在一段时间后被JVM回收。
持久化对象的三种状态可以通过调用Session中的一系列方法实现状态间的转换,具体如下:
瞬时态转换到其他状态
通过前面学习可知,瞬时态的对象由new关键字创建,瞬时态对象转换到其他状态总结如下:
- 瞬间态转换为持久态:执行Session的save()或saveOrUpdate()方法。
- 瞬时态转换为脱管态:为瞬时态对象设置持久化标识OID。
由于持久化对象状态演化图中没有涉及到瞬时态转换到脱管态的情况,这里做下简要的说明,在前面学习中可知,脱管态对象窜OID,但是没有Session的关联,也就是说脱管态和瞬时态的区别就是OID有没有值,所以可以通过为瞬时态对象设置OID,使其变成脱管态对象。
1
2Customer customer = new Customer(); //瞬时态
customer.setCust_id(1); //脱管态持久态对象转换到其他状态
持久化对象可以直接通过Hibernate中Session的
get()、load()
方法,或者Query查询从数据库中获得,持久态对象转换到其他的状态总结如下:- 持久态转换为瞬时态:执行Session的
delete()
方法,需要注意的是被删除的持久化对象,不建议再次使用 - 持久态转换为脱管态:执行Session的
evict()、close()
或clear()
方法。evict()
方法用于清除异己缓存的所有对象。
- 持久态转换为瞬时态:执行Session的
脱管态对象转换到其他状态
脱管态对象无法直接获得,是由其他状态对象转换而来的,脱管态对象转换到其他状态总结如下:
脱管态转换为持久态:执行Session的update()、saveOrUpdate()或lock()方法。
脱管态转换为瞬时态:将脱管态对象的持久化标识OID设置为null。
由于持久化对象状态演化图中没有涉及到脱管态换到瞬时态的情况,这里做下简要的说明,跟瞬时态转换到脱管态的情况相似,脱管态和瞬时态的区别就是OID有没有值,所以可以通过将脱管对象的OID设置为null,使其变成瞬时态对象。例如在session.close()操作后,加入代码customer.setCust_id(null);,customer对象将由脱管态转换为瞬时态。
持久态对象能够自动更新数据库
我们已经持久化对象的三种状态了,其实我们主要去研究持久态对象就够了,持久态对象其实有一个非常重要的特性:持久态对象可以自动更新数据库。
1 | // 持久化状态特点: 持久化状态对象的任何变化都会自动同步到数据库中. |
执行测试我们会发现,我们并没有手动调用update方法,Hibernate就可以将数据自动更新了。持久态对象就有这样的一个功能。持久态对象之所以有这样的功能其实都依赖了Hibernate的一级缓存。接下来我们就开始学习Hibernate缓存。
Hibernate的一级缓存
缓存是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。
Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。接下来围绕Hibernate的一级缓存进行详细地讲解。
什么是Hibernate的一级缓存
Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就知己将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数。
在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为是Session基本的缓存。
Hibernate的一级缓存有如下特点:
- 当应用程序调用Session接口的
save()
、update()
、saveOrUpdate
,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。 - 当调用Session接口的
load()
、get()
方法,一级Query接口的list()
、iterator()
方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,在去数据库中查询对应对象,并添加到一级缓存中。 - 当调用Session的close()方法时,Session缓存会被清空。
测试一级缓存
我们已经大致了解什么是一级缓存,那么一级缓存具体是否存在呢,我们可以通过如下的程序来证明以及缓存是存在的。
1 |
|
在以上代码中,第一次执行Session的get()
方法获取customer1对象时,由于一级缓存中没有数据,所以Hibernate会向数据库发送一条sql语句,查询id等于1的对象;当再次调用了session的get()方法获取customer2对象时,将不会再发送sql语句,这是因为customer2对象是从一级缓存中获取的。
一级缓存的内部结构:(快照区)
Hibernate向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。
Hibernate 的事务控制
Hibernate是对JDBC的轻量级封装,其主要功能是操作数据库。在操作数据库过程中,经常会遇到事务处理的问题,那么我们接下来就来介绍Hibernate中的事务管理。
什么是事务
在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元。当食物中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务都会被回滚。
事务的四个特性
事务有很严格的定义,需要同时满足四个特性,即原子性、一致性、隔离性、持久性。这四个特性通常称之为ACID特性,具体如下:
- 原子性(Atomic): 表示将事务中所做的操作捆绑成一个不可分割的单元,即对事务所进行的数据修改等操作,要么全部执行,要么全都不执行。
- 一致性(Consistency): 表示事务完成时,必须使所有的数据都保持一致状态。
- 隔离性(Isolation): 指一个事物的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离性的,并发执行的各个事务之间不能相互干扰。
- 持久性(Durability): 持久性也称为永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。提交后的其他操作或故障不会对其有任何影响。
事务的并发问题
在实际应用过程中,数据库是要被多个用户所共同访问的。在多个事务同时使用相同的数据时,可能会发生并发的问题,具体如下。
- 脏读:一个事务读取到另一个事务未提交的数据。
- 不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在同一个事务中的多次查询结果不一致。
- 虚读/幻读:一个事务读到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致
事务的隔离级别
为了避免事务并发问题的发生,在标准SQL规范中,定义了4个事务隔离级别,不同隔离级别对事务的处理不同。
- 读未提交(ReadUncommitted,1级):一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。
- 已提交读(Read Committed,2级):一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。
- 可重复读(Repeatable Read,4级):一个事务在执行过程中,可以访问其他事务成功提交的新插入数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务,此隔离级别可有效的防止不可重复读和脏读。
- 序列化/串行化(Serializable,8级):提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。此隔离级别可有效的防止脏读、不可重复读和幻读。
隔离级别 | 含义 |
---|---|
READ_UNCOMMITTED | 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 |
READ_COMMITED | 允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生 |
REPEATABLE_READ | 对相同字段的多次读取是一致的,除非数据被事务本身改变。可以防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的 |
事务的隔离界别,是由数据库提供的,并不是所有数据库都支持四种隔离级别
- MySQL:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE(默认 REPEATABLE_READ)
- Oracle:READ_UNCOMMITTED、READ_COMMITTED、SERIALIZABLE(默认 READ_COMMITTED)
在使用数据库时候,隔离级别越高,安全性越高,性能越低。
实际开发中,不会选择最高或者最低隔离级别,选择READ_COMMITTED(Oracle 默认)、REPEATABLE_READ(Mysql 默认)
Hibernate 中的事务管理
在 Hibernate 中,可以通过代码来操作管理实务,如通过 Transaction tx = session.beginTransaction();
开启一个事务;持久化操作后,通过 tx.commit();
提交事务;如果食物出现异常,又通过tx.rollback();
操作来撤销事务(事务回滚)。
除了在代码块中队事务开启,提交和回滚操作外,还可以在 Hibernate 的配置文件中队事务进行配置。配置文件中,可以设置事务的隔离级别。其具体的配置方法是在 hibernate.cfg.xml 文件中的<session-factory>
标签元素中进行的。配置方法如下所示。
1 | <!-- 引入orm元数据 |
到这我们已经设置了事务的隔离级别,那么我们在真正进行事务管理的时候,需要考虑事务的应用的场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个Dao实现一个业务逻辑的操作。具体操作如下显示:
其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操作使用的是同一个Session对象。
其实有两个方法可以实现:
- 可以在业务层获取到Session,并将Session作为参数传递给DAO。
- 可以使用ThreadLocal将业务层获取的Session绑定到当前线程中,然后再DAO中获取Session的时候,都从当前线程中获取。
其实使用第二种方式肯定是最优方案,那么具体的实现已经不用我们来完成了,Hibernate的内部已经将这个事情做完了。我们只需要完成一段配置即可。
Hibernate5中自身提供了三种管理Session对象的方法
Session对象的生命周期与本地线程绑定、
Session对象的生命周期与 JTA 事务绑定
Hibernate委托程序管理 Session 对象的生命周期
在 Hibernate 的配置文件中,hibernate.current_session_context_class 属性用于指定 Session 管理方式,可选值包括
- Thread:Session对象的生命周期与本地线程绑定
- jta: Session对象的生命周期与JTA事务绑定
- managed: Hibernate 委托程序来管理 Session 对象的生命周期
在hibernate.cfg.xml中进行如下配置:
1 | <!-- 指定session与当前线程绑定 --> |
hibernate提供sessionFactory.getCurrentSession()创建一个session和ThreadLocal绑定方法。
在HibernateUtil工具类中更改getCurrentSession方法:
1 | //获取当前线程绑定的会话 |
而且Hibernate中提供的这个与线程绑定的 session 可以不用关闭,当线程执行结束后,就会自动关闭了。
Hibernate的其他API
Query
Query代表面向对象的一个 Hibernate 查询操作。在 Hibernate 中,通常使用session.createQuery()方法接受一个HQL语句,然后调用 Query 的list()
或uniqueResult()
方法执行查询。所谓的 HQL 是Hibernate Query Language 缩写,其语法很像 SQL 语法,但它是完全面向对象的。
在 Hibernate中使用 Query对象的步骤,具体所示:
- 获得Hibernate的Session对象。
- 编写HQL语句。
- 调用session.createQuery 创建查询对象。
- 如果HQL语句包含参数,则调用Query的 setXxx 设置参数。
- 调用 Query 对象的
list()
或uniqueResult()
方法执行查询。
了解了使用 Query 对象的步骤后,接下来,通过具体示例来演示 Query 对象的查询操作。
1 | //测试HQL语句 |
Query中出来使用list()
方法查询全部数据外,还有其它一些常用方法,具体如下:
- setter方法: Query接口中提供了一系列的 setter 方法用于设置查询语句的参数,针对不同的数据类型,需要用到不同的setter方法。
- iterator()方法:该方法用于查询语句,返回的结果是一个Iterator对象,在读取时只能按照顺序方式读取,它仅把使用到的数据转换成Java实体对象。
- uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
- executeUpdate()方法:该方法是 Hibernate 的新特性,它支持 HQL 语句的更新和删除操作。
- setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
- setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult() 方法结合使用,用于限制结果集的范围,以实现分页功能。
Criteria
Criteria 是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现,以及SQL语句如何编写,它是Hibernate框架的核心查询对象。Criteria查询,又称为 QBC 查询(Query By Criteria),它是Hibernate的另一种对象检索方式。
org.hibernate.criterion.Criterion是 Hibernate 提供的一个面向对象查询条件接口,一个单独的查询就是 Criterion 接口的一个实例,用于限制 Criteria 对象的查询,在 Hibernate 中 Criterion 对象的创建通常是通过 Restrictions 工厂类完成的,它提供饿了条件查询方法。
通常,使用 Criteria 对象查询数据的主要步骤,具体如下:
- 获得Hibernate的 Session 对象。
- 通过 Session 获得 Criteria 对象。
- 使用 Restrictions 的静态方法创建 Criterion 条件对象。Restrictions 类中提供了一系列用于设定查询条件的静态方法,这些静态方法都返回Criterion 实例,每个 Criterion 实例代表一个查询条件。
- 向 Criteria 对象中添加 Criterion 查询条件。Criteria的 add() 方法用于查询条件。
- 执行 Criterita 的
list()
或 uniqueResult() 获得结果。
了解了 Criteria 对象的使用步骤后,接下来,通过具体示例来演示 Criterria 对象的查询操作。
1 | //测试Criteria查询 |
SQLQuery
SQLQuery 这个就比较简单了,这个接口用于接收一个sql语句进行查询,然后调用 list()
或者 uniqueResult()
方法进行查询。但是 sql 语句不会直接封装到实体对象中,需要我们手动写代码才可以封装到实体中。
1 | //测试原生SQL查询 |
Hibernate的进阶
表操作-多对多配置
数据库表与表之间的关系
一对多关系
什么样关系属于一对多?
- 一个部门对应多个员工,一个员工只能属于某一个部门。
- 一个客户对应多个联系人,一个联系人只能属于某一个客服。
多对多关系
- 什么关系属于多对多?
- 一个学生可以选择多门课程,一门课程也可以被多个学生选择。
- 一个用户可以选择多个角色,一个角色也可以被多个用户选择
一对一关系(了解)
- 什么样关系属于一对一?
- 一个公司只能有一个注册地址,一个注册地址只能被一个公司注册。
Hibernate 一对多的关系配置
创建一个项目,引入相应 jar 包
创建数据库和表
1 | CREATE TABLE `cst_customer` ( |
创建实体
一的一方的实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61public class Customer {
private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_phone;
private String cust_mobile;
// 通过ORM方式表示:一个客户对应多个联系人。
// 放置的多的一方的集合。Hibernate默认使用的是Set集合。
private Set<LinkMan> linkMans = new HashSet<LinkMan>();
public Long getCust_id() {
return cust_id;
}
public void setCust_id(Long cust_id) {
this.cust_id = cust_id;
}
public String getCust_name() {
return cust_name;
}
public void setCust_name(String cust_name) {
this.cust_name = cust_name;
}
public String getCust_source() {
return cust_source;
}
public void setCust_source(String cust_source) {
this.cust_source = cust_source;
}
public String getCust_industry() {
return cust_industry;
}
public void setCust_industry(String cust_industry) {
this.cust_industry = cust_industry;
}
public String getCust_level() {
return cust_level;
}
public void setCust_level(String cust_level) {
this.cust_level = cust_level;
}
public String getCust_phone() {
return cust_phone;
}
public void setCust_phone(String cust_phone) {
this.cust_phone = cust_phone;
}
public String getCust_mobile() {
return cust_mobile;
}
public void setCust_mobile(String cust_mobile) {
this.cust_mobile = cust_mobile;
}
public Set<LinkMan> getLinkMans() {
return linkMans;
}
public void setLinkMans(Set<LinkMan> linkMans) {
this.linkMans = linkMans;
}
}多的一方的实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76public class LinkMan {
private Long lkm_id;
private String lkm_name;
private String lkm_gender;
private String lkm_phone;
private String lkm_mobile;
private String lkm_email;
private String lkm_qq;
private String lkm_position;
private String lkm_memo;
// 通过ORM方式表示:一个联系人只能属于某一个客户。
// 放置的是一的一方的对象。
private Customer customer;
public Long getLkm_id() {
return lkm_id;
}
public void setLkm_id(Long lkm_id) {
this.lkm_id = lkm_id;
}
public String getLkm_name() {
return lkm_name;
}
public void setLkm_name(String lkm_name) {
this.lkm_name = lkm_name;
}
public String getLkm_gender() {
return lkm_gender;
}
public void setLkm_gender(String lkm_gender) {
this.lkm_gender = lkm_gender;
}
public String getLkm_phone() {
return lkm_phone;
}
public void setLkm_phone(String lkm_phone) {
this.lkm_phone = lkm_phone;
}
public String getLkm_mobile() {
return lkm_mobile;
}
public void setLkm_mobile(String lkm_mobile) {
this.lkm_mobile = lkm_mobile;
}
public String getLkm_email() {
return lkm_email;
}
public void setLkm_email(String lkm_email) {
this.lkm_email = lkm_email;
}
public String getLkm_qq() {
return lkm_qq;
}
public void setLkm_qq(String lkm_qq) {
this.lkm_qq = lkm_qq;
}
public String getLkm_position() {
return lkm_position;
}
public void setLkm_position(String lkm_position) {
this.lkm_position = lkm_position;
}
public String getLkm_memo() {
return lkm_memo;
}
public void setLkm_memo(String lkm_memo) {
this.lkm_memo = lkm_memo;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
创建映射文件
多的一方的映射的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<hibernate-mapping>
<class name="com.hibernate.domain.LinkMan" table="cst_linkman">
<!-- 建立OID与主键映射 -->
<id name="lkm_id" column="lkm_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与表字段映射 -->
<property name="lkm_name"/>
<property name="lkm_gender"/>
<property name="lkm_phone"/>
<property name="lkm_mobile"/>
<property name="lkm_email"/>
<property name="lkm_qq"/>
<property name="lkm_position"/>
<property name="lkm_memo"/>
<!-- 配置多对一的关系:放置的是一的一方的对象 -->
<!--
many-to-one标签
* name :一的一方的对象的属性名称。
* class :一的一方的类的全路径。
* column :在多的一方的表的外键的名称。
-->
<many-to-one name="customer" class="com.hibernate.domain.Customer" column="lkm_cust_id"/>
</class>
</hibernate-mapping>一的一方的映射的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<hibernate-mapping>
<class name="com.hibernate.domain.Customer" table="cst_customer">
<!-- 建立OID与主键映射 -->
<id name="cust_id" column="cust_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与数据库表字段映射 -->
<property name="cust_name" column="cust_name" />
<property name="cust_source" column="cust_source"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
<!-- 配置一对多的映射:放置的多的一方的集合 -->
<!--
set标签 :
* name :多的一方的对象集合的属性名称。
* cascade:级联
* inverse:放弃外键维护权。
-->
<set name="linkMans" cascade="save-update" inverse="true">
<!--
key标签
* column:多的一方的外键的名称。
-->
<key column="lkm_cust_id"/>
<!--
one-to-many标签
* class :多的一方的类的全路径
-->
<one-to-many class="com.hibernate.domain.LinkMan"/>
</set>
</class>
</hibernate-mapping>
创建核心配置文件
1 |
|
引入工具类
1 | public class HibernateUtils { |
编写测试类
1 | public class HibernateDemo1 { |
Hibernate 的一对多相关操作
一对多关系只保存一边是否可以
1 |
|
一对多的级联操作
- 什么叫级联
- 级联指的是,操作一个对象的时候,是否会同时操作其关联的对象。
- 级联是有方向性
- 操作一的一方的时候,是否操作到多的一方
- 操作多的一方的时候,是否操作到一的一方
级联保存或更新
保存客户级联联系人
1
2
3
4
5
6
7<!--
set标签 :
* name :多的一方的对象集合的属性名称。
* cascade:级联
* inverse:放弃外键维护权。
-->
<set name="linkMans" cascade="save-update" inverse="true">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 级联保存或更新操作:
* * 保存客户级联联系人,操作的主体是客户对象,需要在Customer.hbm.xml中进行配置
* * <set name="linkMans" cascade="save-update">
*/
public void demo3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCust_name("赵洪");
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("如花");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);
session.save(customer);
tx.commit();
}
- 保存联系人联系人级联客户
1 | <!-- 配置多对一的关系:放置的是一的一方的对象 --> |
1 |
|
测试对象的导航
1 |
|
级联删除
- 级联删除:
- 删除一边的时候,同时将另一方的数据也一并删除。
- 删除客户级联删除联系人
1 |
|
- 删除联系人级联删除客户(基本不用)
1 |
|
一对多设置了双向关联产生多余的SQL语句
1 |
|
解决多余的SQL语句
单向维护:
使一方放弃外键维护权:
- 一的一方放弃。在set上配置inverse=”true”
一对多的关联查询的修改的时候。(CRM练习–)
区分cascade和inverse
1 |
|
Hibernate多对多关系的配置
创建表
用户表
1
2
3
4
5
6
7
8CREATE TABLE `sys_user` (
`user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
`user_code` varchar(32) NOT NULL COMMENT '用户账号',
`user_name` varchar(64) NOT NULL COMMENT '用户名称',
`user_password` varchar(32) NOT NULL COMMENT '用户密码',
`user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;角色表
1
2
3
4
5
6CREATE TABLE `sys_role` (
`role_id` bigint(32) NOT NULL AUTO_INCREMENT,
`role_name` varchar(32) NOT NULL COMMENT '角色名称',
`role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;中间表
1
2
3
4
5
6
7
8CREATE TABLE `sys_user_role` (
`role_id` bigint(32) NOT NULL COMMENT '角色id',
`user_id` bigint(32) NOT NULL COMMENT '用户id',
PRIMARY KEY (`role_id`,`user_id`),
KEY `FK_user_role_user_id` (`user_id`),
CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
创建实体
- 用户的实体
1 | public class User { |
- 角色的实体
1 | public class Role { |
创建映射
用户的映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<hibernate-mapping>
<class name="com.hibernate.domain.User" table="sys_user">
<!-- 建立OID与主键的映射 -->
<id name="user_id" column="user_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与字段映射 -->
<property name="user_code" column="user_code"/>
<property name="user_name" column="user_name"/>
<property name="user_password" column="user_password"/>
<property name="user_state" column="user_state"/>
<!-- 建立与角色的多对多的映射关系 -->
<!--
set标签
* name :对方的集合的属性名称。
* table :多对多的关系需要使用中间表,放的是中间表的名称。
-->
<set name="roles" table="sys_user_role" cascade="save-update,delete" >
<!--
key标签:
* column :当前的对象对应中间表的外键的名称。
-->
<key column="user_id"/>
<!--
many-to-many标签:
* class :对方的类的全路径
* column :对方的对象在中间表中的外键的名称。
-->
<many-to-many class="com.hibernate.domain.Role" column="role_id"/>
</set>
</class>
</hibernate-mapping>角色的映射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<hibernate-mapping>
<class name="com.hibernate.domain.Role" table="sys_role">
<!-- 建立OID与主键的映射 -->
<id name="role_id" column="role_id">
<generator class="native"/>
</id>
<!-- 建立普通属性与字段的映射 -->
<property name="role_name" column="role_name"/>
<property name="role_memo" column="role_memo"/>
<!-- 与用户的多对多的映射关系 -->
<!--
set标签
* name :对方的集合的属性名称。
* table :多对多的关系需要使用中间表,放的是中间表的名称。
-->
<set name="users" table="sys_user_role" cascade="save-update,delete" inverse="true">
<!--
key标签:
* column :当前的对象对应中间表的外键的名称。
-->
<key column="role_id"/>
<!--
many-to-many标签:
* class :对方的类的全路径
* column :对方的对象在中间表中的外键的名称。
-->
<many-to-many class="com.hibernate.domain.User" column="user_id"/>
</set>
</class>
</hibernate-mapping>
编写测试类
1 | package com.itheima.hibernate.demo2; |
Hibernated 多对多的操作
只保存一边是否可以
1 |
|
多对多的级联保存或更新
- 保存用户级联保存角色
1 |
|
- 保存角色级联保存用户
1 | /** |
多对多的级联删除(基本用不上)
删除用户级联删除角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* 多对多的级联删除:
* * 删除用户级联删除角色
* * 在User.hbm.xml中的set上配置 cascade="delete"
*/
public void demo5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 查询1号用户:
User user = session.get(User.class, 1l);
session.delete(user);
tx.commit();
}删除角色级联删除用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* 多对多的级联删除:
* * 删除角色级联删除用户
* * 在Role.hbm.xml中的set上配置 cascade="delete"
*/
public void demo6(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 查询2号角色:
Role role = session.get(Role.class, 2l);
session.delete(role);
tx.commit();
}
多对多的其他的操作
给用户选择角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 给用户选择角色
*/
public void demo7(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 给1号用户多选2号角色
// 查询1号用户
User user = session.get(User.class, 1l);
// 查询2号角色
Role role = session.get(Role.class, 2l);
user.getRoles().add(role);
tx.commit();
}给用户改选角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 给用户改选角色
*/
public void demo8(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 给2号用户将原有的2号角色改为3号角色
// 查询2号用户
User user = session.get(User.class, 2l);
// 查询2号角色
Role role2 = session.get(Role.class, 2l);
Role role3 = session.get(Role.class, 3l);
user.getRoles().remove(role2);
user.getRoles().add(role3);
tx.commit();
}
给用户删除角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 给用户改选角色
*/
public void demo9(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 给2号用户删除1号角色
// 查询2号用户
User user = session.get(User.class, 2l);
// 查询2号角色
Role role = session.get(Role.class, 1l);
user.getRoles().remove(role);
tx.commit();
}
}
Hibernate的查询的方式
在Hibernate中提供了很多种的查询的方式。Hibernate共提供了五种查询方式。
Hibernate的查询方式:OID查询
OID检索:Hibernate根据对象的OID(主键)进行检索
使用get方法
1 | Customer customer = session.get(Customer.class,1l); |
使用load方法
1 | Customer customer = session.load(Customer.class,1l); |
Hibernate的查询方式:对象导航检索
对象导航检索:Hibernate根据一个已经查询到的对象,获得其关联的对象的一种查询方式。
1 | LinkMan linkMan = session.get(LinkMan.class,1l); |
Hibernate的查询方式:HQL检索
HQL查询:Hibernate Query Language,Hibernate的查询语言,是一种面向对象的方式的查询语言,语法类似SQL。通过session.createQuery(),用于接收一个HQL进行查询方式。
初始化一些数据
1 |
|
HQL的简单查询
1 |
|
HQL的别名查询
1 |
|
HQL的排序查询
1 |
|
HQL的条件查询
1 |
|
HQL的投影查询
投影查询:查询对象的某个或某些属性
1 |
|
HQL的分页查询
1 |
|
HQL的分组统计查询
1 |
|
HQL的多表查询
SQL的多表查询
连接查询
交叉连接:笛卡尔积
1
select * from A,B;
内连接 : inner join (inner 可以省略)
隐式内连接:
1
select * from A,B where A.id = B.aid;
显示内连接:
1
select * from A inner join B on A.id = B.aid;
外连接 :
左外连接:left outer join(outer 可以省略)
1
select * from A left outer join B on A.id= B.aid;
右外连接:right outer join(outer 可以省略)
1
select * from A right outer join B on A.id = B.aid;
- 子查询
HQL的多表查询
- 连接查询
- 交叉连接
- 内连接
- 显示内连接
- 隐式内连接
- 迫切内连接
- 外连接
- 左外连接
- 右外连接
- 迫切左外连接
- 连接查询
1 |
|
Hibernate的查询方式:QBC检索
QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询的方式。
简单查询
1 |
|
排序查询
1 |
|
分页查询
1 |
|
条件查询
1 |
|
统计查询
1 |
|
离线条件查询(SSH)—DetachedCriteria
1 |
|
Hibernate的查询方式:SQL检索
SQL查询
SQL查询:通过使用sql语句进行查询
1 | public class HibernateDemo3 { |
Hibernate的抓取策略(优化)
延迟加载的概述
什么是延迟加载
延迟加载:lazy(懒加载)。执行到该行代码的时候,不会发送语句去进行查询,在真正使用这个对象的属性的时候才会发送SQL语句进行查询。
延迟加载的分类
类级别的延迟加载
指的是通过load方法查询某个对象的时候,是否采用延迟。session.load(Customer.class,1l);
类级别延迟加载通过
上的lazy进行配置,如果让lazy失效 将lazy设置为false
将持久化类使用final修饰
Hibernate. Initialize()
关联级别的延迟加载
指的是在查询到某个对象的时候,查询其关联的对象的时候,是否采用延迟加载。
1
Customer customer = session.get(Customer.class,1l);
customer.getLinkMans();—-通过客户获得联系人的时候,联系人对象是否采用了延迟加载,称为是关联级别的延迟。
抓取策略往往会和关联级别的延迟加载一起使用,优化语句。
1 | public class HibernateDemo1 { |
抓取策略
抓取策略的概述
通过一个对象抓取到关联对象需要发送SQL语句,SQL语句如何发送,发送成什么样格式通过策略进行配置。
通过<set>或者<many-to-one>上通过fetch属性进行设置
fetch和这些标签上的lazy如何设置优化发送的SQL语句
<set>上的fetch和lazy
fetch:抓取策略,控制SQL语句格式
select :默认值,发送普通的select语句,查询关联对象
join :发送一条迫切左外连接查询关联对象
subselect :发送一条子查询查询其关联对象
lazy:延迟加载,控制查询关联对象的时候是否采用延迟
true :默认值,查询关联对象的时候,采用延迟加载
false :查询关联对象的时候,不采用延迟加载
extra :及其懒惰。
在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。
1 | /** |
<many-to-one>上的fetch和lazy
fetch :抓取策略,控制SQL语句格式。
select :默认值,发送普通的select语句,查询关联对象。
join :发送一条迫切左外连接。
lazy :延迟加载,控制查询关联对象的时候是否采用延迟。
proxy :默认值,proxy具体的取值,取决于另一端的<class>上的lazy的值。
false :查询关联对象,不采用延迟。
no-proxy :(不会使用)
在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。
1 | /** |
批量抓取
什么是批量抓取
一批关联对象一起抓取,batch-size
测试批量抓取
1 | /** |