Spring Bootでアプリを作成してみた【Quiz app】

## 1.プロジェクトを作成する

公式：[spring initializr](https://start.spring.io/)

① ブラウザを開き、(https://start.spring.io/) を入力し、開く。
② 下記を入力し、Dependenciesで下記を選択し、『GENERATE』ボタンを押す。

- Project: Maven
- Language: Java
- SpringBoot: 3.1.1
- Project: Maven
- Group: com.telusko
- Artifact: quizapp
- Name : quizapp
- Description: Telusko Quiz App
- Package name: com.telusko.quizapp

Dependencies
- Spring Web
- PostgreSQL Driver
- Spring Data JPA
- Lombok

## 2.アプリを作成する

① application.propertiesを編集する。

```application.properties
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432/questiondb
spring.datasource.username=postgres
spring.datasource.password=0000
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
```

② src/java/com.telusko/quizappにControllerファイルとQuestionController.javaを作成する。

```Controller/QuestionController.java
package com.telusko.quizapp.Controller;

import com.telusko.quizapp.Service.QuestionService;
import com.telusko.quizapp.model.Question;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("question")
public class QuestionController {

    @Autowired
    QuestionService questionService;

    @GetMapping("allQuestions")
    public ResponseEntity<List<Question>> getAllQuestions() {
        return questionService.getAllQuestions();
    }

    @GetMapping("category/{category}")
    public ResponseEntity<List<Question>> getQuestionByCategory(@PathVariable String category) {
        return questionService.getQuestionsByCategory(category);
    }

    @GetMapping("category/{category}")
    public String addQuestion(@RequestBody Question question) {
        return questionService.addQuestion(question);
    }
}
```

③ src/java/com.telusko/quizappにServiceファイルとQuestionService.javaを作成する。

```Service/QuestionService.java
package com.telusko.quizapp.Service;

import com.telusko.quizapp.dao.QuestionDao;
import com.telusko.quizapp.model.Question;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@Service
public class QuestionService {
    @Autowired
    QuestionDao questionDao;

    public ResponseEntity<List<Question>> getAllQuestions() {
        try {
            return new ResponseEntity<>(questionDao.findAll(), HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ResponseEntity<>(new ArrayList<>(), HttpStatus.BAD_REQUEST);
    }

    public ResponseEntity<List<Question>> getQuestionsByCategory(String category) {
        try {
            return new ResponseEntity<>(questionDao.findAll(), HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ResponseEntity<>(new ArrayList<>(), HttpStatus.BAD_REQUEST);
    }

    public ResponseEntity<String> addQuestion(Question question) {
        questionDao.save(question);
        return new ResponseEntity<>("success", HttpStatus.CREATED);
    }
}
```

④ src/java/com.telusko/quizappにmodelファイルとにQuestion.javaを作成する。

```model/Question.java
package com.telusko.quizapp.model;

import jakarta.persistence.Id;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import lombok.Data;

@Data
@Entity
public class Question {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;
    private String questionTitle;
    private String option1;
    private String option2;
    private String option3;
    private String option4;
    private String rightAnswer;
    private String difficultylevel;
    private String category;
}
```
    
⑤ src/java/com.telusko/quizappにdaoファイルとQuestionDao.javaを作成する。

```dao/QuestionDao.java
package com.telusko.quizapp.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.telusko.quizapp.model.Question;
import java.util.List;

@Repository
public interface QuestionDao extends JpaRepository<Question, Integer> {
    List<Question> findByCategory(String category);

    @Query(value = "SELECT * FROM question a Where q.category=:category ORDER BY RANDOM() LIMIT :numQ", nativeQuery = true)
    List<Question> findRandomQuestionsByCategory(String category);
}
```

⑥ src/java/com.telusko/quizappにControllerファイルとQuizController.javaを作成する。

```Controller/QuizController.java
package com.telusko.quizapp.Controller;

import com.telusko.quizapp.model.Question;
import com.telusko.quizapp.model.QuestionWrapper;
import com.telusko.quizapp.service.QuizService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping
public class QuizController {

    ＠Autowired QuizService quizService;

    @PostMapping("create")
    public ResponseEntity<String> createQuiz(@RequestParam String category, @RequestParam int numQ,
            @RequestParam String title) {
        return quizService.createQuiz(category, numQ, title);
    }

    @GetMapping("get/{id}")
    public ResponseEntity<List<QuestionWrapper>> getQuizQuestions(@PathVariable Integer id) {
        return quizService.getQuizQuestions(id);
    }

    @GetMapping("get/{id}")
    public ResponseEntity<Integer> submitQuiz(@PathVariable Integer id, @RequestBody List<Response> responses){
        return quizService.calculateResult(id, responses);
    }
}
```

⑦ src/java/com.telusko/quizappにService/QuizService.javaを作成する。

```Service/QuizService.java
package com.telusko.quizapp.Service;

import com.telusko.quizapp.dao.QuestionDao;
import com.telusko.quizapp.dao.QuizDao;
import com.telusko.quizapp.model.Question;
import com.telusko.quizapp.model.QuestionWrapper;
import com.telusko.quizapp.model.Quiz;
import com.telusko.quizapp.model.Respose;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

@Service
public class QuizService {

    ＠
    Autowired QuizDao quizDao;
    @Autowired
    QuestionDao questionDao;

    public ResponseEntity<String> createQuiz(String category, int numQ, String title) {
        List<Question> questions = questionDao.findRandomQuestionsByCategory(category, numQ);
        Quiz quiz = new Quiz();
        quiz.setTitle(title);
        quiz.setQuestions();
        quizDao.save(quiz);

        return new ResponseEntity<>("success", HttpStatus.CREATED);
    }

    public ResponseEntity<List<QuestionWrapper>> getQuizQuestions(Integer id) {
        Optional<Quiz> quiz = quizDao.findById(id);
        List<Question> questionsFromDB = quiz.get().getQuestions();
        List<QuestionWrapper> questionsFromUser = new ArrayList<>();
        for (Question q : questionsFromDB) {
            QuestionWrapper qw = new QuestionWrapper(q.getId(), q.getQuestionTitle(), q.getOption1(), q.getOption2(),
                    q.getOption3(), q.getOption4());
            questionsFromUser.add(qw);
        }

        return new ResponseEntity<>(questionsForUser, HttpStatus.OK);
    }

    public ResponseEntity<Integer> calculateResult(Integer id, List<Respose> responses){
        Quiz quiz = quizDao.findById(id).get();
        List<Question> questions = quiz.getQuestions();
        int right = 0;
        int i = 0;
        for(Respose response:responses){
            if(response.getResponse().equals(questions.get(i).getRightAnswer()))
            right++;
            i++;
        }
        return new ResponseEntity<Integer>(right, HttpStatus.OK);
    }
}
```

⑧ src/java/com.telusko/quizappにdao/QuizDao.javaを作成する。

```dao/QuizDao.java
package com.telusko.quizapp.dao;

import com.telusko.quizapp.model.Quiz;
import org.springframework.data.jpa.repository.JpaRepository;

public interface QuizDao extends JpaRepository<Quiz, Integer> {

}
```

⑨ model/Quiz.javaを作成する。

```model/Quiz.java
package com.telusko.quizapp.model;

import jakarta.persistence.*;
import lombok.Data;
import java.util.List;

@Entity
@Data
public class Quiz {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String title;

    @ManyToMany
    private List<Question> questions;
}
```
⑩ model/QuestionWrapper.javaを作成する。

```model/QuestionWrapper.java
package com.telusko.quizapp.model;

import lombok.Data;

@Data
public class QuestionWrapper {

    private Integer id;
    private String questionTitle;
    private String option1;
    private String option2;
    private String option3;
    private String option4;

    public QuestionWrapper(Integer id, String questionTitle, String option1, String option2, String option3,
            String option4) {

        this.id = id;
        this.questionTitle = questionTitle;
        this.option1 = option1;
        this.option2 = option2;
        this.option3 = option3;
        this.option4 = option4;
    }
}
```

⑪ model/Respose.javaを作成する。

```model/Respose.java
package com.telusko.quizapp.model;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@Data
@RequiredArgsConstructor
public class Response {
    private Integer id;
    private String response;
}
```

## 3.Databaseにテーブルを作成する

① pgAdmin 4から、前の手順で作成したデータベースを選択、「スキーマ」⇒「public」を選択して、「テーブル」を右クリック ⇨「作成」⇒「テーブル」を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/32ddbf59-e103-f68c-a5f4-15fcb9dbad2d.png)

② 「テーブル作成」ダイアログ画面が表示 ⇨「一般」タブを選択して「名称」（テーブル名）を入力し、「所有者」に前の手順で作成したユーザを設定。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/7f528b56-3ec6-c3ba-79bf-fe424aca218d.png)


