Cheug's Blog

当前位置:网站首页 / JAVA / 正文

Hibernate[三]

2019-11-01 / JAVA / 1145 次围观 / 0 次吐槽 /

Hibernate实体状态回顾

image.png

1、Hibernate的多表关联关系映射

1.1、多对多的关联关系映射

²  目标:掌握如何对多表关联的映射进行配置

²  以学生和课程为例,一个学生可以对应多个课程,多个学生可以对应一个课程

1.1.1 学生和课程的JavaBeanentitypojo,model

image.png

1.1.2学生和课程表的hbm.xml映射

Student.hbm.xml

image.png

Course.hbm.xml

image.png

1.2、多对多的测试

1.2.1 自动生成三张表

    public void test1(){

       Session session = HibernateUtils.openSession();

       session.beginTransaction();

       session.getTransaction().commit();

       session.close();

    }

image.png

1.2.2 保存一个学生

l  注意保存前的外键维护和级联是如何的配置

l  一般操作多的一方选择级联,这里学生操作比较多

@Test
 public void test1(){
    /**
         * 保存多对多数据
     */
    Session session = HibernateUtils.openSession();
 
    session.getTransaction().begin();
 
    //1.创建2个学生
    Student stu1 = new Student("特朗普");
    Student stu2 = new Student("普京");
 
    //2.创建2个课程
    Course c1 = new Course("维护世界和平");
    Course c2 = new Course("外贸");
 
    //3.绑定课程到学生
    stu1.getCourses().add(c1);
    stu1.getCourses().add(c2);
 
    stu2.getCourses().add(c1);
    stu2.getCourses().add(c2);
 
    //4.保存
    /**
     * 保存的注意事项
     * 1.配置级联保存,只保存学生对象
     *   插入2个学生,插入2两课程,中间4条,8条sql
     * 2.如果在Student配置inverse="true",由Course来维护外键关系,中间表没数据
     * 3.默认Student配置inverse="false",由Student来维护外键关系,中间表有数据
     * 4.多对多,inverse不能两边都为true,如果两边都为true,不管保存哪个对象,中间表都没有数据
     */
    /*session.save(stu1);
    session.save(stu2);*/
 
    //课程拥有有学生
    c1.getStudents().add(stu1);
    c1.getStudents().add(stu2);
 
    c2.getStudents().add(stu1);
    c2.getStudents().add(stu2);
 
    session.save(c1);
    session.save(c2);
 
 
    session.getTransaction().commit();
    session.close();
 
 }

1.3 加载策略

1.3.1 类级别的加载策略

n  get立即检索get方法一执行,立即查询所有字段的数据。

n  load延迟检索。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值才查询

n  如果在类级别上配置lazytrue,load方法就会即时加载,否则为延时加载

image.png

image.png

1.3.2 关联级别的加载策略

n  关联级别的集合加载的策略是默认是懒加载

n  可以设置为即时加载,如图,即加载学生时,学生所属的课程也会加载进来

image.png

image.png

1.3.3 fecth

Fecth:拿取,获取数据

fecth:是指查询集合的sql方式

   select:默认的,普通select查询语句

   join:表连接语句查询

   subselect:使用子查询

image.png

fetch:select

n  set集合默认的sql查询方式就是fectch=select,一个普通的select查询语句

fecth:join

image.png

image.png

image.png

fetch:subselect

n  【只能用于多对多,一对多】

image.png

image.png

1.3.4 多对一的加载策略

²  这里以CustomerOrder多例来讲解

²  多对一标签<many-to-one fetch="" lazy="">

image.png

image.png

image.png

1.3.5 批量加载【了解】

set标签可以配置一个batch-size="2",表示每次可以加载两条数据

image.png

image.png

1.3.6 检索策略

检索策略

优点

缺点

优先考虑使用的场合

立即检索/

即时加载

对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象

(1)select语句多

(2)浪费内存空间

(1)类级别

(2)应用程序需要立即访问的对象

(3)使用了二级缓存

延迟检索/

懒加载的意思

由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。

应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。

(1)一对多或者多对多关联

(2)应用程序不需要立即访问或者根本不会访问的对象

                            

表连接检索

