В приложении я использую Spring Data JPA и Hibernate.У меня есть два класса Study
и StudiesCategory
.Эти два класса имеют двунаправленную ассоциацию @ManyToOne
/ @OneToMany
.
В рамках транзакции, которую я изменяю, а затем извлекаю сущность Study
.В рамках той же транзакции я получаю доступ к ассоциированному StudiesCategory
и Set
из Study
s.
При вызове aStudy.getName()
я получаю LazyInitializationException
, который, я думаю, не должен быть брошен, потому чтоСессия все еще открыта.Мне кажется, что Hibernate не может каким-либо образом лениво инициализировать коллекцию.
Почему выдается исключение и как мне предотвратить это?
Я попытался установить fetch
для ассоциации на EAGER
, что решает проблему, но я не хочу этого делать и полагаться на графы сущностей.Глядя на SQL-запрос, который создает Hibernate, я не вижу проблем, его выполнение возвращает все, как и ожидалось.
Рассматриваемый метод:
@Transactional
public void addAndPrintStudy(Study study) {
try {
study = studyRepository.saveAndFlush(study);
} catch (DataAccessException e) {
throw new IllegalArgumentException("Study exists.");
}
Study study = studyRepository.findDeepByIdentifier(study.getIdentifier()).get();
Set<Study> studies = study.getStudiesCategory().getStudies();
for (Study aStudy : studies) {
System.out.println(aStudy.getName());
}
}
Вот метод репозитория для извлечения сущности:
@PreAuthorize("permitAll()")
@EntityGraph("Study.studiesCategory.studies")
Optional<Study> findDeepByIdentifier(long identifier);
Вот два класса:
@Entity
@Table(name = "studies")
@NamedEntityGraphs({
@NamedEntityGraph(name = "Study.studiesCategory.studies",
attributeNodes = @NamedAttributeNode(value = "studiesCategory", subgraph = "studiesCategory.studies"),
subgraphs = @NamedSubgraph(name = "studiesCategory.studies", attributeNodes = @NamedAttributeNode("studies"))),
@NamedEntityGraph(name = "Study.studiesCategory",
attributeNodes = @NamedAttributeNode("studiesCategory"))
})
public class Study extends ModelBase implements Serializable, Identifiable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "identifier")
private long identifier;
@Column(name = "name")
@NotBlank
private String name;
@Column(name = "description")
private String description;
@ManyToOne(cascade = {CascadeType.DETACH, CascadeType.REFRESH})
@JoinColumn(name = "studies_category_identifier", referencedColumnName = "identifier")
@Valid
@NotNull
private StudiesCategory studiesCategory;
@Column(name = "visible")
private boolean visible;
public Study() {
}
// getters and setters
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Study study = (Study) o;
return name.equals(study.name);
}
@Override
public long getId() {
return getIdentifier();
}
}
@Entity
@Table(name = "studies_categories")
@NamedEntityGraphs(
@NamedEntityGraph(
name = "StudiesCategory.studies",
attributeNodes = @NamedAttributeNode(value = "studies")
)
)
public class StudiesCategory extends ModelBase implements Serializable, Identifiable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "identifier")
private long identifier;
@Column(name = "name")
@NotBlank
private String name;
@Column(name = "description")
private String description;
@OneToMany(mappedBy = "studiesCategory", cascade = {CascadeType.DETACH, CascadeType.REFRESH})
private Set<Study> studies;
public StudiesCategory(){
}
// getters and setters
@Override
public long getId() {
return getIdentifier();
}
@Override
public int hashCode() {
return Objects.hash(name);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StudiesCategory that = (StudiesCategory) o;
return name.equals(that.name);
}
}