【问题标题】:List of objects not displayed because undefined in Spring boot由于 Spring Boot 中未定义而未显示的对象列表
【发布时间】:2022-02-03 16:52:16
【问题描述】:

我有一个 Question 列表,每个 Question 类型的对象与 Answer 类型的对象具有 1:n 关系(因此每个 Question 对象都有一个 Answer 对象列表)。我正在尝试使用 Angular 材料在浏览器上显示所有 Question 对象及其在单击事件后的答案,但是当我尝试这样做时,Question 对象在没有答案的情况下显示。经过一些研究,我发现即使AnswerQuestion 对象正确“连接”并存储在数据库中,答案列表也未定义,如下面的控制台所示:

{id: 10, question: 'Question1', questionsForUser: Array(0), 答案: 未定义}
答案:homepage.component.ts:32
不明确的 homepage.component.ts:33

我该如何处理这个问题?

这里是QuestionAnswer的关系:

homepage.component 是发生以下点击事件的组件:

<div class="button">
          <button mat-button (click)="getQuestions()" routerLink="questions/getAllQuestions" routerLinkActive="active">Show</button>
</div>

homepage.component.ts:

export class HomepageComponent implements OnInit {
  longText = `...`;
  public questions: Question[] = [];

  constructor(private questionService: QuestionService, private  shared: SharedService) { }

  ngOnInit(): void {
    this.shared.castQuestions.subscribe(questions=>this.questions=questions);
  }

  public getQuestions():void{
    this.questionService.getQuestions().subscribe(
      (response: Question[]) => {
        this.questions =response;

        this.shared.showQuestions(this.questions);
        console.log(response);
        for(let i=0; i<response.length; i++){
          this.questions[i].answers=response[i].answers;
          console.log(response[i]);
          console.log("Answers:");
          console.log(response[i].answers);
        }

      },
      (error: HttpErrorResponse) => {
        alert(error.message);
      }
    );
  }

}

在点击事件之后,由于 Angular 路由,应该执行 tool.component 代码。

tool.component.html:

<table mat-table [dataSource]="questions" class="mat-elevation-z8">
 
  <!-- Question Column -->
  <ng-container matColumnDef="question">
    <th mat-header-cell *matHeaderCellDef> Question </th>
    <td mat-cell *matCellDef="let question"> {{question.question}} </td>
  </ng-container>

  <!-- Answers Column -->
  <ng-container matColumnDef="answers">
    <th mat-header-cell *matHeaderCellDef> Answers </th>
    <td mat-cell *matCellDef="let question"> {{question.answers}} </td>
  </ng-container>



  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

tool.component.ts:

export class ToolComponent implements OnInit {
  public questions: Question[] = [];
  displayedColumns = ['question', 'answers'];
  constructor(private shared: SharedService) { }

  ngOnInit(): void {
    this.shared.castQuestions.subscribe(questions=>this.questions=questions);
  }

}

shared.service.ts:

@Injectable({
  providedIn: 'root'
})
export class SharedService {
    private questions= new BehaviorSubject<Array<Question>>([]);
      castQuestions = this.questions.asObservable();

    showQuestions(data: Question[]){
      this.questions.next(data);

  }
}

console.log(响应):

question.service.ts:

@Injectable({
  providedIn: 'root'
})
public getQuestions(): Observable<Question[]> {
    return this.http.get<Question[]>('http://localhost:8080/questions/getAllQuestions');
  }
}

我用来创建响应的返回/api:

问题实体:

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity
@Table(name = "question", schema = "purchase")
public class Question {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private int id;

    @Basic
    @Column(name = "question", nullable = true, length = 1000)
    private String question;

    @OneToMany(mappedBy = "question", cascade = CascadeType.MERGE)
    private List<QuestionForUser> questionsForUser;

    @OneToMany(mappedBy = "question", cascade = CascadeType.MERGE)
    @JsonIgnore
    private List<Answer> answers;
}

回答实体:

@Getter
@Setter
@EqualsAndHashCode
@ToString
@Entity
@Table(name = "answer", schema = "purchase")
public class Answer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private int id;

    @Basic
    @Column(name = "answer", nullable = true, length = 1000)
    private String answer;

    @ManyToOne
    @JoinColumn(name = "question")

    private Question question;


    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

