当要求加载一个懒惰的字段时,Hibernate会加载所有懒惰的字段 [英] When asked to load a lazy field, Hibernate loads all lazy fields

查看:103
本文介绍了当要求加载一个懒惰的字段时,Hibernate会加载所有懒惰的字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在学生和地址之间有一对一关系。我想让学生的firstName和lastName字段延迟加载。



这些是我的实体分类:

  @Entity 
@Table(name =students)
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
私人长ID;

@OneToOne(mappedBy =student,cascade = CascadeType.ALL,fetch = FetchType.LAZY)
@LazyToOne(LazyToOneOption.NO_PROXY)
私有地址;

@Basic(fetch = FetchType.LAZY)
@Column(name =first_name)
private String firstName;

@Basic(fetch = FetchType.LAZY)
@Column(name =last_name)
private String lastName;

public Long getId(){
return id;
}

public void setId(Long id){
this.id = id;
}

public Address getAddress(){
return address;
}

public void setAddress(Address address){
this.address = address;
}

public String getFirstName(){
return firstName;
}

public void setFirstName(String firstName){
this.firstName = firstName;
}

public String getLastName(){
return lastName;
}

public void setLastName(String lastName){
this.lastName = lastName;

$ b @Override
public String toString(){
returnStudent [id =+ id +,address =+ address +,firstName =
+ firstName +,lastName =+ lastName +];
}

}

地址类别:

  @Entity 
@Table(name =addresses)
公共类地址{

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private长ID;

@OneToOne
@JoinColumn(name =s_id)
私立学生;

@Column
private String street;

public Long getId(){
return id;
}

public void setId(Long id){
this.id = id;
}

public Student getStudent(){
return student;
}

public void setStudent(Student student){
this.student = student;
}

public String getStreet(){
return street;
}

public void setStreet(String street){
this.street = street;

$ b @Override
public String toString(){
returnAddress [id =+ id +,street =+ street +] ;
}

}

我的测试方法看起来像这样Java 8 lambda只是在后面创建一个entitymanager并且执行所有事务):

  @Test 
