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