![](/img/trans.png)
[英]Post API using Spring Boot returns null values for nested json
[英]Spring Boot API response returns repeating nested JSON
我有一个User
model 和一个TodoItem
model,其中TodoItem
model 具有User
model 的主键和user_id
@joincolumn。 我的问题是我在添加项目后从getUsers
API 得到的响应。 它创建了这个超长嵌套的 JSON,它一遍又一遍地重复自己。 我觉得我没有正确处理主键大小写。
TodoController.java
@RestController
@RequestMapping("/api")
public class TodoController {
@Autowired
private TodoRepository todoRepository;
@PostMapping("/addItem")
public TodoItem addTodoItem(@RequestBody TodoItem todoItem) {
return todoRepository.save(todoItem);
}
用户.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
@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
添加项目请求
{
"todo": "blahblah",
"completed": false,
"user": {
"id": 6
}
}
添加项目响应
{
"id": 26,
"todo": "blahblah",
"completed": false,
"user": {
"id": 6,
"name": null,
"password": null,
"todos": null
}
}
所以我已经不喜欢给出响应的方式了,为什么 name、pass 和 todos null 当用户 ID 为 6 存在时,我也只是传递给它一个 todoitem,所以为什么 todo null。数据库正确填充,只是回应似乎是错误的。 然后我认为这与我在这里遇到的主要问题有关; 这是在我向用户添加项目之后:
获取用户响应
[
{
"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",
它就这样持续了数千行。 即使响应很疯狂,数据库也会正确更新,但由于这个问题,API 调用可能需要一段时间
在您的 TodoItem.java 中,删除 User 属性的 getter。
确保您的 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.
您的实体之间有bidirectional
关联,因此当映射器进入user
时,它会映射所有todos
,并且因为这些todos
都与user
有关系,所以它会获取user
......再次......再次......溢出死亡循环。
“最好”的方式和常见的方式是您应该制作您构建的 DTO class。
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());
请注意,如果您执行log.info(user)
它将执行相同的操作。 避免这种情况的方法(还有其他方法)是将@JsonIgnore
添加到关系的一侧(例如在Todos 上的@ManyToOne-Users 上)或覆盖Todo 的toString()。
对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 +
'}';
}
答案是你已经映射了彼此的实体 class,所以当你获得 Todoitem 实体并且用户被拾取并且再次 Todoitem 并且它继续时。你不必为每个实体都映射 map。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.