Spring Boot Sping Boot JPA -一对多关系导致无限循环

yjghlzjz  于 2022-12-18  发布在  Spring
关注(0)|答案(7)|浏览(185)

我有一个两个对象,具有简单的@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在控制台上到处发送垃圾邮件:

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

(It看起来它正试图在事务上下文之外执行惰性加载)。
这是我的CRUD存储库:

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

有人能告诉我如何解决这个问题吗?我更喜欢带有更快获取的解决方案。谢谢你的任何提示
编辑:下面是我正在使用的模式

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`)
);
jum4pzuy

jum4pzuy1#

正如第一个答案所示:
不要在@Entity类上使用Lombok的@Data注解。

原因:@Data生成hashcode()equals()toString()方法,这些方法使用生成的getter。使用getter当然意味着获取新数据,即使属性标记为FetchType=LAZY

在这个过程中,hib尝试用toString()记录数据,结果崩溃了。

xbp102n0

xbp102n02#

问题解决了。我在引用ParentAccount的LinkedAccount中使用了一个自定义的@toString方法。我不知道这会导致任何问题,因此我没有在问题中包括toString。
显然,这会导致惰性加载的无限循环,删除此引用可以解决此问题。

w51jfk4q

w51jfk4q3#

正如user1819111所说,来自Lombok的@Data@EntityFetchType=LAZY不兼容。我曾经使用过Lombok.Data@Data),我得到了这个错误。
因为我不想创建所有的get/set,我只是把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<>();
}
aiazj4mn

aiazj4mn4#

像这样的东西不工作?

@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>();

}
yftpprvb

yftpprvb5#

我最近遇到了这个问题,由于定义不好的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()
);
polkgigr

polkgigr6#

这个简单的方法对我很有效,只要使用JsonIgnoreProperties。

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

iqih9akk7#

这种方法对我来说很有效,无需删除@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中,您还可以忽略多个字段,以防止无限循环

相关问题