Spring/SpringBoot

Spring Boot으로 웹 출시까지 #10. JPA를 이용하여 @OneToMany 관계 설정하기

느리지만 꾸준하게 2022. 6. 4. 00:16

Board Class에 User 정보를 넣어주자.

 

게시글 입장에서 사용자는 다대일 관계

 

사용자 입장에서는 게시글과 일대다 관계

 

참고 https://zetawiki.com/wiki/MySQL_%ED%85%8C%EC%9D%B4%EB%B8%94_%EC%BB%AC%EB%9F%BC_%EC%B6%94%EA%B0%80

 

MySQL 테이블 컬럼 추가 - 제타위키

다음 문자열 포함...

zetawiki.com

board 테이블에 user_id 컬럼을 만들고 user_id 컬럼을 user의 id 컬럼을 참조하여 외래키로 만들어준다.

MySQL [mydb]> ALTER TABLE board ADD user_id BIGINT(20);
Query OK, 0 rows affected, 1 warning (0.028 sec)
Records: 0  Duplicates: 0  Warnings: 1

MySQL [mydb]> SELECT * FROM BOARD;
+----+--------+---------+---------+
| id | title  | content | user_id |
+----+--------+---------+---------+
|  1 | 제목   | 내용    |    NULL |
|  2 | 헬로   | 월드    |    NULL |
+----+--------+---------+---------+
2 rows in set (0.001 sec)

MySQL [mydb]> alter table board add FOREIGN KEY(user_id) REFERENCES user(id);
Query OK, 2 rows affected (0.033 sec)
Records: 2  Duplicates: 0  Warnings: 0

 

 

 

BoardController에 form에 postmapping을 아래와 같이 설정 해준다.

@PostMapping("/form")
    public String postForm(@Valid Board board, BindingResult bindingResult) {
        boardValidator.validate(board, bindingResult);
        if (bindingResult.hasErrors()) {
            return "board/form";
        }
        board.setUser(user);
        boardRepository.save(board);
        return "redirect:/board/result";
    }

 

 

 

인증정보도 같이 받아준다.(spring boot get login user도 참고해준다.)

@PostMapping("/form")
    public String postForm(@Valid Board board, BindingResult bindingResult, Authentication authentication) {
        boardValidator.validate(board, bindingResult);
        if (bindingResult.hasErrors()) {
            return "board/form";
        }
        String username = authentication.getName();
        boardRepository.save(board);
        return "redirect:/board/result";
    }

 

 

BoardService Class 만들어주고

package com.example.myhome.service;


import com.example.myhome.model.Board;
import com.example.myhome.repository.BoardRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BoardService {

    @Autowired
    private BoardRepository boardRepository;

    public Board save(String username, Board board) {

    }
}

 

BoardController을 아래와 같이 설정해준다.

package com.example.myhome.controller;


import com.example.myhome.model.Board;
import com.example.myhome.repository.BoardRepository;
import com.example.myhome.service.BoardService;
import com.example.myhome.validator.BoardValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@Controller
@RequestMapping("/board")
public class BoardController {

    // DI => 서버 기동될 때 인스턴스가 들어온다.
    @Autowired
    private BoardRepository boardRepository;

    @Autowired
    private BoardService boardService;

    @Autowired
    private BoardValidator boardValidator;

    @GetMapping("/list")
    public String list(Model model, @PageableDefault(size = 2) Pageable pageable,
                       @RequestParam(required = false, defaultValue = "") String searchText) {

        Page<Board> boards = boardRepository.findByTitleContainingOrContentContaining(searchText, searchText, pageable);
        int startPage = Math.max(1, boards.getPageable().getPageNumber() - 4);
        int endPage = Math.min(boards.getTotalPages(), boards.getPageable().getPageNumber() + 4);
        model.addAttribute("startPage", startPage);
        model.addAttribute("endPage", endPage);
        model.addAttribute("boards", boards);
        return "board/list";
    }

    @GetMapping("/form")
    public String form(Model model, @RequestParam(required = false) Long id) {
        if(id == null) {
            model.addAttribute("board", new Board());
        } else {
            Board board = boardRepository.findById(id).orElse(null);
            model.addAttribute("board", board);
        }

        return "board/form";
    }

    @PostMapping("/form")
    public String postForm(@Valid Board board, BindingResult bindingResult, Authentication authentication) {
        boardValidator.validate(board, bindingResult);
        if (bindingResult.hasErrors()) {
            return "board/form";
        }
        String username = authentication.getName();
        boardService.save(username, board);
//        boardRepository.save(board);
        return "redirect:/board/result";
    }
}

 