public void dummyTest(){
JPA_UTILS.runInTransaction(e - > {
Student s = e.find(Student.class,150L);
System.out.println(---- ------ ++++++++++++++ -----------);
s.getFirstName();
System。 out.println(---------- ++++++++++++++ -----------);
});





$ b

所以我在这里从数据库加载一个现有的学生,然后获取懒惰属性firstName(映射到first_name列)。问题是,Hibernate不仅加载firstName,而且加载姓和地址字段:

  just.hibernate.one2one.TestApp> ; dummyTest STANDARD_OUT 
Hibernate:
选择
student0_.id作为id1_1_0_

学生student0_
其中
student0_.id =?
---------- ++++++++++++++ -----------
Hibernate:
/ *顺序选择
just.hibernate.one2one.Student * / select
student_.first_name as first_na2_1_,
student_.last_name as last_nam3_1_
from
students student_
其中
student_.id =?
Hibernate:
/ * load just.hibernate.one2one.Address * / select
address0_.id as id1_0_1_,
address0_.street as street2_0_1_,
address0_.s_id as s_id3_0_1_,
student1_.id as id1_1_0_
from
addresses address0_
left outer join
students student1_
on address0_.s_id = student1_.id
其中
address0_.s_id =?
---------- ++++++++++++++ -----------

我不想要这种行为,我只想加载我请求的内容。有人可以帮我找到问题吗?

谢谢b

UPDATE1:

使用maven完成检测,我只发布相关的代码(我已经用gradle和相同的结果尝试过)

 <插件> 
< groupId> org.apache.maven.plugins< / groupId>
< artifactId> maven-antrun-plugin< / artifactId>
<执行次数>
<执行>
<阶段>程序类< /阶段>
<目标>
< goal>跑步< / goal>
< /目标>
< /执行>
< /执行次数>
<配置>
<任务>
< taskdef name =instrument
classname =org.hibernate.tool.instrument.javassist.InstrumentTask>
< classpath>
< path refid =maven.runtime.classpath/>
<路径refid =maven.plugin.classpath/>
< / classpath>
< / taskdef>
< instrument verbose =false>
< fileset dir =$ {project.build.outputDirectory}>
< include name =** / *。class>
< / include>
< / fileset>
< / instrument>
< /任务>
< / configuration>
< / plugin>


解决方案

正确解决此问题有两个基本资源。首先,属性的延迟加载不是标准设置。请参阅:




Hibernate支持单个属性的延迟读取。这种优化技术也被称为获取组。 请注意,这主要是一种营销功能;优化行读取比列读取的优化要重要得多。然而,只有加载某些类的某些属性才能用于极端情况


blockquote>

其次,基于上述文档部分,有一篇详细的文章:


  • NHibernate新功能:懒惰属性



  • $ b

    虽然这与NHibernate有关,但内容相同,因为此功能来自Hibernate。



    我们可以在那里阅读:


    多重惰性属性怎么样? NHibernate支持它们,但是你需要记住一件事。 NHibernate会加载所有实体的懒惰属性,而不仅仅是被立即访问的属性。同样的道理,你不能急于从HQL中加载一些实体的懒惰属性。


    同样,我们可以理解设置属性的 lazy 设置不是标准设置。它用于一些非常特殊的情况,例如:


    此功能主要用于特殊情况,如 Person.Image Post.Text 等。像往常一样,谨慎使用它。 > 摘要:特殊情况下,延迟加载属性(ValueTypes /字符串,而不是关联)。它应该帮助我们避免加载一些巨大的列。但是,一旦这样的财产被访问,所有其他人也正在加载

    I have a one2one relation between Student and Address. I want the firstName and lastName fields of Student to be lazy loaded. Also I want lazy for the address field.

    These are my entity clasess:

    @Entity
    @Table(name = "students")
    public class Student {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @OneToOne(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        @LazyToOne(LazyToOneOption.NO_PROXY)
        private Address address;
    
        @Basic(fetch = FetchType.LAZY)
        @Column(name = "first_name")
        private String firstName;
    
        @Basic(fetch = FetchType.LAZY)
        @Column(name = "last_name")
        private String lastName;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        @Override
        public String toString() {
            return "Student [id=" + id + ", address=" + address + ", firstName="
                    + firstName + ", lastName=" + lastName + "]";
        }
    
    }
    

    The Address class:

    @Entity
    @Table(name = "addresses")
    public class Address {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @OneToOne
        @JoinColumn(name = "s_id")
        private Student student;
    
        @Column
        private String street;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public Student getStudent() {
            return student;
        }
    
        public void setStudent(Student student) {
            this.student = student;
        }
    
        public String getStreet() {
            return street;
        }
    
        public void setStreet(String street) {
            this.street = street;
        }
    
        @Override
        public String toString() {
            return "Address [id=" + id + ", street=" + street + "]";
        }
    
    }
    

    My test method looks like this ( the Java 8 lambda is just creating an entitymanager in back and executes all in transaction ):

    @Test
    public void dummyTest() {
        JPA_UTILS.runInTransaction(e -> {
            Student s = e.find(Student.class, 150L);
            System.out.println("----------++++++++++++++-----------");
            s.getFirstName();
            System.out.println("----------++++++++++++++-----------");
        });
    }
    

    So here I am loading an existing student from the database, then fetch the lazy property firstName ( mapped to first_name column ). The problem is that Hibernate doesn't load only firstName but also lastName and address fields:

    just.hibernate.one2one.TestApp > dummyTest STANDARD_OUT
        Hibernate: 
            select
                student0_.id as id1_1_0_ 
            from
                students student0_ 
            where
                student0_.id=?
        ----------++++++++++++++-----------
        Hibernate: 
            /* sequential select
                just.hibernate.one2one.Student */ select
                    student_.first_name as first_na2_1_,
                    student_.last_name as last_nam3_1_ 
                from
                    students student_ 
                where
                    student_.id=?
        Hibernate: 
            /* load just.hibernate.one2one.Address */ select
                address0_.id as id1_0_1_,
                address0_.street as street2_0_1_,
                address0_.s_id as s_id3_0_1_,
                student1_.id as id1_1_0_ 
            from
                addresses address0_ 
            left outer join
                students student1_ 
                    on address0_.s_id=student1_.id 
            where
                address0_.s_id=?
        ----------++++++++++++++-----------
    

    I don't want this behavior, I want to load only what I request. Can someone help me find the problem ?

    Thanks

    UPDATE1:

    Instrumentation is done with maven, I'm posting only the relevant code ( I've tried with gradle and the same results )

    <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <executions>
                        <execution>
                            <phase>process-classes</phase>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <tasks>
                            <taskdef name="instrument"
                                classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                                <classpath>
                                    <path refid="maven.runtime.classpath" />
                                    <path refid="maven.plugin.classpath" />
                                </classpath>
                            </taskdef>
                            <instrument verbose="false">
                                <fileset dir="${project.build.outputDirectory}">
                                    <include name="**/*.class">
                                    </include>
                                </fileset>
                            </instrument>
                        </tasks>
                    </configuration>
                </plugin>
    

    解决方案

    There are two essential resources to properly undestand this issue. Firstly, the lazy loading of properties is not the standard setting. See:

    Hibernate supports the lazy fetching of individual properties. This optimization technique is also known as fetch groups. Please note that this is mostly a marketing feature; optimizing row reads is much more important than optimization of column reads. However, only loading some properties of a class could be useful in extreme cases.

    Secondly, based on the above doc part, there is a detailed article about that:

    While this is related to NHibernate, the content is the same, because this feature comes from Hibernate.

    We can read there:

    What about multiple lazy properties? NHibernate support them, but you need to keep one thing in mind. NHibernate will load all the entity’s lazy properties, not just the one that was immediately accessed. By that same token, you can’t eagerly load just some of an entity’s lazy properties from HQL.

    And again, we can understand that lazy setting for a property is not a standard setting. It is intended for some very specific cases, e.g.:

    This feature is mostly meant for unique circumstances, such as Person.Image, Post.Text, etc. As usual, be cautious in over using it.

    SUMMARY: Lazy loading of properties (ValueTypes/strings, not associations) is there for special cases. It should help us to avoid loading some huge columns. BUT, once such a property is accessed, all others are loading as well

    这篇关于当要求加载一个懒惰的字段时,Hibernate会加载所有懒惰的字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