我定义了两个实体:School
和District
。一个地区可以有很多学校,学校可以属于一个学区
在针对此端点GET
执行http://localhost:8080/districts
请求时,我希望获得所有区域的列表,而不会获取每个区域的相关学校集。但似乎无论我做什么,hibernate都会调用DB来为每个学校单独获取数据。
@Getter
@Setter
@NoArgsConstructor
@Entity
public class School {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
@Column(unique=true)
private Long number;
@NotNull
@Column
private String name;
@NotNull
private boolean closed;
@Embedded
private ContactInfo contactInfo;
private String gradeLow;
private String gradeHigh;
private int enrollment;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "district_id")
private District district;
}
@Getter
@Setter
@NoArgsConstructor
@Entity
public class District {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(unique = true)
private Integer number;
private String name;
private String type;
private int enrollment;
private Date updated;
@Embedded
private ContactInfo contactInfo;
@Getter(AccessLevel.NONE)
@JsonIgnore
@OneToMany(fetch = FetchType.LAZY, mappedBy = "district")
private Set<School> schoolList;
}
SELECT district0_.id AS id1_5_,
district0_.city AS city2_5_,
district0_.email AS email3_5_,
district0_.fax AS fax4_5_,
district0_.first_name AS first_na5_5_,
district0_.last_name AS last_nam6_5_,
district0_.name_prefix AS name_pre7_5_,
district0_.phone AS phone8_5_,
district0_.state AS state9_5_,
district0_.street AS street10_5_,
district0_.title AS title11_5_,
district0_.website AS website12_5_,
district0_.zip AS zip13_5_,
district0_.enrollment AS enrollm14_5_,
district0_.NAME AS name15_5_,
district0_.number AS number16_5_,
district0_.type AS type17_5_,
district0_.updated AS updated18_5_
FROM district district0_
SELECT schoollist0_.district_id AS distric20_7_0_,
schoollist0_.id AS id1_7_0_,
schoollist0_.id AS id1_7_1_,
schoollist0_.closed AS closed2_7_1_,
schoollist0_.city AS city3_7_1_,
schoollist0_.email AS email4_7_1_,
schoollist0_.fax AS fax5_7_1_,
schoollist0_.first_name AS first_na6_7_1_,
schoollist0_.last_name AS last_nam7_7_1_,
schoollist0_.name_prefix AS name_pre8_7_1_,
schoollist0_.phone AS phone9_7_1_,
schoollist0_.state AS state10_7_1_,
schoollist0_.street AS street11_7_1_,
schoollist0_.title AS title12_7_1_,
schoollist0_.website AS website13_7_1_,
schoollist0_.zip AS zip14_7_1_,
schoollist0_.district_id AS distric20_7_1_,
schoollist0_.enrollment AS enrollm15_7_1_,
schoollist0_.grade_high AS grade_h16_7_1_,
schoollist0_.grade_low AS grade_l17_7_1_,
schoollist0_.NAME AS name18_7_1_,
schoollist0_.number AS number19_7_1_
FROM school schoollist0_
WHERE schoollist0_.district_id = ?
SELECT schoollist0_.district_id AS distric20_7_0_,
schoollist0_.id AS id1_7_0_,
schoollist0_.id AS id1_7_1_,
schoollist0_.closed AS closed2_7_1_,
schoollist0_.city AS city3_7_1_,
schoollist0_.email AS email4_7_1_,
schoollist0_.fax AS fax5_7_1_,
schoollist0_.first_name AS first_na6_7_1_,
schoollist0_.last_name AS last_nam7_7_1_,
schoollist0_.name_prefix AS name_pre8_7_1_,
schoollist0_.phone AS phone9_7_1_,
schoollist0_.state AS state10_7_1_,
schoollist0_.street AS street11_7_1_,
schoollist0_.title AS title12_7_1_,
schoollist0_.website AS website13_7_1_,
schoollist0_.zip AS zip14_7_1_,
schoollist0_.district_id AS distric20_7_1_,
schoollist0_.enrollment AS enrollm15_7_1_,
schoollist0_.grade_high AS grade_h16_7_1_,
schoollist0_.grade_low AS grade_l17_7_1_,
schoollist0_.NAME AS name18_7_1_,
schoollist0_.number AS number19_7_1_
FROM school schoollist0_
WHERE schoollist0_.district_id = ?
SELECT schoollist0_.district_id AS distric20_7_0_,
schoollist0_.id AS id1_7_0_,
schoollist0_.id AS id1_7_1_,
schoollist0_.closed AS closed2_7_1_,
schoollist0_.city AS city3_7_1_,
schoollist0_.email AS email4_7_1_,
schoollist0_.fax AS fax5_7_1_,
schoollist0_.first_name AS first_na6_7_1_,
schoollist0_.last_name AS last_nam7_7_1_,
schoollist0_.name_prefix AS name_pre8_7_1_,
schoollist0_.phone AS phone9_7_1_,
schoollist0_.state AS state10_7_1_,
schoollist0_.street AS street11_7_1_,
schoollist0_.title AS title12_7_1_,
schoollist0_.website AS website13_7_1_,
schoollist0_.zip AS zip14_7_1_,
schoollist0_.district_id AS distric20_7_1_,
schoollist0_.enrollment AS enrollm15_7_1_,
schoollist0_.grade_high AS grade_h16_7_1_,
schoollist0_.grade_low AS grade_l17_7_1_,
schoollist0_.NAME AS name18_7_1_,
schoollist0_.number AS number19_7_1_
FROM school schoollist0_
WHERE schoollist0_.district_id = ?
SELECT schoollist0_.district_id AS distric20_7_0_,
schoollist0_.id AS id1_7_0_,
schoollist0_.id AS id1_7_1_,
schoollist0_.closed AS closed2_7_1_,
schoollist0_.city AS city3_7_1_,
schoollist0_.email AS email4_7_1_,
schoollist0_.fax AS fax5_7_1_,
schoollist0_.first_name AS first_na6_7_1_,
schoollist0_.last_name AS last_nam7_7_1_,
schoollist0_.name_prefix AS name_pre8_7_1_,
schoollist0_.phone AS phone9_7_1_,
schoollist0_.state AS state10_7_1_,
schoollist0_.street AS street11_7_1_,
schoollist0_.title AS title12_7_1_,
schoollist0_.website AS website13_7_1_,
schoollist0_.zip AS zip14_7_1_,
schoollist0_.district_id AS distric20_7_1_,
schoollist0_.enrollment AS enrollm15_7_1_,
schoollist0_.grade_high AS grade_h16_7_1_,
schoollist0_.grade_low AS grade_l17_7_1_,
schoollist0_.NAME AS name18_7_1_,
schoollist0_.number AS number19_7_1_
FROM school schoollist0_
WHERE schoollist0_.district_id = ?
....
如上所示,SELECT FROM school
重复了数百次,尽管将schoolList配置为延迟加载。
版本:
springBootVersion = '1.4.2.RELEASE'
hibernate-core:5.0.11
'org.springframework.boot:spring-boot-starter-data-jpa'
'org.springframework.boot:spring-boot-starter-data-rest'
'org.springframework.boot:spring-boot-starter-web'
'org.springframework.boot:spring-boot-starter-actuator'
'org.springframework.boot:spring-boot-starter-hateoas'
'org.springframework.boot:spring-boot-starter-security'
答案 0 :(得分:1)
在尝试获取所有可用的学校时,看起来您的代码甚至无法使用@JsonIgnore
字段district
,您应该收到类似的内容:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException:没有 为类找到序列化器 org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer而且没有 发现创建BeanSerializer的属性(以避免异常, 禁用SerializationFeature.FAIL_ON_EMPTY_BEANS)(通过引用 链: 的java.util.ArrayList [0] - &GT; com.mberazouski.stackoverflow.springboothibernate.model.School [&#34;区&#34;] - &GT; com.mberazouski.stackoverflow.springboothibernate.model.District _ _ $$ jvstad5_0 [ &#34;处理&#34;]) 在 com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77) 〜[杰克逊 - 数据绑定-2.9.6.jar:2.9.6]
但是如果你要添加这个注释 - 一切都应该按预期开始工作。所以应该添加的唯一变化应该是:
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "district_id")
@JsonIgnore
private District district;
跟踪结果将是:
2018-06-17 17:27:56.431 DEBUG 25024 --- [nio-8080-exec-1] org.hibernate.SQL:选择school0_.id为 id1_3_,school0_.closed为closed2_3_,school0_.district_id为 district8_3_,school0_.enrollment as enrollme3_3_,school0_.grade_high 作为grade_hi4_3_,school0_.grade_low作为grade_lo5_3_,school0_.name为 name6_3_,school0_.number as number7_3_ from school school0 _
与此相反,如果你要删除懒惰:
@ManyToOne
@JoinColumn(name = "district_id")
private District district;
用于测试的控制器:
import com.mberazouski.stackoverflow.springboothibernate.model.School;
import com.mberazouski.stackoverflow.springboothibernate.repository.SchoolRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class SchoolController {
@Autowired
SchoolRepository schoolRepository;
@GetMapping("/schools")
public List<School> getAllSchools() {
return schoolRepository.findAll();
}
}
希望建议的更改能解决您的问题。
祝你好运。答案 1 :(得分:1)
最后想出来了......为了简单起见,我最初发布这个问题并没有包含所有代码。不幸的是,错误的代码不是我最初发布的。
我为School
对象创建了Projection,并将投影映射到SchoolRepository
,如下面的代码所示。我思想投影仅在REST请求中明确指定时应用(即:/schools?projection=schoolExcerpt
)但显然,投影一直在应用。由于某些原因,District
对象将SchoolProjection
应用于每个相关学校 - &gt;即使存在@JsonIgore
注释,也会导致SQL查询单独从每个学校获取数据。
通过简单地移除投影,我能够检索所有Districts
的列表,而无需进行数千次调用来解析每个关联的学校对象。
@RepositoryRestResource(excerptProjection = SchoolProjection.class) //removing this line solves my problems
public interface SchoolRepository extends CrudRepository<School, Long>{
}
我实际上甚至不需要@JsonIgnore
注释... HATEOAS足够智能,不包含相关对象 - 而是包含指向相关对象的链接。
答案 2 :(得分:0)
在您的情况下,您必须为区域服务调用创建另一个VIEW,或者您想尝试@JSONIGNORE
schoolList
属性以使LazyLoad生效(我猜你的表示层是JSON) 。
当你正在进行区域服务调用时,它正在返回带有lazyload ofcourse的District
对象,但是一旦你的POJO到JSON转换由控制器处理,它就会调用School getter然后School对象的另一种方法,用于创建完整的数据表示,使hibernate也加载学校对象。
<强>更新强>
在github中为您添加了示例代码以供参考。
您可以选择以下三个选项:
@JsonIgnore
- 将完全忽略任何序列化/反序列化的属性。不确定这是不是你真正想要它。@JsonManagedReference & @JsonManagedReference
- 由于hibernate / table交叉引用结构,将避免json递归/ stackoverflow问题。@JsonIgnoreProperties
- 避免参考实体的json递归问题的另一种方法。注意:FetchType.LAZY
是默认设置,因此未明确说明。
快速参考的代码段:
@Getter
@Setter
@Entity
public class District {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true)
private Integer number;
private String name;
private String type;
private int enrollment;
private Date updated;
@Embedded
private ContactInfo contactInfo;
@OneToMany(mappedBy = "district")
//@JsonManagedReference //to avoid JSON recursion solution 1
//@JsonIgnoreProperties("district") //to avoid JSON recursion solution 2
@JsonIgnore
private Set<School> schools;
}
@Getter
@Setter
@Entity
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique=true, nullable = false)
private Long number;
@Column(nullable = false)
private String name;
private boolean closed;
@Embedded
private ContactInfo contactInfo;
private String gradeLow;
private String gradeHigh;
private int enrollment;
@ManyToOne
@JoinColumn(name = "district_id", nullable = false)
//@JsonBackReference //to avoid JSON recursion solution 1
//@JsonIgnoreProperties("schools") //to avoid JSON recursion solution 2
@JsonIgnore
private District district;
}