hibernate 延迟加载


延迟加载
延迟加载:
当hibernate从数据库中加载某个对象时,不加载关联的对象,而只是生成了代理对象。使用session中的load的方法(在没有改变lazy属性,属性值为true的情况下)获取到的也是代理对象。

立即加载:
当Hibernate从数据库中加载某个对象时,加载关联的对象,生成实际的对象。使用session中的get的方法获取到的也是实际对象。

延迟加载的好处:
延迟加载策略能避免加载应用程序不需要访问的关联对象,以提高应用程序的性能。

立即加载的缺点:
Hibernate在查询某个对象时,立即查询与之关联的对象:
1、当select的语句数目太多,需要频繁的访问数据库,会影响查询的性能。
2、在应用程序只需要访问要的对象,而不需要访问与他关联的对象的场景下,加载与之关联的对象完全是多余的操作,这些多余的操作是会占内存,这就造成了内存空间的浪费。

延迟加载的原理图:

Hibernate的lazy生效期:
生效期和session一样的,session关闭,lazy失效。hibernate支持lazy策略只在session打开状态下有效。如果session已经关闭了,则会抛出LazyInitalizationException异常
Hibernate lazy属性,在3.x后是默认打开的,在以前版本中默认是关闭的

hibernate在对象关系映射文件中配置加载策略的方式:(lazy)
1、类级别
元素中lazy属性的可选值为true(延迟加载)和false(立即加载);
元素中的lazy属性的默认值为true
2、一对多关联级别:
元素中的lazy属性的可选值为:true(延迟加载),extra(增强延迟加载)和false(立即加载);
元素中的lazy属性的默认值为true
extra其实是一种比较智能的延迟加载,即调用集合的size/contains等方法的时候,hibernate并不会去加载整个集合的数据,而是发出一条聪明的SQL语句,以便获得需要的值,只有在真正需要用到这些集合元素对象数据的时候,才去发出查询语句加载所有对象的数据。
3、多对一关联级别:
元素中lazy属性的可选值为:proxy(延迟加载),no-proxy(无代理延迟加载)和false(立即加载)
元素中的lazy属性的默认值为proxy

load和hibernate的主要的区别:
1、load采用的是延迟加载,get 不能采用延迟加载技术而是立刻加载
2、load方法认为数据库中一定存在要检索的数据,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,那么只能抛出异常ObjectNotFoundException;而对于get方法,Hibernate一定要获取到真实的数据才会返回对象,否则返回null。
3、load方法可以返回一个没有加载实体数据的代理类实例,而get方法永远返回有实体数据的对象。
(对于load和get方法返回类型:好多书中都说:“get方法永远只返回实体类”,实际上并不正确,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数 据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。)

下面介绍get和post方法的具体用法及事例。

我的工具类:主要是用来创建session工厂并打开session的。下面两个事例中会用到这个类

public class SessionFactoryUtils {
    private SessionFactory factory;
    private static SessionFactoryUtils factoryUtils;

    // 单例模式:把构造方法设置为私有的,说明不可以new这个类的实例。
    private SessionFactoryUtils() {
    }

    // 通过定义这个类的静态方法,并且返回类型与这个类的类型一样,来实现对这个类的访问
    public static SessionFactoryUtils getInstance() {
        if (factoryUtils == null) {
            factoryUtils = new SessionFactoryUtils();
        }
        return factoryUtils;
    }

    public SessionFactory openSessionFactory() {
        if (factory == null) {
            //加载主配置文件
            Configuration configuration = new Configuration().configure();

            //建立工厂
            factory = configuration.buildSessionFactory();
        }
        return factory;
    }
}

get方法的加载方式
1、因为hibernate规定get方法不使用延迟加载,所以hibernate会去确认该id对应的数据是否存在
2、它会首先在session缓存中查找
3、如果session中找不到,就会去二级缓存中查找
4、如果二级缓存中还是没有找到,就会去数据库中查找
5、如果数据库中还是找不到就返回null。

