티스토리 뷰

728x90

fooball.zip
2.36MB

1. 이 포스트는 축구 클럽 관리를 위한 테스트 프로젝트로 다양한 스프링 기능을 데모하기 위해 만들었다.

  1-1 세션 범위의 공유객체 사용방법과 완료처리 방법

  1-2 세션 객체가 존재하지 않거나 만료한 경우 처리방법(https://kogle.tistory.com/202)

  1-3 SimpleJdbcInserter를 사용하는 방법

  1-4 JdbcTemplate을 이용한 마지막 포스트이니 위에 프로젝트를 첨부하였다.

 

2. 팀작성 페이지는 input 밖에 없어 검증 작성하기가 수월하다. 아래는 검증과 저장을 수행하는 메소드를 추가하였다.

  2-1 사용자가 입력한 데이터는 @modelAttribute로 받는데 @Valid를 설정하여 검증을 수행한다.

  2-2 검증의 결과는 BindingResult로 받아 에러가 있는지를 확인한다.

  2-3 에러가 없다면 team의 저장 로직을 수행하고 저장으로

    2-3-1 완료된 team 세션 범위의 team 객체 공유를 마무리하기 위해 SessionStatus을 받아 setComplete처리를 한다.

    2-3-2 이렇게 처리하면 더 이상 Session 범위에 team객체가 존재하지 않게 된다.

 

package pe.pilseong.fooball.controller;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttribute;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

import lombok.extern.slf4j.Slf4j;
import pe.pilseong.fooball.entity.Team;
import pe.pilseong.fooball.repository.TeamRepository;

@Slf4j
@Controller
@RequestMapping("/teams")
@SessionAttributes("team")
public class TeamController {

  @Autowired
  private TeamRepository teamRepository;

  @GetMapping
  public String showTeamSetup(@SessionAttribute(required = false) Team team) {
    log.info("showTeamSetup in TeamController");

    if (team == null || team.getPlayers().size() == 0) {
      return "redirect:/players";
    }

    return "team-creation";

  }

  @PostMapping
  public String processTeamSetup(@Valid @ModelAttribute Team team, BindingResult errors, SessionStatus status) {
    if (errors.hasErrors()) {
      return "team-creation";
    }

    this.teamRepository.save(team);
    log.info("processTeamSetup in TeamController :: " + team.toString() + "\n");

    status.setComplete();

    return "redirect:/";
  }
}


3. 이 team 객체를 저장하는 TeamRepository를 다름과 같이 작성한다.

  3-1 보여주기를 위한 프로젝트기 때문에 저장로직만 작성한다.

 

package pe.pilseong.fooball.repository;

import pe.pilseong.fooball.entity.Team;

public interface TeamRepository {
  Team save(Team team);
}

 

  3-2 TeamRepository 구현체이다.

    3-2-0 지난 Player를 저장하는 것과 아주 유사하다. 

      3-2-0-1 우선 team을 저장한 후 저장된 team의 id를 받아 team-player 테이블에 넣어준다.

    3-2-1 특이한 부분은 데이터의 저장을 위해 Player 때 사용한 JdbcTemplate 대신에 SimpleJdbcInserter를 사용한다.

      3-2-1-1 생성자에서 SimpleJdbcInserter객체를 생성하는 방법에 주의한다. 특정 테이블과 매핑하는 부분이 있다.

 

    3-2-2 ObjectMapper는 객체의 데이터를 읽어서 key와 값 형식의 Map 객체를 반환한다.

    3-2-3 SimpleJdbcInsert는 Map객체를 통해 값을 받아서 데이터베이스에 저장한다.

      3-2-3-1 이 Map 객체 작성을 위해 ObjectMapper 클래스를 사용한 것 뿐이다.

      3-2-3-2 key가 다른 경우 그냥 반환된 map 객체에 put으로 추가하거나 삭제하면 된다.

    3-2-2 SimpleJdbcInsert도 PreparedStatementFactory처럼 id를 반환할 수 있는데 좀 더 단순하다.

      3-2-2-1 별도의 KeyHolder가 필요없다.

      3-2-2-2 저장 할 때 executeAndReturnKey를 사용하여 id를 반환 받을 수 있다.

 

package pe.pilseong.fooball.repository;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;

import lombok.extern.slf4j.Slf4j;
import pe.pilseong.fooball.entity.Player;
import pe.pilseong.fooball.entity.Team;

@Slf4j
@Repository
public class TeamRepositoryImpl implements TeamRepository {

  private SimpleJdbcInsert teamInserter;
  private SimpleJdbcInsert teamPlayerInserter;

  private ObjectMapper objectMapper;

  @Autowired
  public TeamRepositoryImpl(JdbcTemplate jdbc) {
    this.teamInserter = new SimpleJdbcInsert(jdbc).withTableName("team").usingGeneratedKeyColumns("id");

    this.teamPlayerInserter = new SimpleJdbcInsert(jdbc).withTableName("teams_players");

    this.objectMapper = new ObjectMapper();
  }

  @Override
  public Team save(Team team) {
    team.setCreatedAt(new Date());
    Long teamId = saveTeamDetails(team);

    team.setId(teamId);

    team.getPlayers().forEach(player -> {
      saveTeamsPlayers(player, teamId);
    });

    log.info("save in TeamRepositoryImpl");

    return null;
  }

  private void saveTeamsPlayers(Player player, Long teamId) {
    Map<String, Long> values = new HashMap<>();
    values.put("team_id", teamId);
    values.put("player_id", player.getId());

    this.teamPlayerInserter.execute(values);    
  }

  private Long saveTeamDetails(Team team) {
    @SuppressWarnings("unchecked")
    Map<String, Object> teamMap = this.objectMapper.convertValue(team, Map.class);
    @SuppressWarnings("unchecked")
    Map<String, Object> addressMap = this.objectMapper.convertValue(team.getAddress(), Map.class);
    
    teamMap.putAll(addressMap);
    teamMap.put("created_at", team.getCreatedAt());

    log.info("saveTeamDetails in TeamRepositoryImpl :: " + teamMap.toString());
    
    return this.teamInserter.executeAndReturnKey(teamMap).longValue();
  }
  
}

 

4. 결과 화면

  4-1 아무것도 입력하지 않고 submit했을 때 화면

 

 

  4-2 팀을 하나 만들고 등록한 데이터베이스 결과

 

 

  4-2-1 선수는 나밖에 없다 ㅋㅋㅋ

 

 

  4-2-2 대한민국 팀에도 나밖에 등록이 안되어 있다. 당연하게 나만 등록했으니

 

 

728x90
댓글