hibernate查漏补缺3

hibernate的检索策略

转载请注明: TheViper http://www.cnblogs.com/TheViper

先看个大概

 

 

 1.类级别

立即检索 <class lazy='false'>;延迟检索 <class lazy='true'>.默认为延迟检索。

如果加载一个持久化对象是为了获取它的属性,用立即检索;而如果仅仅是为了获取它的引用,用延迟加载。比如,

    Friend_Category fc = new Friend_Category();
        fc.setCategory_name("已经存在的好友分类");
        User_Friend_pk pk = new User_Friend_pk();
        pk.setFriend_name(“新好友”);
        pk.setName("TheViper");
        User_Friend uf = new User_Friend();
        uf.setUser_friend_pk(pk);
        Friend_Category fc1 = (Friend_Category) session.load(
                Friend_Category.class, 1);
        uf.setFriend_categorys(fc1);

        // uf.setSort("好基友");
        session.saveOrUpdate(fc1);
        session.save(uf);

可以看到,加载Friend_Catagory类只是为了把新加好友加入到已经存在的好友分类里。这里用load()返回一个代理类,里面只有id属性,其他属性均为null,并没有返回所有的属性,因此占用的内存很少。

 另外,不管<class> 的lazy属性是true还是false,get(),list()总是用立即加载,但是并不会去加载与之相关联的类(关联级别检索为默认设置延迟加载)。

 2.关联级别

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

由<set>的lazy和fetch属性决定

lazy:决定关联集合被初始化的时机。即是在加载时就被初始化,还是在被访问时才初始化。

fetch:取值为select或subselect时,决定初始化关联集合时查询语句的形式;取值为fetch时,决定关联集合被初始化的时机,这时显式设置lazy属性是无意义的。

 lazy=true时,关联集合类的代理在下面情况会被初始化:

1.应用程序第一次访问它,如调用它的iterator(),size(),isEmpty()或contains().

2.通过org.hibernate.Hibernate类的initialize()静态方法初始化。

对于lazy=extra,hibernate会进一步延迟关联对象的初始化时机。具体说就是仅当应用程序第一次调用关联对象的iterator()时才会加载。只有这一个方法能起作用。

对于lazy=false,

Feeling f = (Feeling) session.get(Feeling.class, 1);
        <set name="feelingComments" inverse="true" lazy="false">
            <key column="feeling_id" />
            <one-to-many class="model.FeelingComment" />
        </set>

当取出Feeling类时,便会一并取出FeelingComment类

Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?

 下面设置fetch=subselect,

        Session session = sf.openSession();
        Transaction transaction = session.beginTransaction();
        Query q = session.createQuery("from Feeling feeling");
        List<Feeling> feelings = (List<Feeling>) q.list();
        for (Feeling feeling : feelings) {
            Set comments = feeling.getFeelingComments();
            System.out.println(comments);
        }
        transaction.commit();
        session.close();
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_,
        feeling0_.content as content1_,
        feeling0_.id as id1_ 
    from
        feeling feeling0_
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id in (
            select
                feeling0_.feeling_id  
            from
                feeling feeling0_
        )
[model.FeelingComment@17f2fe3, model.FeelingComment@1211a4b, model.FeelingComment@533fc3]
[model.FeelingComment@a9a994, model.FeelingComment@455d96]
[model.FeelingComment@35c6f]

可以看到hibernate是用in子查询的方式抓取关联对象的,这时batch-size属性会被忽略。

fetch=join表示采用左外连接的检索策略抓取关联对象,注意list()会自动忽略这个设置,这个很重要。

还是上面的代码,结果

Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_,
        feeling0_.content as content1_,
        feeling0_.id as id1_ 
    from
        feeling feeling0_
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@1fbf047, model.FeelingComment@82a092, model.FeelingComment@15bd7c5]
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@c407e, model.FeelingComment@5e3212]
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@69757f]

 而这时设置lazy,batch-size属性没有意义。

 关于batch-size属性,它用来为检索策略设定批量检索的数量,可以减少select语句的数量,提供检索的运行性能。

比如batch-size设为2,关联集合里面有3个对象。

        Session session = sf.openSession();
        Transaction transaction = session.beginTransaction();
        Query q = session.createQuery("from Feeling feeling");
        List<Feeling> feelings = (List<Feeling>) q.list();
        for (Feeling feeling : feelings) {
            Set comments = feeling.getFeelingComments();
            System.out.println(comments);
        }
        transaction.commit();
        session.close();
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_,
        feeling0_.content as content1_,
        feeling0_.id as id1_ 
    from
        feeling feeling0_
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id in (
            ?, ?
        )
[model.FeelingComment@177151, model.FeelingComment@16e125, model.FeelingComment@110a0ca]
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@1c8f247, model.FeelingComment@1d67998]
[model.FeelingComment@ac44e3]

注意,如果对Query接口用iterator()返回数据的话,会忽略batch-size!

Hibernate: 
    select
        feeling0_.feeling_id  as col_0_0_ 
    from
        feeling feeling0_
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@ee7d2a, model.FeelingComment@fb3758, model.FeelingComment@17596bc]
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@855e17]
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?
[model.FeelingComment@85ba73, model.FeelingComment@191bae7]

iterator()参见上一篇说明.

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

 

fetch=join

        <set name="feelingComments" inverse="true">
            <key column="feeling_id" />
            <one-to-many class="model.FeelingComment" />
        </set>
        <many-to-one name="feelings" column="feeling_id" class="model.Feeling"
            not-null="false" fetch="join">
        </many-to-one>
        FeelingComment f = (FeelingComment) session
                .get(FeelingComment.class, 1);
