簡體   English   中英

Spring Boot JPA - OneToMany 關系導致無限循環

[英]Spring Boot JPA - OneToMany relationship causes infinite loop

我有兩個具有簡單@OneToMany 關系的對象,如下所示:

父母:

@Entity
public class ParentAccount {

  @Id
  @GeneratedValue
  private long id;
  private String name;

  @OneToMany(fetch = FetchType.EAGER, mappedBy = "parentAccount")
  private Set<LinkedAccount> linkedAccounts;

}

孩子:

@Entity
public class LinkedAccount {

  @Id
  @GeneratedValue
  private long id;

  @ManyToOne(optional = false)
  private ParentAccount parentAccount;

  private String name;

  // empty constructor for JPA
  public LinkedAccount() {
  }

}

我可能會使用 Spring CrudRepository來操作這些實體。 但是,當調用ParentAccount parent = parentAccountRepository.findOne(id); ,某種無限循環開始發生並且休眠在整個控制台上發送垃圾郵件:

Hibernate: select linkedacco0_.parent_account_id as parent_a6_1_0_, linkedacco0_.id as id1_0_0_, linkedacco0_.id as id1_0_1_, linkedacco0_.aws_id as aws_id2_0_1_, linkedacco0_.key_id as key_id3_0_1_, linkedacco0_.name as name4_0_1_, linkedacco0_.parent_account_id as parent_a6_0_1_, linkedacco0_.secret_key as secret_k5_0_1_ from linked_account linkedacco0_ where linkedacco0_.parent_account_id=?

我嘗試將獲取類型更改為 LAZY 但隨后出現此錯誤:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.berrycloud.scheduler.model.ParentAccount.linkedAccounts, could not initialize proxy - no Session

(似乎它正試圖在事務上下文之外進行延遲加載)。

這是我的 CRUD 存儲庫:

@Repository
public interface ParentAccountRepository extends CrudRepository<ParentAccount, Long> {
}

有人可以告訴我如何解決這個問題嗎? 我更喜歡使用 EAGER fetch 的解決方案。 謝謝你的任何提示

編輯:這是我正在使用的架構

CREATE TABLE parent_account (
    id BIGINT auto_increment,
    name VARCHAR(80) null,
    PRIMARY KEY (`id`)
);

CREATE TABLE linked_account (
    id BIGINT auto_increment,
    parent_account_id BIGINT,
    name VARCHAR(80) null,
    FOREIGN KEY (`parent_account_id`) REFERENCES `parent_account` (`id`),
    PRIMARY KEY (`id`)
);

正如第一個答案所暗示的:

不要在@Entity類上使用 Lombok 的@Data注釋。

原因: @Data生成hashcode()equals()toString()方法,這些方法使用生成的 getter。 即使屬性標記為FetchType=LAZY ,使用 getter 也意味着獲取新數據。

在某個地方,hibernate 嘗試使用toString()記錄數據並且它崩潰了。

問題解決了。 我在引用 ParentAccount 的 LinkedAccount 中使用了自定義@toString方法。 我不知道這可能會導致任何問題,因此我沒有在我的問題中包含 toString 。

顯然,這導致了延遲加載的無限循環,刪除這個引用解決了這個問題。

像這樣的東西不起作用?

@Entity
public class Account {

    @Id
    @GeneratedValue
    private long id;
    private String name;

    @ManyToOne(cascade={CascadeType.ALL})
    @JoinColumn(name="manager_id")
    private Account manager;

    @OneToMany((fetch = FetchType.EAGER, mappedBy="manager")
    private Set<Account> linkedAccounts = new HashSet<Account>();

}

由於 Jackson2HttpMessageConverter 定義不明確,我最近遇到了這個問題。

我做了類似以下的事情。

@Bean
RestTemplate restTemplate(@Qualifier("halJacksonHttpMessageConverter")
                                  TypeConstrainedMappingJackson2HttpMessageConverter halConverter) {
    final RestTemplate template = new RestTemplateBuilder().build();
    halConverter.setSupportedMediaTypes(List.of(/* some media types */)); 
    final List<HttpMessageConverter<?>> converters = template.getMessageConverters();
    converters.add(halConverter);
    template.setMessageConverters(converters);
    return template;
}

這導致了一個問題,因為媒體類型不包括所有默認值。 將其更改為以下內容為我解決了這個問題。

halConverter.setSupportedMediaTypes(
    new ImmutableList.Builder<MediaType>()
        .addAll(halConverter.getSupportedMediaTypes())
        .add(/* my custom media type */)
        .build()
);

正如user1819111所說,來自 Lombok 的@Data@EntityFetchType=LAZY不兼容。 我使用過Lombok.Data ( @Data ),我收到了這個錯誤。

因為我不想創建所有獲取/設置,所以我只是將Lombok @Setter@Getter放在你的班級中,一切都會正常工作。

@Setter
@Getter
@Entity
@Table(name = "file")
@SequenceGenerator(name = "File_Sequence", allocationSize=1, sequenceName = "file_id_seq")
public class MyClass{
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "File_Sequence")
    @Column(name = "id")
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "file", cascade = CascadeType.DETACH, fetch = FetchType.LAZY)
    private Set<Base2FileDetail> details = new HashSet<>();
}

這種簡單的方法對我有用。 只需使用 JsonIgnoreProperties 。

    @JsonIgnoreProperties(value = {"linkedAccounts"})
    @ManyToOne(cascade = { CascadeType.PERSIST})
    @JoinColumn(name = "abc", referencedColumnName = "abc")
    private ParentAccount parentAccount;
    

這種方式對我有用,無需刪除@ToSring注釋:

@Entity
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@AllArgsConstructor
@Table(name = "parent_accounts")

public class ParentAccount {
    @JsonIgnoreProperties({"parentAccount"})
    @OneToMany(mappedBy = "parentAccount",
            cascade = CascadeType.ALL,
            orphanRemoval = true)
    private List<LinkedAccount> linkedAcounts;
    // ...
}
@Entity
@Getter
@Setter
@ToString
@RequiredArgsConstructor
@AllArgsConstructor
@Table(name = "linked_accounts")

public class LinkedAccount {
    @JsonIgnoreProperties("linkedAcounts")
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "parentAccount_id")
    private ParentAccount parentAccount;
    // ...
}

PS:在@JsonIgnoreProperties中也可以忽略多個字段,防止死循環

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM