[英]Spring Boot API response returns repeating nested JSON
I have a User
model and a TodoItem
model where the TodoItem
model has a primary key to the User
model with a user_id
@joincolumn.我有一个
User
model 和一个TodoItem
model,其中TodoItem
model 具有User
model 的主键和user_id
@joincolumn。 My issue is the response I get from the getUsers
API after I add an item.我的问题是我在添加项目后从
getUsers
API 得到的响应。 It creates this super long nested JSON where it repeats itself over and over again.它创建了这个超长嵌套的 JSON,它一遍又一遍地重复自己。 I feel like I'm not handling the primary key case properly.
我觉得我没有正确处理主键大小写。
TodoController.java TodoController.java
@RestController
@RequestMapping("/api")
public class TodoController {
@Autowired
private TodoRepository todoRepository;
@PostMapping("/addItem")
public TodoItem addTodoItem(@RequestBody TodoItem todoItem) {
return todoRepository.save(todoItem);
}
User.java用户.java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "name")
private String name;
@Column(name = "password")
private String password;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<TodoItem> todos;
public User() {
}
public User(String name, String password, List<TodoItem> todos) {
this.name = name;
this.password = password;
this.todos = todos;
}
// setter and getters
TodoItem.java TodoItem.java
@Entity
@Table(name = "todo_item")
public class TodoItem {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "todo")
private String todo;
@Column(name = "completed")
private boolean completed;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
public TodoItem() {
}
public TodoItem(String todo, boolean completed) {
this.todo = todo;
this.completed = completed;
}
// setters and getters
Add Item Request添加项目请求
{
"todo": "blahblah",
"completed": false,
"user": {
"id": 6
}
}
Add Item Response添加项目响应
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": null,
"password": null,
"todos": null
}
} }
So already I don't like the way the response is given, why is name, pass, and todos null when the user with id 6 exists, also I just passed it a todoitem, so why is todo null. The database populates properly, it's just that the response seems wrong.所以我已经不喜欢给出响应的方式了,为什么 name、pass 和 todos null 当用户 ID 为 6 存在时,我也只是传递给它一个 todoitem,所以为什么 todo null。数据库正确填充,只是回应似乎是错误的。 And then I think it ties into the main problem I have which is here;
然后我认为这与我在这里遇到的主要问题有关; this is after I add item to a user:
这是在我向用户添加项目之后:
Get Users Response获取用户响应
[
{
"id": 6,
"name": "joe",
"password": "pass",
"todos": [
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": "joe",
"password": "pass",
"todos": [
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": "joe",
"password": "pass",
"todos": [
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": "joe",
"password": "pass",
"todos": [
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": "joe",
"password": "pass",
"todos": [
{
"id": 26,
"todo": "blahblah",
And it just continues like that for literally thousands of lines.它就这样持续了数千行。 Even though the response is crazy, the database updates properly, but the API calls can take a while due to this issue
即使响应很疯狂,数据库也会正确更新,但由于这个问题,API 调用可能需要一段时间
In your TodoItem.java, remove the getter for the User property.在您的 TodoItem.java 中,删除 User 属性的 getter。
Make sure that you only have the setter for user property in your TodoItem.java.确保您的 TodoItem.java 中只有用户属性的设置器。
Essentially, when Spring forms the response, it is doing a ".toString()" like method to map the entity in to a JSON object to pass to the front end. Essentially, when Spring forms the response, it is doing a ".toString()" like method to map the entity in to a JSON object to pass to the front end.
You have a bidirectional
association between your entities, so when the mapper goes in to the user
it maps all the todos
and because those todos
all have a relationship with the user
it then gets the user
...and again...and again and loop of overflow death.您的实体之间有
bidirectional
关联,因此当映射器进入user
时,它会映射所有todos
,并且因为这些todos
都与user
有关系,所以它会获取user
......再次......再次......溢出死亡循环。
The "best" way and is common is you should make a DTO class which you construct. “最好”的方式和常见的方式是您应该制作您构建的 DTO class。
UserTodoDTO
: UserTodoDTO
:
//Lombok Getter/Setter/ToString and All Args Constructor.
@ToString
@Getter
@Setter
@AllArgsConstructor
public class UserTodoDTO {
private long id;
@JsonProperty("name")
private String username;
private List<TodoItem> todoItems;
}
//Pretend this is full of your 'users'.
List<User> usersFromDatabaseAsEntity = new ArrayList<>();
//Return these and the serialisation will not occur.
final List<UserTodoDTO> viewsOfUser = usersFromDatabaseAsEntity
.stream()
.map(entity -> new UserTodoDTO(entity.getId(), entity.getName(), entity.getTodos()))
.collect(Collectors.toList());
Just be aware, if you do log.info(user)
it will do the same thing.请注意,如果您执行
log.info(user)
它将执行相同的操作。 The way to avoid this (there are others) is to add a @JsonIgnore
to one side of the relationship (like on the @ManyToOne-Users on the Todos) or override the toString() for the Todo.避免这种情况的方法(还有其他方法)是将
@JsonIgnore
添加到关系的一侧(例如在Todos 上的@ManyToOne-Users 上)或覆盖Todo 的toString()。
Changes to TodoItem
对
TodoItem
的更改
//Will not be mapped to JSON so stops the loop of death.
@JsonIgnore
//Lombok can make the toString() for you but need to
// tell it to ignore this field to stop loop of death.
@ToString.Exclude
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
//If not using lombok and you are doing TodoItem.toString() somewhere...
//Remove the user from toString().
@Override
public String toString() {
return "TodoItem{" +
"id=" + id +
", todo='" + todo + '\'' +
", completed=" + completed +
'}';
}
The answer is you have mapped each other entity class,so when you get Todoitem entity and User gets pickedup and again the Todoitem and it goes on.You don't have to map each and every entities.答案是你已经映射了彼此的实体 class,所以当你获得 Todoitem 实体并且用户被拾取并且再次 Todoitem 并且它继续时。你不必为每个实体都映射 map。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.