【郑州校区】三大框架课堂笔记之Hibernate-day07(下)
【郑州校区】三大框架课堂笔记之Hibernate-day07(上)
多表操作
SQL多表操作
1.交叉连接 CROSS JOIN 会产生迪卡尔积SELECT * FROM t_customer CROSS JOIN t_order; 2.内连接 INNER JOIN ON SELECT * FROM t_customer AS c INNER JOIN t_order AS o ON c.id=o.c_customer_id; 使用内连接它只能将有关联的数据得到。 隐式内连接 使用 "逗号"将表分开,使用WHERE来消除迪卡尔积 SELECT * FROM t_customer AS c ,t_order o WHERE c.id=o.c_customer_id; 3.外连接 左外LEFT OUTER JOIN 右外RIGHT OUTER JOIN OUTER可以省略 SELECT * FROM t_customer c LEFT OUTER JOIN t_order o ON c.id=o.c_customer_id; HQL多表操作 Hql多表操作分类: 1. 交叉连接 2. 内连接 a) 显示内连接 b) 隐式内连接 c) 迫切内连接 3. 外连接 左外连接 迫切左外连接 右外连接 注意:在hibernate中有迫切连接的概念,而sql中没有。 内连接显示内连接显示内连接使用的是inner join with 隐式内连接隐式内连接也我们在sql中操作不一样,它是通过”.”运算符来关联 迫切内连接迫切内连接得到的结果是直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象。 外连接 Hibernate事务管理事务介绍问题:什么是事务? 事务就是逻辑上的一组操作,组成这组操作的各个单元要么全部成功,要么全都失败。 问题:事务四个特性? 原子性:不可分割 一致性:事务在执行前后,要保证数据的一致。 隔离性:一个事务在执行的过程中,不应该受到其它事务的干扰。 持久性:事务一旦结束,数据持久化到数据库。 问题:不考虑事务的隔离性,会产生什么问题? 脏读:一个事务读取到另一个事务的未提交数据 不可重复读:一个事务读取到另一个事务提交的数据(主要是指update),会导致两次读取的结果不一致。 虚读(幻读): 一个事务读取到另一个事务提交的数据(主要是指insert),会导致两次读取结果不一致. 问题:对于上述问题如何解决? 我们可以通过设置隔离级别来解决. READ_UNCOMMITED 读取未提交,它引发所有的隔离问题 READ_COMMITTED 读已提交,阻止脏读,可能发生不可重复读与虚读. REPEATABLE_READ 重复读 阻止脏读,不可重复读 可能发生虚读 SERIALIZABLE 串行化 解决所有问题 不允许两个事务,同时操作一个目标数据。(效率低下) ORACLE 默认的是事务隔离级别 READ_COMMITTED MYSQL 默认的事务隔离级别 REPEATABLE_READ Hibernate中设置事务隔离级别hibernate.connection.isolation 它可取的值有 1 2 4 8 1代表的事务隔离级别为READ UNCOMMITTED 2代表的事务隔离级别为READ COMMITTED 4.代表的事务隔离级别为 REPEATABLE READ 8代表的事务隔离级别为 SERIALIZABLE 在hibernate.cfg.xml文件中配置 Hibernate中session管理 Hibernate提供了三种管理session的方式: 1. Session对象的生命周期与本地线程绑定(ThreadLocal) 2. Session对象的生命周期与JTA事务绑定(分布式事务管理) 3. Hibernate委托程序来管理Session的生命周期 我们之前所使用的是第三种 ,通过程序获取一个Session对象,使用它,最后session.close(); 在实际开发中我们一般使用的是前两种: 主要介绍关于本地线程绑定Session。 步骤: 1. 需要在hibernate.cfg.xml文件配置 2. 在获取session时不要在使用openSession而是使用getCurrentSession()方法。 关于getCurrentSession使用时的注意事项: 上述代码执行后,会产生问题 原因:使用getCurrentSession获取的与线程绑定的session对象,在事务关闭时,session对象也会close,简单说,就不需要我们在手动close;
Hibernate优化方案HQL优化1.使用参数绑定 1.使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用用生成好的执行计划,这样做节省CPU时间和内存。 2.避免SQL注入 2.尽量少使用NOT 如果where子句中包含not关键字,那么执行时该字段的索引失效。 3.尽量使用where来替换havingHaving在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销 4.减少对表的查询 在含有子查询的HQL中,尽量减少对表的查询,降低开销 5.使用表的别名当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。 6.实体的更新与删除 在hibernate3以后支持hql的update与delete操作 一级缓存优化一级缓存也叫做session缓存,在一个hibernate session有效,这级缓存的可干预性不强,大多于hibernate自动管理,但它提供清除缓存的方法,这在大批量增加(更新)操作是有效果的,例如,同时增加十万条记录,按常规进行,很可能会出现异常,这时可能需要手动清除一级缓存,session.evict以及session.clear. 检索策略(抓取策略)延迟加载延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。 load方法采用的策略延迟加载. get方法采用的策略立即加载。 检索策略分为两种: 1. 类级别检索 2. 关联级别检索 类级别检索类级别检索是通过session直接检索某一类对应的数据,例如 Customer c=session.load(Customer.class,1) Session.createQuery(“from Order”) 类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过<class>元素的lazy属性来设置 ,默认值是true 在hbm配置文件中设置 在类中使用注解 如果将lazy设置为false,代表类级别检索也使用立即检索。这时load与get就一样,都是立即检索。 如果对一个延迟代理对象进行初始化? 关联级别检索 查询到某个对象,获得其关联的对象或属性,这种称为关联级别检索,例如 c.getOrders().size() c.getName() 对于关联级别检索我们就要研究其检索策略(抓取策略) 检索策略(抓取策略)抓取策略介绍 指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。 一对一 <one-to-one> 一对多(多对一) <set>下有<one-to-many> <many-to-one> 多对多 <set>下有<many-to- many> 我们主要是在<set>与<many-to-one>或<one-to-one>上设置fetch lazy 例如:查询一个客户,要关联查询它的订单 客户是一的一方,在客户中有set集合来描述其订单,在配置中我们是使用 <set> <one-to-many> </set>.. 可以在set标签上设置两个属性 fetch lazy Fetch主要描述的是SQL语句的格式(例如是多条,子查询,多表联查 Lazy 控制SQL语句何时发送 例如:在查询一个订单时,要查询客户信息 <many-to-one> 或<one-to-one> 也可以设置fetch lazy Fetch主要描述的是SQL语句的格式(例如是多条,子查询,多表联查 Lazy 控制SQL语句何时发送 总结: 讲解抓取策略 在两方面设置 <set fetch=”” lazy=””> <many-to-one fetch=”” lazy=””> <one-to-one> 注解配置抓取策略问题:如何使用注解来设置 在<setse>设置的fetch与lazy可以使用下面注解来描述 在<many-to-one>或<one-to-one>上如何设置 fetch与lazy set上的fetch与lazyset上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。 Fetch可取值有: 1. SELECT 多条简单的sql (默认值) 2. JOIN 采用迫切左外连接 3. SUBSELECT 将生成子查询的SQL lazy可取值有: 1. TURE 延迟检索 (默认值) 2. FALSE 立即检索 3. EXTRA 加强延迟检索(及其懒惰) 第一种组合 会首先查询客户信息,当需要订单信息时,才会关联查询订单信息。 第二种组合s
当查询客户信息时,就会将订单信息也查询,也就是说订单信息没有进行延迟。 第三种组合当查询客户信息时,不会查询订单信息,当需要订单的个数时,也不会查询订单信息, 只会通过count来统计订单个数。 当我们使用size(),contains()或isEmpty()方法时不会查询订单信息。 第四种组合如果fetch选择的是join方案,那么lazy它会失效。 生成SQl将采用的是迫切左外连接(left outer join fetch) 会立即查询。 第五种组合会生成子查询,但是我们在查询订单时采用的是延迟加载。 第六种组合会生成子查询,在查询客户信息时,就会将订单信息也查询出来 第七种组合在查询订单时,只会根据情况来确定是否要订单信息,如果不需要,例如我们 程序中size操作,那么就会发出select count(*) from Order where c_customer_id=? One的一言fetch与lazy<set fetch lazy>它主要是设置在获取到一的一方时,如果去查询多的一方。 在<many-to-one>或<one-to-one>如果去查询对方。 对于程序 就是在多的一方如何查询一的主方信息 例如:获取到一个订单对象,要查询客户信息。 Fetch可取值: select 默认值,代表发送一条或多条简单的select语句 join 发送一条迫切左外连接 lazy可取值 false 不采用延迟加载 proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定 no-proxy 不用研究 第一种组合注意:Customer的类级别延迟策略 当我们执行时,会首先发送一条sql只查询订单信息,客户信息会延迟,只有真正需要客户信息时,才会发送sql来查询客户信息. 第二种组合注意:Customer的类级别延迟策略 当查询订单时,就会将客户信息也查询到,原因是Customer它的类级别延迟为false,也就是立即查询。 第三种组合当查询订单时,不会对客户信息进行延迟,立即查询客户信息 第四种组合如果fetch值为join,那么lazy失效。 会发送一条迫切左外连接来查询,也就立即查询。 批量抓取我们在查询多个对象的关联对象时,可以采用批量抓取方式来对程序进行优化. 要想实现批量抓取: 可以在配置文件中 batch-size属性来设置 可以使用注解 @BatchSize(size=4) 可以采用批量抓取来解决N+1问题. 查询客户,查询订单可以在客户配置文件中配置batch-size,是在<set>标签上 查询订单,查询客户订单与客户,客户它是一个主表,订单是一个从表。 在设置批量抓取时都是在主表中设置 在配置文件中在主表的<calss>标签上设置batch-size 在注解使用中 注意:无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置 , 如果是要查询子信息,那么我们是在<set>上来设置batch-size,如果是从子方来查询父方, 也是在父方设置在<class>设置batch-size.
父与子区分: 有外键的表是子(从关联方就是父(主)表 传智播客·黑马程序员郑州校区地址 河南省郑州市 高新区长椿路11号大学科技园(西区)东门8号楼三层 联系电话 0371-56061160/61/62 来校路线 地铁一号线梧桐街站A口出
|