问题存储库:

@Repository
public interface QuestionRepository extends JpaRepository<Question, Integer> {

    List<Question> findByQuestionContaining(String question);

    boolean existsByQuestion(String question);

}

问题服务:

@Service
public class QuestionService {

    @Autowired
    private QuestionRepository questionRepository;
@Transactional(readOnly = true)
    public List<Question> getAllQuestions(){
        return questionRepository.findAll();
    }
}

问题控制器:

@GetMapping("/getAllQuestions")
    public List<Question> getAll(){
        List<Question> ques = questionService.getAllQuestions();
        for(Question q:ques){
            System.out.println(q.getQuestion());
            if(q.getAnswers()!=null){
                System.out.println("The answers are: "+q.getAnswers().size());
            }
        }

        return questionService.getAllQuestions();
    }

按照建议,我尝试在QuestionController 中向getAll() 添加测试。为了让测试返回一个字符串,我暂时把getAll()的方法改成这样:

@GetMapping("/getAllQuestions")//funziona
    public String getAll(){
        List<Question> result = questionService.getAllQuestions();
        String answer = result.get(0).getAnswers().get(0).getAnswer();
        return answer;
    }

然后,我编写了以下测试:

class QuestionControllerTest {

    @Test
    void getAll() {
        QuestionController controller = new QuestionController(); //Arrange
        String response = controller.getAll(); //Act
        assertEquals("a1", response); //Assert
    }
}

第一个问题的第一个答案应该是a1,但是当我在IntelliJ上执行测试时,结果如下:

java.lang.NullPointerException:无法调用 “com.ptoject.demo.services.QuestionService.getAllQuestions()”因为 “this.questionService”为空

在 com.ptoject.demo.controller.QuestionController.getAll(QuestionController.java:64) 在 com.ptoject.demo.controller.QuestionControllerTest.getAll(QuestionControllerTest.java:12) 在 java.base/java.util.ArrayList.forEach(ArrayList.java:1511)在 java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

【问题讨论】:

  • 请在您的问题中添加此指令的控制台快照/捕获:console.log(response);
  • 当然,我刚刚添加了快照。 @JuanVicenteBerzosaTejero
  • 正如我所担心的那样,您会从您的背部/API 获得未定义的内容。您在 Angular 前端无能为力来解决它。您应该在“背面”方面遇到问题(除非您在“questionService”服务中遇到问题,在“getQuestions”方法中)。我建议您添加到您的质疑该方法“getQuestions() 的代码,以及用于创建“响应”对象的后台/API 的代码。
  • 我将添加 questionService 服务和 back/API 的代码。谢谢@JuanVicenteBerzosaTejero
  • 当然,问题似乎不在“getQuestions”方法中,而是在您的后端。 将您使用的语言的标签添加回您的原始问题

标签: java spring typescript spring-boot angular-material


【解决方案1】:

问题是由Question 实体类中的List&lt;Answer&gt; answers 字段上的@JsonIgnore 注释引起的。这告诉 Jackson 在将对象序列化为 JSON 时忽略(即不包括)该字段。

解决这个问题:

  1. 在您的 Question 实体类中,从 List&lt;Answer&gt; answers 字段中删除 @JsonIgnore 注释

  2. 另一方面,在您的 Answer 实体类中,您应该在Question question 字段中添加@JsonIgnore 注释——这是为了避免由bidrection 引起的潜在Jackson infinite recursion issue关系。

问题实体:

...
public class Question {
    ...

    // remove @JsonIgnore
    @OneToMany(mappedBy = "question", cascade = CascadeType.MERGE)
    private List<Answer> answers;
    ...
}

回答实体:

...
public class Answer {
    ...

    @ManyToOne
    @JoinColumn(name = "question")
    @JsonIgnore // add this here
    private Question question;
    ...
}

【讨论】:

    猜你喜欢
    • 2018-11-11
    • 2018-02-28
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 2021-10-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    相关资源
    最近更新 更多