BoardService 아래와 같이 생성

package com.example.myhome.service;


import com.example.myhome.model.Board;
import com.example.myhome.model.User;
import com.example.myhome.repository.BoardRepository;
import com.example.myhome.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BoardService {

    @Autowired
    private BoardRepository boardRepository;

    @Autowired
    private UserRepository userRepository;

    public Board save(String username, Board board) {
        User user = userRepository.findByUsername(username);
        board.setUser(user);
        return boardRepository.save(board);
    }
}

 

 

 UserRepository에 findByUsername 생성

package com.example.myhome.repository;

import com.example.myhome.model.Board;
import com.example.myhome.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

 

 

UserApiController도 아래와 같이 생성해준다.

package com.example.myhome.controller;

import com.example.myhome.model.User;
import com.example.myhome.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api")
class UserApiController {

    @Autowired
    private UserRepository repository;

    @GetMapping("/users")
    List<User> all() {
        return repository.findAll();
    }

    @PostMapping("/users")
    User newUser(@RequestBody User newUser) {
        return repository.save(newUser);
    }

    // Single item

    @GetMapping("/users/{id}")
    User one(@PathVariable Long id) {
        return repository.findById(id).orElse(null);
    }

    @PutMapping("/users/{id}")
    User replaceUser(@RequestBody User newUser, @PathVariable Long id) {

        return repository.findById(id)
                .map(user -> {
//                    user.setTitle(newuser.getTitle());
//                    user.setContent(newuser.getContent());
                    
                    return repository.save(user);
                })
                .orElseGet(() -> {
                    newUser.setId(id);
                    return repository.save(newUser);
                });
    }

    @DeleteMapping("/users/{id}")
    void deleteUser(@PathVariable Long id) {
        repository.deleteById(id);
    }
}

 

그리고 User Class에는 OneToMany 어노테이션을 준다. (board user 양방향 매핑을 시켜준다.)

 

User Class

@OneToMany(mappedBy = "user")
private List<Board> boards = new ArrayList<>();

 

Board Class

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

 

 

지금 계속해서 에러가 떠서 postman으로  localhost:8080/api/users로 확인을 할 수 없다. 해결하고 자야한다.

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-06-03 23:26:08.179 ERROR 75658 --- [  restartedMain] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'webSecurityConfig': Requested bean is currently in creation: Is there an unresolvable circular reference?

 

 

아래와 같이 postman으로 나중에 테스트를 해주자.

 

postman 테스트를 원활하게 하기 위해서 WebSecurityConfig를 아래와 같이 설정 해준다.

// WebSecurityConfig Class

@PutMapping("/users/{id}")
    User replaceUser(@RequestBody User newUser, @PathVariable Long id) {

        return repository.findById(id)
                .map(user -> {
//                    user.setTitle(newuser.getTitle());
//                    user.setContent(newuser.getContent());
                    user.setBoards(newUser.getBoards());
                    return repository.save(user);
                })
                .orElseGet(() -> {
                    newUser.setId(id);
                    return repository.save(newUser);
                });
    }

 

User Class에서 cascade를 넣어준다.

@JsonIgnore
private List<Role> roles = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE)
private List<Board> boards = new ArrayList<>();

 

UserApiController Class를 아래와 같이 나타내준다.

@PutMapping("/users/{id}")
    User replaceUser(@RequestBody User newUser, @PathVariable Long id) {

        return repository.findById(id)
                .map(user -> {
//                    user.setTitle(newuser.getTitle());
//                    user.setContent(newuser.getContent());
                    user.setBoards(newUser.getBoards());
                    for(Board board : user.getBoards()) {
                        board.setUser(user);
                    }
                    return repository.save(user);
                })
                .orElseGet(() -> {
                    newUser.setId(id);
                    return repository.save(newUser);
                });
    }

 

 

그리고 로그를 확인할 수 있는 방법이 있다. 여기를 참고하자.

application.properties를 Via Loggers를 참고하여 코드를 넣어준다.

spring.datasource.url=jdbc:mariadb://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<참고 :Spring Boot으로 웹 출시까지 #10. JPA를 이용하여 @OneToMany 관계 설정하기>

https://www.youtube.com/watch?v=wM7P-6_CHFM&list=PLPtc9qD1979DG675XufGs0-gBeb2mrona&index=10