我在学生和地址之间有一个二者关系。我希望Student的firstName和lastName字段是延迟加载的。我也想要地址字段懒惰。
这些是我的实体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 + "]";
}
}
地址类:
@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 + "]";
}
}
我的测试方法看起来像这样(Java 8 lambda只是在后面创建一个实体管理器并在事务中执行所有操作):
@Test
public void dummyTest() {
JPA_UTILS.runInTransaction(e -> {
Student s = e.find(Student.class, 150L);
System.out.println("----------++++++++++++++-----------");
s.getFirstName();
System.out.println("----------++++++++++++++-----------");
});
}
所以这里我从数据库加载一个现有的学生,然后获取lazy属性firstName(映射到first_name列)。问题是Hibernate不仅加载firstName而且加载lastName和地址字段:
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=?
----------++++++++++++++-----------
我不想要这种行为,我只想加载我要求的内容。有人可以帮我找到问题吗?
由于
UPDATE1:
使用maven完成仪表,我只发布相关代码(我已尝试使用gradle并获得相同的结果)
<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>
答案 0 :(得分:4)
有两个基本资源可以正确解决这个问题。首先,属性的延迟加载不是标准设置。参见:
Hibernate支持延迟获取各个属性。此优化技术也称为提取组。 请注意,这主要是营销功能;优化行读取比列读取的优化要重要得多。但是,只加载类的某些属性在极端情况中可能很有用。
其次,根据上述文档部分,有一篇详细的文章:
虽然这与NHibernate有关,但内容是相同的,因为这个功能来自Hibernate。
我们可以在那里阅读:
多个懒惰属性怎么样? NHibernate支持它们,但你需要记住一件事。 NHibernate将加载所有实体的延迟属性,而不仅仅是那些立即访问的属性。同样的道理,你不能急于从HQL中加载一些实体的惰性属性。
同样,我们可以理解,属性的lazy
设置不是标准设置。它适用于某些非常具体的情况,例如:
此功能主要用于特殊情况,例如 Person.Image , Post.Text 等。像往常一样,请谨慎使用它。
摘要:延迟加载属性(ValueTypes / strings,not associations)适用于特殊情况。它应该有助于我们避免加载一些巨大的列。但是,一旦访问了这样的属性,所有其他属性也会加载
答案 1 :(得分:0)
对于one2one关系,我认为以下文章解释得很好: http://justonjava.blogspot.co.uk/2010/09/lazy-one-to-one-and-one-to-many.html
关于字段lastName,这是一个基本类型字段。 在Hibernate中,基本类型字段总是一次被查询,如果你声明它们被加载为懒惰则重新进行查询。
答案 2 :(得分:0)
我不知道你对它的了解所以我会解释;如果你已经知道大部分内容我为任何冒犯道歉。
根据经验,我们最初可以这样说:
@Entity
public class Student
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(mappedBy = "student", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Address address;
现在观察术语FetchType.Eager。所以在'Page1'中我们可以说有一个学生列表。然后,如果你点击学生名称,它应该转到'Page2'。因此,在“Page2”中,您需要显示学生的地址。
因此,在代码中,如果执行FetchType.Eager,则不必执行两个查询。所有你要做的就是学生s =“从学生中选择s,其中id ='1'”;
然后将此Object发送到'Page2'并执行System.out.println(s.getAddress());
现在只有当FetchType为Eager时才会这样。但如果它是懒惰的。您需要另一个查询,因为之前的错误会抛出一个惰性异常。那么你需要这样做 “从第2页的地址a中选择一个a.student.id =':id',然后执行System.out.println(resultOfSecondQuery);
现在这是我的经验,因为我们首先获得了EAGER然后它被改为LAZY并且错误开始在多个地方突然出现。因为我们不能再做s.getAddress()了。
在你的测试块中,看起来你得到了所有学生,然后打印出名字。您是否曾尝试在代码中打印出地址?它打印出来还是抛出错误?我不知道你的第四个街区在做什么。你写过那个或是Hibernate做了什么吗?
之证件:
http://java.dzone.com/articles/lazyeager-loading-using http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#entity-hibspec-singleassoc-fetching http://howtoprogramwithjava.com/hibernate-eager-vs-lazy-fetch-type/
同样在jboss-hibernate的文档中说,你不需要延迟加载像lastName这样的简单属性。我知道你可能只是练习,但无论如何,lastName通常都是急切的。然而,像地址这样的东西是不同的。
答案 3 :(得分:0)
使用Hibernate,您可以使用@Basic(fetch=FetchType.LAZY)
懒惰地加载列,但为此工作Hibernate需要字节码检测来拦截属性访问请求并按需发出辅助选择语句。如果使用Maven字节码增强插件,然后enableLazyInitialization
配置属性必须设置为true:
<plugin>
<groupId>org.hibernate.orm.tooling</groupId>
<artifactId>hibernate-enhance-maven-plugin</artifactId>
<version>${hibernate.version}</version>
<executions>
<execution>
<configuration>
<failOnError>true</failOnError>
<enableLazyInitialization>true</enableLazyInitialization>
</configuration>
<goals>
<goal>enhance</goal>
</goals>
</execution>
</executions>
</plugin>
以上插件有其自身的缺点。因此,如果您只需要某些列,则可以编写命名查询,仅请求
这些列TypedQuery<ViewTemplate> query=getEntityManager().createQuery("SELECT NEW com.abcd.test.Table(v.id,v.name,v.type) FROM ViewTemplate v where v.id>1",Table.class).getResultList();
在您的实体中定义一个只包含这些值的构造函数