hibernate 检索策略

    xiaoxiao2021-03-25  64

    Hibernate 检索策略 检索数据时的 2 个问题: 不浪费内存:当 Hibernate从数据库中加载Customer对象时,如果同时加载所有关联的Order对象,而程序实际上仅仅需要访问Customer对象,那么这些关联的Order对象就白白浪费了许多内存. 更高的查询效率:发送尽可能少的 SQL 语句 类级别的检索策略 类级别可选的检索策略包括立即检索和延迟检索,默认为延迟检索 立即检索:立即加载检索方法指定的对象 延迟检索:延迟加载检索方法指定的对象。在使用具体的属性时,再进行加载 类级别的检索策略可以通过 <class> 元素的 lazy属性进行设置 如果程序加载一个对象的目的是为了访问它的属性,可以采取立即检索. 如果程序加载一个持久化对象的目的是仅仅为了获得它的引用,可以采用延迟检索。注意出现懒加载异常! 无论 <class> 元素的 lazy 属性是 true 还是 false,Session get() 方法及 Query list() 方法在类级别总是使用立即检索策略 <class> 元素的 lazy 属性为 true 或取默认值 ,Session load() 方法不会执行查询数据表的 SELECT 语句 , 仅返回代理类对象的实例 , 该代理类实例有如下特征 : Hibernate 在运行时采用 CGLIB 工具动态生成Hibernate 创建代理类实例时 , 仅初始化其 OID 属性在应用程序第一次访问代理类实例的非 OID 属性时 ,Hibernate 会初始化代理类实例 一对多和多对多的检索策略 在映射文件中 , <set> 元素来配置一对多关联及多对多关联关系 .<set> 元素有 lazy fetch 属性lazy : 主要决定 orders 集合被初始化的时机 . 即到底是在加载 Customer 对象时就被初始化 , 还是在程序访问 orders 集合时被初始化fetch : 取值为 “ select” 或 “ subselect , 决定初始化 orders 的查询语句的形式 若取值为” join”, 则决定 orders 集合被初始化的时机若把 fetch 设置为 “ join”, lazy 属性将被忽略<set> 元素 batch-size 属性:用来 为延迟检索策略或立即检索策略设定批量检索的数量 . 批量检索能减少 SELECT 语句的数目 , 提高延迟检索或立即检索的运行性能 .1-n n-n 默认延迟加载;lazy默认true,不建议修改; 增强延迟检索:lazy可以设置extra,尽可能的延迟集合初始化的时机 <set> 元素的 lazy fetch 属性 延迟检索和增强延迟检索在延迟检索 (lazy 属性值为 true) 集合属性时 ,Hibernate 在以下情况下初始化集合代理类实例 应用程序第一次访问集合属性 : iterator (),size(), isEmpty (),contains() 等方法通过 Hibernate.initialize () 静态方法显式初始化增强延迟检索 (lazy 属性为 extra): lazy=“true” 类似 . 主要区别是 增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机 当程序第一次访问 orders 属性的 iterator () 方法时 , 会导致 orders 集合代理类实例的初始化当程序第一次访问 order 属性的 size(),contains() isEmpty () 方法时 ,Hibernate 不会初始化 orders 集合类的实例 , 仅通过特定的 select 语句查询必要的信息 , 不会检索所有的 Order 对象 <set> 元素的batch-size属性<set> 元素有一个 batch-size 属性 , 用来为延迟检索策略或立即检索策略设定批量检索的数量 . 批量检索能减少 SELECT 语句的数目 , 提高延迟检索或立即检索的运行性能 . 第一条sql查总数,后面的查询条数,则受到sbatch-ize属性影响 用带子查询的 select语句整批量初始化orders集合(fetch属性为“subselect ”) <set> 元素的 fetch 属性 : 取值为 “ select” 或 “ subselect , 决定初始化 orders 的查询语句的形式 若取值为” join”, 则决定 orders 集合被初始化的时机 . 默认值为 select fetch 属性为“ subselect 假定 Session 缓存中有 n orders 集合代理类实例没有被初始化 ,Hibernate 能够通过带子查询的 select 语句 , 来批量初始化 n orders 集合代理类实例batch-size 属性将被忽略子查询中的 select 语句为查询 CUSTOMERS OID SELECT 语句 迫切左外连接检索(fetch属性值设为“ join”) <set> 元素的 fetch 属性 : 取值为“ select” 或 “ subselect , 决定初始化 orders 的查询语句的形式 若取值为” join”, 则决定 orders 集合被初始化的时机 . 默认值为 select fetch 属性为“ join” :检索 Customer 对象时 , 会采用 迫切左外连接 ( 通过左外连接加载与检索指定的对象关联的对象 ) 策略来检索所有关联的 Order 对象lazy 属性将被忽略Query list() 方法会忽略映射文件中配置的迫切左外连接检索策略 , 而依旧采用延迟加载策略 迫切左外连接:使用左外 链接进行查询,且把集合属性进行初始化 加载1的一端对象时,使用迫切左外连接的方式检索 n的一端的集合属性 忽略lazy属性;hql查询忽略fetch=join的取值 fetch属性:确定初始化orders集合的方式。设为select等于N+1次检索 多对一和一对一关联的检索策略 <set> 一样 ,<many-to-one> 元素也有一个 lazy 属性和 fetch 属性 . fetch 属性设为 join, 那么 lazy 属性被忽略迫切左外连接检索策略的优点在于比立即检索策略使用的 SELECT 语句更少 .无代理延迟检索需要增强持久化类的字节码才能实现Query list 方法会忽略映射文件配置的迫切左外连接检索策略 , 而采用延迟检索策略如果在关联级别使用了延迟加载或立即加载检索策略 , 可以 设定批量检索的大小 , 以帮助提高延迟检索或立即检索的运行性能 .Hibernate 允许在应用程序中覆盖映射文件中设定的检索策略 . 检索策略小结Hibernate 提供了以下几种检索对象的方式导航对象图检索方式 根据已经加载的对象导航到其他对象OID 检索方式 按照对象的 OID 来检索对象HQL 检索方式 : 使用面向对象的 HQL 查询语言QBC 检索方式 : 使用 QBC(QueryBy Criteria) API 来检索对象 . 这种 API 封装了基于字符串形式的查询语句 , 提供了更加面向对象的查询接口 .本地 SQL 检索方式 : 使用本地数据库的 SQL 查询语句 HQL 检索方式HQL(Hibernate Query Language) 是面向对象的查询语言 , 它和 SQL 查询语言有些相似 . Hibernate 提供的各种检索方式中 ,HQL 是使用最广的一种检索方式 . 它有如下功能 :在查询语句中设定各种查询条件支持投影查询 , 即仅检索出对象的部分属性支持分页查询支持连接查询支持分组查询 , 允许使用 HAVING GROUPBY 关键字提供内置聚集函数 , sum(),min() max()支持子查询支持动态绑定 参数能够调用 用户定义的 SQL 函数或标准的 SQL 函数HQL 检索方式包括以下步骤 :通过 Session createQuery () 方法创建一个 Query 对象 , 它包括一个 HQL 查询语句 .HQL 查询语句中可以包含命名参数动态绑定参数调用 Query 相关方法执行查询语句 .Qurey 接口支持方法链编程风格 , 它的 setXxx () 方法返回自身实例 , 而不是 void 类型HQL vs SQL:HQL 查询语句是面向对象的 , Hibernate 负责解析 HQL 查询语句 , 然后根据对象 - 关系映射文件中的映射信息 , HQL 查询语句 翻译 成相应的 SQL 语句 .HQL 查询语句中的主体是 域模型中的类及类的属性SQL 查询语句是与关系数据库绑定在一起的 .SQL 查询语句中的主体是数据库表及表的字段 .绑定参数 :Hibernate 的参数绑定机制依赖于 JDBCAPI 中的 PreparedStatement 的预定义 SQL 语句功能 .HQL 的参数绑定由两种形式 :按参数名字绑定 : HQL 查询语句中定义命名参数 , 命名参数以“ : 开头 .按参数位置绑定 : HQL 查询语句中用“ ?” 来定义参数位置相关方法 :setEntity (): 把参数与一个持久化类绑定setParameter (): 绑定任意类型的参数 . 该方法的第三个参数显式指定 Hibernate 映射类型HQL 采用 ORDER BY 关键字对查询结果 排序分页查询 :setFirstResult ( int firstResult ): 设定从哪一个对象开始检索 , 参数 firstResult 表示这个对象在查询结果中的索引位置 , 索引位置的起始值为 0. 默认情况下 ,Query 从查询结果中的第一个对象开始检索setMaxResults ( int maxResults ): 设定一次最多检索出的对象的数目 . 在默认情况下 ,Query Criteria 接口检索出查询结果中所有的对象在映射文件中定义命名查询语句Hibernate 允许在映射文件中定义字符串形式的查询语句 .<query> 元素用于定义一个 HQL 查询语句 , 它和 <class> 元素并列 . <query name="findNewsByTitle">      <![CDATA[             FROM News n WHERE n.title LIKE :title       ]]> </query> – 在程序中通过 Session getNamedQuery () 方法获取查询语句对应的 Query 对象 . 投影查询投影查询 : 查询结果仅包含实体的部分属性 . 通过 SELECT 关键字实现 .Query list() 方法返回的集合中包含的是数组类型的元素 , 每个对象数组代表查询结果的一条记录可以在持久化类中定义一个对象的构造器来包装投影查询返回的记录 , 使程序代码能完全运用面向对象的语义来访问查询结果集 .可以通过 DISTINCT 关键字来保证查询结果不会返回重复元素 报表查询报表查询用于对数据分组和统计 , SQL 一样 ,HQL 利用 GROUPBY 关键字对数据分组 , HAVING 关键字对分组数据设定约束条件 . HQL 查询语句中可以调用以下聚集函数count()min()max()sum()avg () HQL ( 迫切 ) 左外连接 迫切左外连接 :LEFT JOIN FETCH 关键字表示迫切左外连接检索策略 .list() 方法 返回的集合中存放实体对象的引用 , 每个 Department 对象关联的 Employee  集合都被初始化 , 存放所有关联的 Employee 的实体对象 .查询结果中可能会包含重复元素 , 可以通过一个 HashSet 来过滤重复元素左外连接 :LEFT JOIN 关键字表示左外连接查询 .list() 方法返回的集合中存放的是对象数组类型根据配置文件来决定 Employee 集合的检索策略 .如果希望 list() 方法返回的集合中仅包含 Department 对象 , 可以在 HQL 查询语句中使用 SELECT 关键字 HQL ( 迫切 ) 内连接迫切内连接 :INNER JOIN FETCH 关键字表示迫切内连接 , 也可以省略 INNER 关键字list() 方法返回的集合中存放 Department 对象 的引用 , 每个 Department 对象 Employee 集合 都被初始化 , 存放所有关联的 Employee 对象内连接 :INNER JOIN 关键字表示内连接 , 也可以省略 INNER 关键字list() 方法的集合中存放的每个元素对应查询结果的一条记录 , 每个元素都是对象数组类型如果希望 list() 方法的返回的集合仅包含 Department  对象 , 可以在 HQL 查询语句中使用 SELECT 关键字 关联级别运行时的检索策略 如果在 HQL 中没有显式指定检索策略 , 将使用映射文件配置的检索策略 .HQL 会忽略映射文件中设置的迫切左外连接检索策略 , 如果希望 HQL 采用迫切左外连接策略 , 就必须在 HQL 查询语句中显式的指定它若在 HQL 代码中显式指定了检索策略 , 就会覆盖映射文件中配置的检索策略 QBC 检索和本地 SQL 检索QBC 查询 就是通过 使用 Hibernate 提供的 Query ByCriteria API 查询对象, 这种 API 封装了 SQL 语句 的动态拼装,对查询提供了更加面向对象的功能 接口本地 SQL 查询来完善 HQL 不能涵盖所有的查询特性 Hibernate 二级缓存缓存 (Cache): 计算机领域非常通用的概念。它 介于应用程序和永久性数据存储源 ( 如硬盘上的文件或者数据库 ) 之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能 。缓存中的数据是数据存储源中数据的拷贝。 缓存的物理介质通常是内存Hibernate 中提供了两个级别的缓存第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存 SessionFactory 级别的缓存 SessionFactory 的缓存可以分为两类 :内置缓存 : Hibernate 自带的 , 不可卸载 . 通常在 Hibernate 的初始化阶段 ,Hibernate 会把映射元数据和预定义的 SQL 语句放到 SessionFactory 的缓存中 , 映射元数据是映射文件中数据( .hbm.xml 文件中的数据)的复制 . 该内置缓存是只读的 .外置缓存 ( 二级缓存 ) : 一个可配置的缓存插件 . 在默认情况下 , SessionFactory 不会启用这个缓存插件 . 外置缓存中的数据是数据库数据的复制 , 外置缓存的物理介质可以是内存或硬盘使用 Hibernate 的二级缓存 适合放入二级缓存中的数据 :很少被修改不是很重要的数据 , 允许出现偶尔的并发问题不适合放入二级缓存中的数据 :经常被修改财务数据 , 绝对不允许出现并发问题与其他应用 程序 共享的数据 二级缓存的并发访问策略 两个并发的事务同时访问持久层的缓存的相同数据时 , 也有可能出现各类并发问题 .二级缓存可以设定以下 4 种类型的并发访问策略 , 每一种访问策略对应一种事务隔离级别非严格读写 ( Nonstrict -read-write): 不保证缓存与数据库中数据的一致性 . 提供 Read Uncommited 事务隔离级别 , 对于极少被修改 , 而且允许脏读的数据 , 可以采用这种策略读写型 (Read-write): 提供 Read Commited 数据隔离级别 . 对于经常读但是很少被修改的数据 , 可以采用这种隔离类型 , 因为它可以防止脏读事务型 (Transactional): 仅在受管理环境下适用 . 它提供了 RepeatableRead 事务隔离级别 . 对于经常读但是很少被修改的数据 , 可以采用这种隔离类型 , 因为它可以防止脏读和不可重复读只读型 (Read-Only): 提供 Serializable 数据隔离级别 , 对于从来不会被修改的数据 , 可以采用这种访问策略管理 Hibernate 的二级缓存Hibernate 的二级缓存是进程或集群范围内的缓存二级缓存是可配置的的插件 ,Hibernate 允许选用以下类型的缓存插件 :EHCache : 可作为进程范围内的缓存 , 存放数据的物理介质可以使内存或硬盘 , Hibernate 的查询缓存提供了支持OpenSymphony OSCache : 可作为进程范围内的缓存 , 存放数据的物理介质可以使内存或硬盘 , 提供了丰富的缓存数据过期策略 , Hibernate 的查询缓存提供了支持SwarmCache : 可作为集群范围内的缓存 , 但不支持 Hibernate 的查询缓存JBossCache : 可作为集群范围内的缓存 , 支持 Hibernate 的查询缓存4 种缓存插件支持的并发访问策略 (x 代表支持 , 空白代表不支持 ) 配置进程范围内的二级缓存配置进程范围内的二级缓存的步骤 :选择合适的缓存插件 : EHCache (jar 包和配置文件 ), 并编译器配置文件 Hibernate 的配置文件中启用二级缓存并指定和 EHCache 对应的缓存适配器选择需要使用二级缓存的持久化类 , 设置它的二级缓存的并发访问策略<class> 元素的 cache 子元素表明 Hibernate 会缓存对象的简单属性 , 但不会缓存集合属性 , 若希望缓存集合属性中的元素 , 必须在 <set> 元素中加入 <cache> 子元素 hibernate 配置文件中通过 <class-cache/> 节点配置使用缓存ehcache.xml< diskStore >: 指定一个目录 EHCache 把数据写到硬盘上时 , 将把数据写到这个目录下 .< defaultCache >: 设置缓存的默认 数据过期策略 <cache> 设定具体的 命名缓存 的数据过期策略。 每个命名缓存代表一个缓存区域缓存 区域 (region) :一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即: < defaultCache .../>Hibernate 在不同的缓存区域保存不同的类 / 集合。对于类而言,区域的名称是类名。如 : com.atguigu.domain.Customer对于集合而言,区域的名称是类名加属性名。如 com.atguigu.domain.Customer.orders   • cache 元素 的属性  name: 设置缓存的名字 , 它的取值为类的全限定名或类的集合的名字maxInMemory : 设置基于内存的缓存中可存放的对象最大数目eternal: 设置对象是否为永久的 ,true 表示永不过期 , 此时将忽略 timeToIdleSeconds timeToLiveSeconds 属性 ; 默认值是 falsetimeToIdleSeconds : 设置对象空闲最长时间 , 以秒为单位 , 超过这个时间 , 对象过期。当对象过期时 , EHCache 会把它从缓存中清除。如果此值为 0, 表示对象可以无限期地处于空闲状态。timeToLiveSeconds : 设置对象生存最长时间 , 超过这个时间 , 对象过期。 如果此值为 0, 表示对象可以无限期地存在于缓存中 . 该属性值必须大于或等于 timeToIdleSeconds 属性值overflowToDisk : 设置基于 内存的 缓存中的对象数目达到上限后 , 是否把溢出的对象写到基于硬盘的缓存中查询缓存对于经常使用的 查询语句 , 如果启用了查询缓存 , 当第一次执行查询语句时 ,Hibernate 会把查询结果存放在查询缓存中 . 以后再次执行该查询语句时 , 只需从缓存中获得查询结果 , 从而提高查询性能查询缓存使用于如下场合 :应用程序运行时经常使用查询语句很少对与查询语句检索到的数据进行插入 , 删除和更新操作启用查询缓存的步骤配置二级缓存 , 因为查询缓存依赖于二级缓存 hibernate 配置文件中启用查询缓存对于希望启用查询缓存的查询语句 , 调用 Query setCacheable () 方法 时间戳缓存区域时间戳缓存区域存放了对于查询结果相关的表进行插入 , 更新或删除操作的时间戳 Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期 , 其运行过程如下 :T1 时刻执行查询操作 , 把查询结果存放在 QueryCache 区域 , 记录该区域的时间戳为 T1T2 时刻对查询结果相关的表进行更新操作 ,Hibernate T2 时刻存放在 UpdateTimestampCache 区域 .T3 时刻执行查询结果前 , 先比较 QueryCache 区域的时间戳和 UpdateTimestampCache 区域的时间戳 , T2>T1, 那么就丢弃原先存放在 QueryCache 区域的查询结果 , 重新到数据库中查询数据 , 再把结果存放到 QueryCache 区域 ; T2< T1, 直接从 QueryCache 中获得查询结果 Query 接口的 iterate() 方法Query 接口的 iterator () 方法 list() 一样也能执行查询操作list() 方法执行的 SQL 语句包含实体类对应的数据表的所有字段Iterator () 方法执行的 SQL 语句中 仅包含实体类对应的数据表的 ID 字段当遍历访问结果集时 , 该方法先到 Session 缓存及二级缓存中查看是否存在特定 OID 的对象 , 如果存在 , 就直接返回该对象 , 如果不存在该对象就通过相应的 SQL Select 语句到数据库中加载特定的实体对象大多数情况下 , 应考虑使用 list() 方法执行查询操作 . iterator () 方法仅在满足以下条件的场合 , 可以 稍微 提高查询性能 :要查询的数据表中包含大量字段启用了二级缓存 , 且二级缓存中可能已经包含了待查询的对象 管理 SessionHibernate  自身提供了三种管理 Session 对象的方法Session 对象的生命周期与本地线程绑定Session 对象的生命周期与 JTA 事务绑定Hibernate 委托程序管理 Session 对象的生命周期 Hibernate 的配置文件中 , hibernate.current_session_context_class 属性用于指定 Session 管理方式 , 可选值包括thread :Session 对象的生命周期与本地线程绑定jta *:Session 对象的生命周期与 JTA 事务绑定managed: Hibernate 委托程序来管理 Session 对象的生命周期 Session 对象的生命周期与本地线程绑定 如果把 Hibernate 配置文件的 hibernate.current_session_context_class 属性值设为 thread,Hibernate 就会按照与本地线程绑定的方式来管理 SessionHibernate 按一下规则把 Session 与本地线程绑定当一个线程 ( threadA ) 第一次调用 SessionFactory 对象的 getCurrentSession () 方法时 , 该方法会创建一个新的 Session( sessionA ) 对象 , 把该对象与 threadA 绑定 , 并将 sessionA 返回 threadA 再次调用 SessionFactory 对象的 getCurrentSession () 方法时 , 该方法将返回 sessionA 对象 threadA 提交 sessionA 对象关联的事务时 ,Hibernate 会自动 flush sessionA 对象的缓存 , 然后提交事务 , 关闭 sessionA 对象 . threadA 撤销 sessionA 对象关联的事务时 , 也会自动关闭 sessionA 对象 threadA 再次调用 SessionFactory 对象的 getCurrentSession () 方法时 , 该方法会又创建一个新的 Session( sessionB ) 对象 , 把该对象与 threadA 绑定 , 并将 sessionB 返回 批量处理数据 批量处理数据是指在一个事务中处理大量数据 .在应用层进行批量操作 , 主要有以下方式 :通过 Session通过 HQL 通过 StatelessSession通过 JDBC API 通过 Session 来进行批量操作 Session save() update() 方法都会把处理的对象存放在自己的缓存中 . 如果通过一个 Session 对象来处理大量持久化对象 , 应该 及时从缓存中清空已经处理完毕并且不会再访问的对象 . 具体的做法是 在处理完一个对象或小批量对象后 , 立即调用 flush() 方法刷新缓存 , 然后在调用 clear() 方法清空缓存通过 Session 来进行处理操作会受到以下约束需要在  Hibernate 配置文件中设置 JDBC 单次批量处理的数目 , 应保证每次向数据库发送的批量的 SQL 语句数目与 batch_size 属性一致若对象采用 “ identity” 标识符生成器 , Hibernate 无法在 JDBC 层进行批量插入操作进行批量操作时 , 建议关闭 Hibernate 的二级缓存   通过 Session 来进行批量操作批量更新 : 在进行批量更新时 , 如果一下子把所有对象都加载到 Session 缓存 , 然后再缓存中一一更新 , 显然是不可取的使用可滚动的结果集 org.hibernate.ScrollableResults , 该对象中实际上并不包含任何对象 , 只包含用于在线定位记录的游标 . 只有当程序遍历访问 ScrollableResults 对象的特定元素时 , 它才会到数据库中加载相应的对象 .org.hibernate.ScrollableResults 对象由 Query scroll 方法返回 通过 HQL 来进行批量操作注意 : HQL 只支持 INSERTINTO … SELECT 形式的插入语句 , 但不支持 INSERTINTO … VALUES 形式的插入语句 . 所以使用 HQL 不能进行批量插入操作 .   通过 StatelessSession 来进行批量操作从形式上看, StatelessSession session 的用法类似。 StatelessSession session 相比,有以下区别 :StatelessSession 没有缓存,通过 StatelessSession 来加载、保存或更新后的对象处于游离状态。StatelessSession 不会与 Hibernate 的第二级缓存交互。当调用 StatelessSession save()、update() delete() 方法时,这些方法会立即执行相应的 SQL 语句,而不会仅计划执行一条 SQL 语句StatelessSession 不会进行脏检查,因此修改了 Customer 对象属性后,还需要调用 StatelessSession update() 方法来更新数据库中数据。StatelessSession 不会对关联的对象进行任何级联操作。通过同一个 StatelessSession 对象两次加载 OID 1 Customer 对象,得到的两个对象内存地址不同。StatelessSession 所做的操作可以被 Interceptor 拦截器捕获到,但是会被 Hibernate 的事件处理系统忽略掉。
    转载请注明原文地址: https://ju.6miu.com/read-35969.html

    最新回复(0)