Hibernate: 
    select
        feelingcom0_.feelingComment_id  as feelingC1_2_1_,
        feelingcom0_.content as content2_1_,
        feelingcom0_.feeling_id as feeling3_2_1_,
        feelingcom0_.id as id2_1_,
        feeling1_.feeling_id  as feeling1_1_0_,
        feeling1_.content as content1_0_,
        feeling1_.id as id1_0_ 
    from
        feeling_comment feelingcom0_ 
    left outer join
        feeling feeling1_ 
            on feelingcom0_.feeling_id=feeling1_.feeling_id  
    where
        feelingcom0_.feelingComment_id =?

上面的很简单。稍微变一下,在<set>添加lazy=false.

Hibernate: 
    select
        feelingcom0_.feelingComment_id  as feelingC1_2_1_,
        feelingcom0_.content as content2_1_,
        feelingcom0_.feeling_id as feeling3_2_1_,
        feelingcom0_.id as id2_1_,
        feeling1_.feeling_id  as feeling1_1_0_,
        feeling1_.content as content1_0_,
        feeling1_.id as id1_0_ 
    from
        feeling_comment feelingcom0_ 
    left outer join
        feeling feeling1_ 
            on feelingcom0_.feeling_id=feeling1_.feeling_id  
    where
        feelingcom0_.feelingComment_id =?
Hibernate: 
    select
        feelingcom0_.feeling_id as feeling3_1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_1_,
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feeling_id=?

可以看到,这次发出了两条sql,第二次会再次根据feeling_id取出feeling_comment,个人感觉没意义啊。而在一对多和多对多中就不会出现这种情况。

另外,list()也会像一对多和多对多,忽略fetch=join.

lazy=proxy(默认)

这个像一对多和多对多里面的lazy=true.即查询“多”那边时,不会查询“一”那边的关联对象。只有当实际要用关联对象时才会初始化它。

注意对于<one-to-one>,必须要设置constrained=true.表明“一”那边的关联对象不能为空。

lazy=no-proxy

这个和lazy=proxy很相似。不过需要编译时字节码增强,否则和proxy没区别 。

这样做了后的效果。

        FeelingComment f = (FeelingComment) session
                .get(FeelingComment.class, 1);
        Feeling f1 = f.getFeelings();
        f1.getContent();

当运行到getFeelings()时,hibernate会发出sql.而lazy=proxy时,要运行到getContent()时才发出。

lazy=false

当运行到get()时就会发出两条sql.

Hibernate: 
    select
        feelingcom0_.feelingComment_id  as feelingC1_2_0_,
        feelingcom0_.content as content2_0_,
        feelingcom0_.feeling_id as feeling3_2_0_,
        feelingcom0_.id as id2_0_ 
    from
        feeling_comment feelingcom0_ 
    where
        feelingcom0_.feelingComment_id =?
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?

另外如果

        <many-to-one name="feelings" column="feeling_id" class="model.Feeling"
            not-null="false" lazy="false">

同时在“一”那边

    <class name="model.Feeling" table="feeling" lazy="false">
    </class>

便会出现和fetch=join一样的sql.

        FeelingComment f = (FeelingComment) session
                .get(FeelingComment.class, 1);
Hibernate: 
    select
        feelingcom0_.feelingComment_id  as feelingC1_2_1_,
        feelingcom0_.content as content2_1_,
        feelingcom0_.feeling_id as feeling3_2_1_,
        feelingcom0_.id as id2_1_,
        feeling1_.feeling_id  as feeling1_1_0_,
        feeling1_.content as content1_0_,
        feeling1_.id as id1_0_ 
    from
        feeling_comment feelingcom0_ 
    left outer join
        feeling feeling1_ 
            on feelingcom0_.feeling_id=feeling1_.feeling_id  
    where
        feelingcom0_.feelingComment_id =?

在多对一和一对一中使用批量检索

在<many-to-one>,<one-to-one>中没有batch-size属性,需要“一”那边设置batch-size.比如,需要加装三个关联实例(一),batch-size=2.

    <class name="model.Feeling" table="feeling" batch-size="2">
    </class>
        Query q = session.createQuery("from FeelingComment");
        List<FeelingComment> feelings = (List<FeelingComment>) q.list();
        for (FeelingComment feelingComment : feelings) {
            Feeling feeling = feelingComment.getFeelings();
            System.out.println(feeling);
        }
Hibernate: 
    select
        feelingcom0_.feelingComment_id  as feelingC1_2_,
        feelingcom0_.content as content2_,
        feelingcom0_.feeling_id as feeling3_2_,
        feelingcom0_.id as id2_ 
    from
        feeling_comment feelingcom0_
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id  in (
            ?, ?
        )
model.Feeling@1e684bd
model.Feeling@1e684bd
model.Feeling@1e684bd
model.Feeling@1149b6c
Hibernate: 
    select
        feeling0_.feeling_id  as feeling1_1_0_,
        feeling0_.content as content1_0_,
        feeling0_.id as id1_0_ 
    from
        feeling feeling0_ 
    where
        feeling0_.feeling_id =?
model.Feeling@fb8996
model.Feeling@1149b6c

注意上面的例子<many-to-one>中的lazy是默认的proxy.如果lazy=false的话,当运行到feelingComment.getFeelings();时就会发出后面的sql了。

  • 属性级别的检索策略

只需在<property>,<component>上设置lazy=true.这种策略适用于二进制大对象,字符串大对象,大容量组件类型的属性。比如User类的image属性储存着图片的二进制数据,

User user=(User)session.get(User.class,1);user.getImage();

get()时,hibernate没有加载image属性,运行到getImage()时才会加载。

 注意,用的时候必须向多对一,一对一lazy=no-proxy一样,需要编译时字节码增强。

原文地址:https://www.cnblogs.com/TheViper/p/4116377.html