`

hibernate4之检索策略

 
阅读更多

 

概述

 

检索数据时的 2 个问题:

1) 不浪费内存:当 Hibernate 从数据库中加载 Customer 对象时, 如果同时加载所有关联的 Order 对象, 而程序实际上仅仅需要访问 Customer 对象, 那么这些关联的 Order 对象就白白浪费了许多内存.

2) 更高的查询效率:发送尽可能少的 SQL 语句

 

模型


 

1. 域模型

    

package org.rabbitx.hibernate4.searchstrategy;

public class Order {

	private Integer id;
	
	private Integer no;
	
	//在many端添加one端的引用来构造many-to-one关联关系
	private Customer customer;

	setter,getter...
}

    

package org.rabbitx.hibernate4.searchstrategy;

import java.util.HashSet;
import java.util.Set;

public class Customer {

	private Integer id;
	
	private String name;
	
	//在one端添加many端的集合类型来构造one-to-many关联关系
	//1. 声明集合类型时, 需使用接口类型, 因为 hibernate 在获取集合类型时, 返回的是 Hibernate 内置的集合类型, 而不是 JavaSE 一个标准的集合实现.
	//2.需要把集合进行初始化,防止发生空指针异常
	private Set<Order> orders = new HashSet<>();

	setter,getter...
}

 

2. 映射文件

    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.rabbitx.hibernate4.searchstrategy">
    <class name="Order" table="TBL_SEARCH_STRATEGY_ORDER">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="no" type="java.lang.Integer">
            <column name="NO" />
        </property>
        
        <!-- 
			  使用 many-to-one 来映射多对一的关联关系 
			 name:   many端类中关联one端的属性的名字,此值与many端中属性名一致
			 class:  many端类中关联one端的属性的类型,此值与many端中属性类型一致
			 column: many端数据表中关联one端的外键的名字,此值自定义
		-->
        <many-to-one name="customer" class="Customer" column="customer_id"></many-to-one>
    </class>
</hibernate-mapping>

    

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.rabbitx.hibernate4.searchstrategy">

    <class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER" lazy="true">
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <!-- 
			  使用 one-to-many 来映射一对多的关联关系 
			 set: 映射 set 类型的属性
			 table: set 中的元素对应的记录放在哪一个数据表中. 该值需要和多对一的多的那个表的名字一致
			 inverse: 指定由哪一方来维护关联关系. 通常设置为 true, 以指定由多的一端来维护关联关系
			 cascade 设定级联操作. 开发时不建议设定该属性. 建议使用手工的方式来处理
			 order-by 在查询时对集合中的元素进行排序, order-by 中使用的是表的字段名, 而不是持久化类的属性名    
		-->
        <set name="orders" table="TBL_SEARCH_STRATEGY_ORDER" inverse="true">
            <!-- 
                  many端表的外键列名 
                                                      即Order.hbm.xml文件中:<many-to-one name="customer" class="Customer" column="customer_id"></many-to-one> 节点column的值     
            -->
            <key column="customer_id"></key>
            <!-- 指定映射类型:因为在many-to-many中也使用到set类型,需要此设置来区分。 -->
            <one-to-many class="Order"/>
        </set>
    </class>
</hibernate-mapping>

 

3. hibernate配置文件

    

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
		"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
		"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
    
        <!-- 配置连接数据库的基本信息 -->
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">root</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://192.168.1.105:3306/hibernate4</property>
        
        <!-- 配置hibernate基本信息 -->
        <!-- 配置数据库方言 -->
        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
        
        <!-- 执行操作时是否在控制台打印 SQL -->
        <property name="show_sql">true</property>
        
        <!-- 是否对 SQL 进行格式化 -->
        <property name="format_sql">true</property>
        
        <!-- 指定自动生成数据表的策略 -->
        <property name="hbm2ddl.auto">update</property>
        
        <!-- 设置隔离级别 :1. READ UNCOMMITED 2. READ COMMITED 4. REPEATABLE READ 8. SERIALIZEABLE-->
        <property name="hibernate.connection.isolation">4</property>
        
        <!-- 配置c3p0数据源 -->
        <!-- 数据库连接池的最大连接数 -->
        <property name="hibernate.c3p0.max_size">100</property>
        <!-- 数据库连接池的最小连接数 -->
        <property name="hibernate.c3p0.min_size">10</property>
        <!-- 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接-->
        <property name="hibernate.c3p0.acquire_increment">10</property>
        <!-- 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁 -->
        <property name="hibernate.c3p0.timeout">1800</property>
        <!-- 
                                       表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时. 
                                       连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,
                                      这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和 timeout 做对比,进而决定是否销毁这个连接对象。 
        -->
        <property name="hibernate.c3p0.idle_test_period">1800</property>
        <!-- 缓存 Statement 对象的数量 -->
        <property name="hibernate.c3p0.max_statements">10</property>
        
        <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
    	<property name="hibernate.jdbc.fetch_size">100</property>
    	
    	<!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
    	<property name="jdbc.batch_size">30</property>
    	
        <!-- 指定关联的 .hbm.xml 文件 -->
	    <mapping resource="org/rabbitx/hibernate4/searchstrategy/Customer.hbm.xml"/>
		<mapping resource="org/rabbitx/hibernate4/searchstrategy/Order.hbm.xml"/>
		
    </session-factory>
</hibernate-configuration>

 

4. 添加数据

    

package org.rabbitx.hibernate4.searchstrategy;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/**
 * 
 * @author RabbitX
 */
public class SearchStrategyJUnitTest {

	private static SessionFactory sessionFactory;
	private static Session session;
	private static Transaction transaction;
	
	@BeforeClass
	public static void beforeClass()
	{
		String cfgPath = "org/rabbitx/hibernate4/searchstrategy/hibernate.cfg.xml";
		Configuration cfg = new Configuration().configure(cfgPath);
		ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
		sessionFactory = cfg.buildSessionFactory(serviceRegistry);
		session = sessionFactory.openSession();
	}
	
	@AfterClass
	public static void afterClass()
	{
		sessionFactory.close();
	}
	
	@Before
	public void before()
	{
		session = sessionFactory.openSession();
		transaction = session.beginTransaction();
	}
	
	@After
	public void after()
	{
		if(session.isOpen())
		{
			transaction.commit();
			session.close();
		}
	}
	
	/**
	 * 准备需要的数据
	 */
	@Test
	public void testPrepareData()
	{
		Customer customer = new Customer("BluceLi");
		
		Order order1 = new Order(100090001);
		Order order2 = new Order(100090002);
		Order order3 = new Order(100090003);
		Order order4 = new Order(100090004);
		Order order5 = new Order(100090005);
		Order order6 = new Order(100090006);
		Order order7 = new Order(100090007);
		Order order8 = new Order(100090008);
		Order order9 = new Order(100090009);
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		order3.setCustomer(customer);
		order4.setCustomer(customer);
		order5.setCustomer(customer);
		order6.setCustomer(customer);
		order7.setCustomer(customer);
		order8.setCustomer(customer);
		order9.setCustomer(customer);
		
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);
		customer.getOrders().add(order3);
		customer.getOrders().add(order4);
		customer.getOrders().add(order5);
		customer.getOrders().add(order6);
		customer.getOrders().add(order7);
		customer.getOrders().add(order8);
		customer.getOrders().add(order9);

		session.save(customer);
		session.save(order1);
		session.save(order2);
		session.save(order3);
		session.save(order4);
		session.save(order5);
		session.save(order6);
		session.save(order7);
		session.save(order8);
		session.save(order9);
	}
}

 

 

 

 

类级别的检索策略

 

类级别可选的检索策略包括立即检索和延迟检索, 默认为延迟检索

 

-立即检索: 立即加载检索方法指定的对象

-延迟检索: 延迟加载检索方法指定的对象。在使用具体的属性时,再进行加载

 

类级别的检索策略可以通过 <class> 元素的 lazy 属性进行设置

 

如果程序加载一个对象的目的是为了访问它的属性, 可以采取立即检索. 

如果程序加载一个持久化对象的目的是仅仅为了获得它的引用, 可以采用延迟检索。注意出现懒加载异常!

 

无论 <class> 元素的 lazy 属性是 true 还是 false, Session 的 get() 方法及 Query 的 list() 方法在类级别总是使用立即检索策略

若 <class> 元素的 lazy 属性为 true 或取默认值, Session 的 load() 方法不会执行查询数据表的 SELECT 语句, 仅返回代理类对象的实例, 该代理类实例有如下特征:

由 Hibernate 在运行时采用 CGLIB 工具动态生成

Hibernate 创建代理类实例时, 仅初始化其 OID 属性

在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例

 

示例 

 

	/**
	 * 在Customer.hbm.xml文件中class节点不设置lazy属性时,默认值为true。
	 *    <class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER" lazy="true">
	 *    在此设置下使用load方法检索数据时为延迟检索。
	 * 执行结果:
	 *    不发送SQL;
	 *    返回一个hibernate创建的代理对象(org.rabbitx.hibernate4.searchstrategy.Customer_$$_javassist_0);    
	 */
	@Test
	public void testClassLazy001()
	{
		Customer customer = (Customer) session.load(Customer.class, 1);
		//返回结果:org.rabbitx.hibernate4.searchstrategy.Customer_$$_javassist_0
		System.out.println(customer.getClass().getName());
		System.out.println(customer.getId());
	}
	
	/**
	 * 配置:<class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER">
	 * 延迟检索情况下,在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例
	 */
	@Test
	public void testClassLazy002()
	{
		Customer customer = (Customer) session.load(Customer.class, 1);
		System.out.println(customer.getName());
		System.out.println(customer.getClass().getName());
	}
	
	/**
	 * 配置:<class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER" lazy="false">
	 * 立即检索情况下,立即检索数据,hibernate不再创建代理对象。
	 * 执行结果:
	 *    发送SQL;
	 *    返回原对象(org.rabbitx.hibernate4.searchstrategy.Customer);    
	 */
	@Test
	public void testClassLazy003()
	{
		Customer customer = (Customer) session.load(Customer.class, 1);
		System.out.println(customer.getId());
		//返回结果:org.rabbitx.hibernate4.searchstrategy.Customer
		System.out.println(customer.getClass().getName());
	}
	
	/**
	 * 配置:<class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER">
	 * 使用get方法检索数据
	 * 执行结果:
	 *    发送SQL;
	 *    返回原对象(org.rabbitx.hibernate4.searchstrategy.Customer);    
	 */
	@Test
	public void testClassLazy004()
	{
		Customer customer = (Customer) session.get(Customer.class, 1);
		System.out.println(customer.getId());
		//返回结果:org.rabbitx.hibernate4.searchstrategy.Customer
		System.out.println(customer.getClass().getName());
	}
	
	/**
	 * 配置:<class name="Customer" table="TBL_SEARCH_STRATEGY_CUSTOMER">
	 * 使用Query 的 list() 方法检索数据
	 * 执行结果:
	 *    发送SQL;
	 *    返回原对象(org.rabbitx.hibernate4.searchstrategy.Customer);    
	 */
	@Test
	@SuppressWarnings("unchecked")
	public void testClassLazy005()
	{
		Query query = session.createQuery("from Customer");
		List<Customer> customers = query.list();
		System.out.println(customers);
	}

 

一对多和多对多的检索策略

 

在映射文件中, 用 <set> 元素来配置一对多关联及多对多关联关系. <set> 元素有 lazy 和 fetch 属性

 

-lazy: 主要决定 orders 集合被初始化的时机. 即是在加载 Customer 对象时就被初始化, 还是在程序访问 ------orders 集合时被初始化

-fetch: 取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式;  若取值为”join”, 则决定 orders 集合被初始化的时机

若把 fetch 设置为 “join”, lazy 属性将被忽略

-<set> 元素的 batch-size 属性:用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能. 

 

<set> 元素的 lazy 和 fetch 属性



 

延迟检索和增强延迟检索

 

1) 在延迟检索(lazy 属性值为 true) 集合属性时, Hibernate 在以下情况下初始化集合代理类实例 

-应用程序第一次访问集合属性: iterator(), size(), isEmpty(), contains() 等方法

-通过 Hibernate.initialize() 静态方法显式初始化

2) 增强延迟检索(lazy 属性为 extra): 与 lazy=“true” 类似. 主要区别是增强延迟检索策略能进一步延迟 Customer 对象的 orders 集合代理实例的初始化时机:

-当程序第一次访问 orders 属性的 iterator() 方法时, 会导致 orders 集合代理类实例的初始化

-当程序第一次访问 order 属性的 size(), contains() 和 isEmpty() 方法时, Hibernate 不会初始化 orders 集合类的实例, 仅通过特定的 select 语句查询必要的信息, 不会检索所有的 Order 对象 

 

<set> 元素的 batch-size 属性

 

<set> 元素有一个 batch-size 属性, 用来为延迟检索策略或立即检索策略设定批量检索的数量. 批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能. 

 

用带子查询的 select 语句整批量初始化 orders 集合(fetch 属性为 “subselect”)

 

1) <set> 元素的 fetch 属性: 取值为 “select” 或 “subselect” 时, 决定初始化 orders 的查询语句的形式;  若取值为”join”, 则决定 orders 集合被初始化的时机.默认值为 select 

2) 当 fetch 属性为 “subselect” 时

-假定 Session 缓存中有 n 个 orders 集合代理类实例没有被初始化, Hibernate 能够通过带子查询的 select 语句, 来批量初始化 n 个 orders 集合代理类实例

-batch-size 属性将被忽略

-子查询中的 select 语句为查询 CUSTOMERS 表 OID 的 SELECT 语句 

 

迫切左外连接检索(fetch 属性值设为 “join”)

 

当 fetch 属性为 “join” 时:

-检索 Customer 对象时, 会采用迫切左外连接(通过左外连接加载与检索指定的对象关联的对象)策略来检索所有关联的 Order 对象

-lazy 属性将被忽略

-Query 的list() 方法会忽略映射文件中配置的迫切左外连接检索策略, 而依旧采用延迟加载策略

 

多对一和一对一关联的检索策略

 

和 <set> 一样, <many-to-one> 元素也有一个 lazy 属性和 fetch 属性.



 

-若 fetch 属性设为 join, 那么 lazy 属性被忽略

-迫切左外连接检索策略的优点在于比立即检索策略使用的 SELECT 语句更少. 

-无代理延迟检索需要增强持久化类的字节码才能实现

 

Query 的 list 方法会忽略映射文件配置的迫切左外连接检索策略, 而采用延迟检索策略

如果在关联级别使用了延迟加载或立即加载检索策略, 可以设定批量检索的大小, 以帮助提高延迟检索或立即检索的运行性能. 

Hibernate 允许在应用程序中覆盖映射文件中设定的检索策略.

 

检索策略小结

 

类级别和关联级别可选的检索策略及默认的检索策略



 

3 种检索策略的运行机制



 

 

映射文件中用于设定检索策略的几个属性



 

 

 

 

 

  • 大小: 3.2 KB
  • 大小: 60.7 KB
  • 大小: 62.7 KB
  • 大小: 56.9 KB
  • 大小: 54.7 KB
  • 大小: 114.9 KB
分享到:
评论

相关推荐

    HIBERNATE检索策略

    标题:“HIBERNATE检索策略” 描述:本文深入解析了HIBERNATE的检索策略,为读者提供了一条清晰的学习路径,重点分析了HIBERNATE中的抓取策略及其应用场景,帮助开发者更有效地管理和优化数据加载过程。 在...

    hibernate5--4.检索方式及策略

    一、Hibernate检索方式 1. **HQL(Hibernate Query Language)**: HQL是Hibernate提供的面向对象的查询语言,类似于SQL,但它是基于类和对象的。你可以通过HQL来查询、更新和删除对象,而无需关心底层的SQL语法。...

    Hibernate的检索策略

    ### Hibernate的检索策略详解 #### 一、概述 在Java持久化框架Hibernate中,检索策略是一种重要的机制,它主要用于控制对象何时以及如何加载到应用程序内存中。合理的检索策略不仅可以提高应用性能,还能简化代码...

    基于Spring的Hibernate Search全文检索功能示例

    在现代Web应用开发中,全文检索功能已经成为提升用户体验的关键因素之一。Spring框架是Java领域最广泛使用的应用框架,而Hibernate则是流行的ORM(对象关系映射)解决方案。当这两个强大的工具结合,通过Hibernate ...

    hibernate3数据检索、抓取策略 、 注解的使用

    hibernate3数据检索、抓取策略 、 注解的使用

    hibernate的延迟检索在轻量级J2EE框架中的应用

    ### Hibernate的延迟检索在轻量级J2EE框架中的应用 #### 1. 引言 随着互联网技术的发展,Web应用程序的需求日益增加,对于高效、快速的开发模式也提出了更高的要求。轻量级J2EE框架(Struts+Spring+Hibernate)因...

    hibernate基础 注解开发 检索

    注解作为Java SE 5引入的新特性之一,在Hibernate中被广泛应用于配置实体类和关联关系。 ##### PO类注解配置(掌握) - **@Entity**:声明类为一个实体,即该类将映射到数据库中的某个表。 - **@Table**:用于指定...

    hibernate4+spring4+memcached 工程

    综合以上,这个项目提供了一个实用的示例,展示了如何在Spring 4环境中整合Hibernate 4和Memcached,以实现高性能的数据访问和缓存策略。对于想要学习或实践这些技术的人来说,这是一个极好的起点。通过深入研究和...

    Hibernate4 + Ehcache 例子

    标题 "Hibernate4 + Ehcache 例子" 暗示了我们将探讨如何在Java应用程序中集成Hibernate4 ORM框架和Ehcache缓存系统。这个例子可能是关于如何提高数据访问性能,通过缓存策略来减少数据库查询的次数。 描述中的链接...

    java私塾独家首发最新Hibernate4教程

    ### Hibernate4核心知识点详解 #### 一、Hibernate4简介与入门 **1.1 Hibernate4概述** Hibernate4是一款开源的对象关系映射(Object Relational Mapping, ORM)框架,它简化了Java应用程序与数据库之间的交互...

    Hibernate4学习基础教程(26集)

    内容涵盖安装 Hibernatetools 插件、Session 核心方法、持久化对象生命周期、对象关系映射、检索策略、检索方式、Hibernate 一\二级缓存、管理 Session、批量处理等 Hibernate 企业级开发的核心技术。

    使用 hibernate-search 实现全文检索及文档管理的例子

    **使用Hibernate Search实现全文检索及文档管理** 在Java开发中,全文检索和文档管理是常见的需求,特别是对于大型的企业级应用。Hibernate Search是Hibernate ORM框架的一个扩展,它提供了对数据库中的对象进行...

    hibernate的检索

    【Hibernate检索策略】 Hibernate提供三种主要的检索策略:立即检索、延迟检索和迫切左外连接检索。 1. **立即检索**:这种策略将立即加载关联的对象,无论对象是持久化状态还是游离状态。优点是应用程序可以方便...

    [PDF]Hibernate4(基本开发,入门,配置,CRUD)

    ### Hibernate 4 基础概念 #### Hibernate 是什么? Hibernate 是一个开源的对象关系映射(Object Relational Mapping,简称 ORM)框架,用于简化 Java 应用程序与关系型数据库之间的交互。它通过将 Java 类映射到...

    Hibernate知识点总结

    ### Hibernate知识点总结 #### 一、Hibernate概述 ...通过以上内容,我们了解了Hibernate的基本概念、配置步骤、核心类以及查询语言和检索策略等内容,这对于高效地使用Hibernate框架是非常有帮助的。

    Hibernate实战笔记

    - **Hibernate的n+1查询问题**:是ORM框架中常见的性能瓶颈之一,通过合理配置检索策略可以有效解决这一问题。 - **检索策略选择**:根据应用场景的不同,选择合适的检索策略是非常重要的。延迟检索和迫切左外连接...

    J2EE Hibernate-6.ppt

    《J2EE Hibernate-6.ppt》探讨了Hibernate在J2EE环境中的检索策略,主要涉及立即检索策略、延迟检索策略以及迫切左外连接检索策略。 1. **立即检索策略**: 立即检索策略是Hibernate默认的检索方式。在处理一对多...

    hibernate-search-5.0.1.Final-dis

    描述中提到,这个压缩包是Hibernate框架的一部分,特别提到了是"三大框架之一的hibernate"。在Java世界里,通常所说的三大框架是指Spring、Struts和Hibernate,它们分别处理了应用程序的不同方面,如Spring负责整体...

Global site tag (gtag.js) - Google Analytics