英文:
StackOverflowError: null in both side relationship
问题
这是我第一次面临这个问题。我有两个具有相互关系的实体,使用了 @OneToMany
和 @ManyToOne
注解。
团队实体:
@Entity
@Data
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String image;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "team")
private List<Player> playerList = new ArrayList<>();
}
球员实体:
@Data
@Entity
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
@ManyToOne
private Team team;
}
我想实现的是,在将球员列表保存到团队后,球员们能够在他们的表中接收到这个团队的记录。在将团队设置给球员后(这样我就知道这个球员在这个团队中),并通过例如以下控制器获取球员列表:
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class PlayerController {
private final PlayerRepository playerRepository;
@GetMapping("/player")
public List<Player> getPlayers() {
return playerRepository.findAll();
}
}
我遇到了 java.lang.StackOverflowError: null
错误:
2020-03-15 21:58:48.419 WARN 8576 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : 在尝试解析异常时失败 [org.springframework.http.converter.HttpMessageNotWritableException]
java.lang.IllegalStateException: 在响应被提交之后无法调用 sendError()
[...]
...
看起来像是陷入了某种循环,但我不知道为什么。对于每一次的帮助,我都会非常感激!
英文:
This is the first time I'm facing this problem. I have two entities with mutual relationships @OneToMany
and @ManyToOne
.
Team entity:
@Entity
@Data
public class Team {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String image;
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "team")
private List<Player> playerList = new ArrayList<>();
}
Player entity:
@Data
@Entity
public class Player {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nickname;
@ManyToOne
private Team team;
}
I want to achieve that after saving the list of players to the team, players receive the record of this team to their table. After setting team to the player (so I know that this player is in the team) and getting list of players e.g. using this controller:
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class PlayerController {
private final PlayerRepository playerRepository;
@GetMapping("/player")
public List<Player> getPlayers() {
return playerRepository.findAll();
}
}
I'm being faced with java.lang.StackOverflowError: null
:
2020-03-15 21:58:48.419 WARN 8576 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
[...]
2020-03-15 21:58:48.423 ERROR 8576 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.gottaaimfast.website.model.Player["team"]->com.gottaaimfast.website.model.Team["playerList"]->org.hibernate.collection.internal.PersistentBag[0]->com.gottaaimfast.website.model.Player["team"]->com.gottaaimfast.website.model.Team["playerList"]->org.hibernate.collection.internal.PersistentBag[0]->com.gottaaimfast.website.model.Player["team"]->com.gottaaimfast.website.model.Team["playerList"]->org.hibernate.collection.internal.PersistentBag[0]->com.gottaaimfast.website.model.Player["team"]->com.gottaaimfast.website.model.Team["playerList"]->org.hibernate.collection.internal.PersistentBag[0]->com.gottaaimfast.website.model.Player["team"]->com.gottaaimfast.website.model.Team["playerList"]->org.hibernate.collection.internal.PersistentBag[0]->[...] with root cause
java.lang.StackOverflowError: null
at java.base/java.lang.ClassLoader.defineClass1(Native Method) ~[na:na]
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016) ~[na:na]
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:800) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:698) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:621) ~[na:na]
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:579) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:737) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serializeContents(CollectionSerializer.java:145) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:107) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.CollectionSerializer.serialize(CollectionSerializer.java:25) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:727) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719) ~[jackson-databind-2.9.8.jar:2.9.8]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155) ~[jackson-databind-2.9.8.jar:2.9.8]
**AND IT LOOPS OVER AND OVER**
It seems like it bumps into some kind of loop but I have no idea why. I would be really thankful for every help!
答案1
得分: 2
循环是因为您依赖默认的 JSON 序列化。您拥有双向的多对一关系,因此:
- 对于每个玩家,您想要输出他所在的团队
- 对于团队,您输出团队中的玩家
因此,在您的序列化过程中出现了循环。
您的选项是:
- 在多对一关系的一侧使用
@JsonIgnore
来打破循环 - 使用自定义的无循环 DTO 来传输数据
- 使用
@JsonManagedReference
和@JsonBackReference
。
英文:
The loop is due to the fact that you are relying on default serialization to json. You have a bi-directional ManyToOne relationship, so:
- For each player, you want to output his team
- For the team, you output the players in the team
and thus yoy have a cycle in your serialization process.
Your options are:
- use
@JsonIgnore
on one side of the ManyToOne relationship to break the cycle - use a custom DTO With no cycles to transfer the data
- Use
@JsonManagedReference
and@JsonBackReference
答案2
得分: 2
在Player类中:
@ManyToOne
@JsonManagedReference
private Team team;
在Team类中:
@JsonBackReference
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "team")
private List<Player> playerList = new ArrayList<>();
参考这个答案:
英文:
this bidirectional json mapping problem add these annotations to your entities corresponding fields
In Player:
@ManyToOne
@JsonManagedReference
private Team team;
In Team:
@JsonBackReference
@OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy ="team")
private List<Player> playerList = new ArrayList<>();
See this answer:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论