## 4. コンポーネント・ファイル構成

```
src
  ├── main
     　├── java/com/telusko/quizpp
     　├── Controller
          ├── QuestionController.java
        　　　　└── QuizController.java
    　　　├── dao
          ├── QuestionDao.java
          └── QuizDoa.java
    　 ├── model
          ├── Question.java
          ├── QuestionWrapper.java
          ├── Quiz.java
          └── Response.java
       └── Service
           ├── QuestionService.java
           └── QuizService.java
     └── QuizappApplication.java
   └── resources
    　　　　 └── application.properties
 └── java/com/telusko/quizpp
     └── QuizappApplicationTests.java
```


## 参考サイト
[Quiz App Using Spring Boot #1 | Microservices Tutorial](https://www.youtube.com/watch?v=zFGh_Jh-Hvs&t=2s)
[Quiz App Using Spring Boot #２ | Microservices Tutorial](https://www.youtube.com/watch?v=JOWfwjEasaw)
[Quiz App Using Spring Boot #3 | Microservices Tutorial](https://www.youtube.com/watch?v=aoAdMc7-XBQ)
[Quiz App Using Spring Boot #4 | Microservices Tutorial](https://www.youtube.com/watch?v=yrfY3zCJ_tA)
[Quiz App Using Spring Boot #5 | Microservices Tutorial](https://www.youtube.com/watch?v=u9CIKvkfE1M)
[Quiz App Using Spring Boot #6 | Microservices Tutorial](https://www.youtube.com/watch?v=6tzuXRFliqo)
[Quiz App Using Spring Boot #7 | Microservices Tutorial](https://www.youtube.com/watch?v=OTqNjZGaJgc)
[Quiz App Using Spring Boot #8 | Microservices Tutorial](https://www.youtube.com/watch?v=mcxXxrTAIiw)