(1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。

(2)使用了外连接,select语句少

(1)可能会加载应用程序不需要访问的对象,浪费内存。

(2)复杂的数据库表连接也会影响检索性能。

(1)多对一或一对一关联

(2)需要立即访问的对象

(3)数据库有良好的表连接性能。

 

 

2、HQL 查询

2.1 HQL简介

²  HQL(Hibernate Query Language) 描写对象操作的一种查询语言,Hibernate特有

²  HQL的语法与SQL基本一致,不同的是HQL是面向对象的查询,查询的是对象 和对象中的属性

²  HQL关键字不区分大小写,但类名和属性区分大小写

²  语法示例

SELECT 别名/属性名/表达式

FROM 实体 AS 别名

WHERE 过滤条件

GROUP BY 分组条件

HAVING 分组后的结果的过滤条件

ORDER BY 排序条件

2.2案例1:查询所有客户

image.png

2.3 案例2:选择查询

image.png

2.4 案例3:投影查询

image.png

image.png

2.5 案例4:排序

image.png

2.6 案例5:分页

Query query = session.createQuery("from Customer");

// *** pageNum 当前页(之前的 pageCode

query.setFirstResult(0);

// * 每页显示个数 pageSize

query.setMaxResults(2);

2.7 案例6:聚合函数和分组查询

image.png

2.8 案例7: 连接查询

INNER JOIN: 在表中存在至少一个匹配时,INNER JOIN 关键字返回行。

LEFT OUTER JOIN: 关键字会从左表 (table_name1) 那里返回所有的行,即使在右表 (table_name2) 中没有匹配的行

RIGHT OUTER JOIN:关键字会右表 (table_name2) 那里返回所有的行,即使在左表 (table_name1) 中没有匹配的行。

image.png

Ø  交叉连接 ,等效 sql 笛卡尔积

image.png

image.png

Ø  隐式内连接,等效 sql 隐式内连接

image.png

Ø  内连接,等效sql内连接

image.png

Ø  迫切内连接,hibernate底层使用 内连接。

image.png

Ø  左外连接,等效sql左外连接

image.png

迫切左外连接,hibernate底层使用 左外连接

image.png

Ø  右外连接(迫切),等效sql右外连接

image.png

2.9 案例8 命名查询

Ø  HQL语句写在java文件里,有时候不灵活,如果要修改语句,要重新编译项目打包

Ø  我们可以在hbm.xml中命名查询语句,然后java中从hbm.xml取出hql语句,这样,以后的开发,可以直接找hbm.xml进行配置

Hbm.xml

image.png

找全局的HQL,不需要指定包名 session.getNameQuery("abc")

找局部的HQL,需要指定包名 session.getNameQuery("com.cheug.domain.Customer.bcd")



3.QBC查询

3.1简介

QBC:Query By Criteria条件查询.面向对象的查询的方式.

 

3.2 简单查询

image.png

3.3 分页

image.png

3.4 排序

image.png

3.5 条件查询

image.png

3.6 离线查询【了解】

DetachedCriteria 离线查询对象,不需要使用Session就可以拼凑查询条件。一般使用在web层或service层拼凑。将此对象传递给dao层,此时将与session进行绑定执行查询。

image.png

4.常见配置

4.1 整合c3p0连接池

第一步:把c3p0包导入工程 

第二步:配置c3p0


image.png

image.png

第三步:测试

获取session并打印,如果出现下面的c3p0 pool表示配置成功

image.png

4.2 事务的隔离级别

回顾事务

一组业务操作,要么全部成功,要么全部不成功。

特性:ACID

Ø  原子性:整体 【原子性是指事务包含的所有操作要么全部成功,要么全部失败】

Ø  一致性:数据 【一个事务执行之前和执行之后都必须处于一致性状态】

Ø  隔离性:并发 对于任意两个并发的事务T1T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

Ø  持久性:结果 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的

 

隔离问题

Ø  脏读:一个事务读到另一个事务未提交的内容【读取未提交内容】

在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。

 

Ø  不可重复读:一个事务读到另一个事务已提交的内容(insert【读取提交内容】

这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。

 

Ø  虚读(幻读):一个事务读到另一个事务已提交的内容(update

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。

 

Ø  Serializable(可串行化)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

隔离级别--解决问题

Ø  read uncommittd,读未提交。存在3个问题。

Ø  read committed,读已提交。解决:脏读。存在2个问题。

Ø  repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。

Ø  serializable,串行化。单事务。没有问题。

image.png

Hibernate中设置隔离级别

²  hibernate.cfg.xml 配置hibernate.connection.isolation 4

image.png

4.3、悲观锁

悲观锁分为这面两种锁(数据库提供实现) .

读锁/共享锁 【少用】

Ø  读锁可被其他线程所共享,如果是读取的话大家都可以用这把锁读到数据.

Ø  select * from table lock in share mode(读锁、共享锁)

演示步骤

A终端开事务添加读锁

B终端开事务添加读锁

A终端更新

B终端提交

A终端提交

START TRANSACTION;

#添加读锁或者共享锁,其它线程可以用这把锁

SELECT * FROM t_customer LOCK IN SHARE MODE;

 

#如果其它线程用了这把锁,没有提交释放锁,是不能执行更新

UPDATE t_customer SET `name` = 'guoyongfeng' WHERE id=1;

COMMIT;

写锁/排他锁【用的多】

Ø  写锁不能共享,只要有人为数据加入了写锁,其他人就不能为数据加任何锁.

Ø  select * from table for update (写锁、排它锁)

Ø  锁可以锁一张表,或者锁一行记录

演示步骤

A终端开事务添加写锁

B终端开事务读表添加写锁【读不到数据】

A终端更新

A终端提交

B终端提交

START TRANSACTION;

#给表添加写锁,其它线程无法读取这个表,当提交后才可以读取这个表

SELECT * FROM t_customer FOR UPDATE;//锁表【不推荐使用】

UPDATE t_customer SET `name` = 'guoyongfeng' WHERE id=1;

COMMIT;

演示Hibernate添加写锁

image.png

4.4、乐观锁

乐观锁就是在表中添加一个version字段来控制数据不一致性

画图,以扣钱为例

这里以Customer为例

 

PO对象(javabean)提供字段,表示版本字段。一般为Integer

image.png

在映射文件中配置下Version

image.png

测试出结果,如果更新的版本比在数据库的版本低,就会更新出错image.png


Powered By Cheug's Blog

Copyright Cheug Rights Reserved.