代理对象实际就是空的对象,并没有去数据库中查询,所以我们叫做代理对象。如果取数据库查询了,返回了真实的对象,我们就叫做实体对象。

junit测试类:测试get方法

@org.junit.Test
public void query() {
    //调用我上面定义的工具类,通过调用定义的方法来创建session工厂并打开session
    private  SessionFactory sessionFactory = SessionFactoryUtils.getInstance().openSessionFactory();
    Session session = sessionFactory.openSession();

    Admins adminsGet = session.get(Admins.class, 4);
    System.out.println(adminsGet);
}

控制台输出:可以发现在直接得到id的时候,它就发起了sql语句去数据库中查询了。并没有延迟加载。

Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4
-------
4,nowyou

Load的加载方式
1、Load采用延迟加载的方式,hibernate的思想是既然这个方法支持延迟加载,它就认为这个对象一定在数据库中存在,可以放心的使用代理来延迟加载,如果在使用过程中出现了问题就放心的抛异常
2、Load方法会首先查询session缓存,看缓存中有没有这个对象
3、如果缓存中没有这个对象就会去创建个代理对象来管理,因为延迟加载需要代理来执行。但是并没有去数据库中查询
4、只有当你实际使用这个对象的时候,它才会触发sql语句。这个时候hibernate就会去查询二级缓存和数据库,如果数据库中没有这条语句,就抛出异常ObjectNotFoundException。

hibernate load方法加载实体对象的时候,会根据映射文件上 类级别 lazy属性值的配置,分情况讨论:
(1)若为true,即为延迟加载,就是上面的模式
(2)若为false,即为非延迟加载,即立即加载。就跟get方法查找顺序一样,只是最终若没发现符合条件的记录,则会抛出一个ObjectNotFoundException。
junit测试类:load方法:

@org.junit.Test
    public void query() {
        //调用我上面定义的工具类,通过调用定义的方法来创建session工厂并打开session
        private  SessionFactory sessionFactory = SessionFactoryUtils.getInstance().openSessionFactory();
        Session session = sessionFactory.openSession();

         Admins adminsLoad = session.load(Admins.class, 4);
         System.out.println(adminsLoad.getAid());
         System.out.println("-------");

         System.out.println(adminsLoad.getAid()+","+adminsLoad.getAdminname());
    }

1、当类级别lazy属性值为false,即不适用延迟加载的时候:

<hibernate-mapping>
    <class name="org.danni.model.entity.Admins" table="admins" lazy="false">
        <id name="aid" column="aid">
            <generator class="native"></generator>
        </id>
        <property name="adminname" column="adminname"></property>
        <property name="adminpwd" column="adminpwd"></property>
    </class>
</hibernate-mapping>

控制台输出:可以发现没有采用延迟加载,当直接输出id就调用了sql去查询,采用立即查询的方式

Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4
-------
4,nowyou

2、当lazy属性为true的时候,即采用延迟加载:

<hibernate-mapping>
    <class name="org.danni.model.entity.Admins" table="admins" lazy="true">
        <id name="aid" column="aid">
            <generator class="native"></generator>
        </id>
        <property name="adminname" column="adminname"></property>
        <property name="adminpwd" column="adminpwd"></property>
    </class>
</hibernate-mapping>

控制台输出:可以发现,当只是得到id的值的时候并没有取数据库中查询,而是创建了个代理对象来管理。这个代理对象中只保存了实体对象的id。但是当使用这个对象,获得这个对象的名字的时候他才触发了sql语句,它才去二级缓存、数据库中查找。

4
-------
Hibernate: select admins0_.aid as aid1_0_0_, admins0_.adminname as adminnam2_0_0_, admins0_.adminpwd as adminpwd3_0_0_ from admins admins0_ where admins0_.aid=?
4,nowyou


原文链接:https://www.cnblogs.com/xiaohu666/p/11419912.html