类级别的检索策略
知识点
类级别可选的检索策略包括立即检索和延迟检索, 默认为延迟检索。
- 立即检索: 立即加载检索方法指定的对象;
- 延迟检索: 延迟加载检索方法指定的对象。在使用具体的属性时,再进行加载。
类级别的检索策略可以通过 元素的 lazy 属性进行设置。
如果程序加载一个对象的目的是为了访问它的属性,可以采取立即检索;如果程序加载一个持久化对象的目的是仅仅为了获得它的引用,可以采用延迟检索,但 需要注意懒加载异常 (LazyInitializationException:简单理解该异常就是 Hibernate 在使用延迟加载时,并没有将数据实际查询出来,而只是得到了一个代理对象,当使用属性的时候才会去查询,而如果这个时候 session 关闭了,则会报该异常)!
下面通过一个例子来进行讲解:
Demo
在该 Demo 中,我们只需要使用一个 Customer 的对象即可,其中包含了 id,name 等属性。
延迟检索
首先我们来测试一下元素的 lazy 属性为 true 的情况,也就是默认情况(不设置)。
public class HibernateTest {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@After
public void destroy() {
transaction.commit();
session.close();
sessionFactory.close();
}
@Test
public void testClassLevelStrategy() {
Customer customer = (Customer) session.load(Customer.class, 1);
System.out.println(customer.getClass());
System.out.println(customer.getCustomerId());
System.out.println(customer.getCustomerName());
}
}
看一下上面的代码,该代码是利用 Junit 进行测试(关于 Junit 的知识在这不多说,并不是重点)。其中 init 方法是对 SessionFactory、Session 等进行初始化,destroy 方法是进行关闭。
当我们运行 testClassLevelStrategy() 方法时,会得到以下输出结果:
class com.atguigu.hibernate.strategy.Customer_$$_javassist_1
1
Hibernate:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
AA
通过控制台的输出结果,我们可以清楚的看到,当我们执行 session.load(Customer.class, 1)
方法时,Hibernate 并没有发送 SQL 语句,而只是返回了一个对象,通过调用 getClass() 方法,可以看到该对象 class com.atguigu.hibernate.strategy.Customer_$$_javassist_1
是一个代理对象,并且当调用 customer.getCustomerId()
获取 ID 的时候,也没有发送 SQL 语句;当我们这个再调用 customer.getCustomerName()
方法来得到 name 的时候,这个时候才发送了 SQL 语句进行真正的查询,并且 WHERE 条件中带上的就是 ID。
如果我们在 System.out.println(customer.getCustomerName());
语句前插入 session.close()
将 Session 关闭,就能看到之前上文中提到的懒加载异常。
立即检索
为了让 Customer 类可以立即检索,我们要修改 Customer.hbm.xml 文件,在标签中加入 lazy="false"。
<hibernate-mapping package="com.atguigu.hibernate.strategy">
<class name="Customer" table="CUSTOMERS" lazy="false">
<id name="customerId" type="java.lang.Integer">
<column name="CUSTOMER_ID" />
<generator class="native" />
</id>
...
这个时候,我们再运行之前的单元测试代码,控制台会得到以下输出结果:
Hibernate:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_0_,
customer0_.CUSTOMER_NAME as CUSTOMER2_0_0_
from
CUSTOMERS customer0_
where
customer0_.CUSTOMER_ID=?
class com.atguigu.hibernate.strategy.Customer
1
AA
我们可以看到,当调用 load 方法时,会发送 SQL 语句,并且得到的不再是代理对象。这个时候就算我们在输出 name 属性之前将 session 关闭,也不会报错。
小结
上文中对 Hibernate 的类级别的检索策略进行了总结,当然这些是建立在有一定基础的前提下。 需要注意的是:
- 无论元素的 lazy 属性是 true 还是 false,Session 的 get() 方法及 Query 的 list() 方法在类级别总是使用立即检索策略。
- 若 元素的 lazy 属性为 true 或取默认值, Session 的 load() 方法不会执行查询数据表的 SELECT 语句,仅返回代理类对象的实例,该代理类实例有如下特征:
- 由 Hibernate 在运行时采用 CGLIB 工具动态生成;
- Hibernate 创建代理类实例时,仅初始化其 OID 属性;
- 在应用程序第一次访问代理类实例的非 OID 属性时, Hibernate 会初始化代理类实例。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论