티스토리 뷰

728x90

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

  1-1 데이터베이스에 저장하면서 id를 받아오는 방법

  1-2 PreparedStatementCreator, PreparedStatementFactory 사용 방법

    1-2-1 PreparedStatementCreatorFactory는 저장할 값 타입 정보를 가진다.

    1-2-2 PreparedStatementCreator 는 저장할 값 정보를 가진다.

 

2. 검증 로직을 통과한 경우 PlayerRepository를 통하여 선수 정보와 선수-기술 매핑 정보를 저장한다.

  2-1 PlayerRepository.java 인터페이스

    2-1-1 우선 저장하는 것만 작성하였다. JdbcTemplate으로 작성하는 걸 보이려니 진짜 복잡하다.

 

package pe.pilseong.fooball.repository;

import pe.pilseong.fooball.entity.Player;

public interface PlayerRepository {
  Player save(Player player);
}

 

  2-2 PlayerRepositoryImpl.java

    2-2-0 저장 순서는 

      2-2-0-1 우선 선수정보를 저장하여 해당 선수의 ID를 확보한 후

      2-2-0-2 그 id를 가지고 선수-기술 테이블에 매핑하여 저장한다.

    2-2-1 데이터베이스는 id를 identity로 지정하여 auto increment를 사용하고 있다.

 

    2-2-2 savePlayer 메소드는 하나의 player객체를 데이터베이스에 저장하고 id를 반환하고 있다.

      2-2-2-1 짜증나는 부분이 어떻게 id를 받아오느냐 인데 그냥 queryForObject로 받아올 수도 있다.

      2-2-2-2 여기서는 PreparedStatmentCreatorFactory와 PreparedStatementCreator를 사용하고 있다.

 

      2-2-2-3 PreparedStatementCreatorFactory에서 id을 반환하도록 설정해야 반환이 이루어진다.

        2-2-2-3-1 pscf.setReturnGeneratedKeys(true); 코드가 이에 해당하고 가장 중요한 부분이다.

 

      2-2-2-4 실제 처리는 jdbcTemplate의 update로 저장되는데

        2-2-2-4-1 첫번째는 데이터가 있는 PreparedStatementCreator이고 두번째인자가 id를 저장할 KeyHolder이다.

        2-2-2-4-2 이 keyHolder 객체가 반환 id를 저장하고 최종으로 long 타입으로 반환해 준다.

    2-2-3 이제 선수 id도 알고 기술 id도 아니 선수-기술 테이블에 저장은 간단히 JdbcTemplate의 update로 가능하다.

 

package pe.pilseong.fooball.repository;

import java.sql.Timestamp;
import java.sql.Types;
import java.util.Arrays;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

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

@Slf4j
@Repository
public class PlayerRepositoryImpl implements PlayerRepository {

  @Autowired
  private JdbcTemplate jdbc;

  @Override
  public Player save(Player player) {

    log.info("save in PlayerRepository " + player.toString());

    Long playerId = savePlayer(player);

    player.setId(playerId);

    player.getSkills().forEach(skill-> {
      this.jdbc.update("insert into players_skills(player_id, skill_id) values(?, ?)", 
      	playerId, skill.getId());
    });

    return player;
  }
  
  private Long savePlayer(Player player) {
    player.setCreatedAt(new Date());

    String query = "insert into player(first_name, last_name, salary, created_at) values (?, ?, ?, ?)";
    PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(query, 
      Types.VARCHAR, Types.VARCHAR, Types.DECIMAL, Types.TIMESTAMP);

    pscf.setReturnGeneratedKeys(true);
    PreparedStatementCreator psc = pscf.newPreparedStatementCreator(Arrays.asList(
      player.getFirstName(), player.getLastName(), 
      player.getSalary(), new Timestamp(player.getCreatedAt().getTime())));

    KeyHolder keyHolder = new GeneratedKeyHolder();
    jdbc.update(psc, keyHolder);

    return keyHolder.getKey().longValue();
  }
  
}

 

3. 이 Repository를 사용하는 컨트롤러의 해당 함수

  3-1 검증에 통과한 경우 우선 팀 객체에 선수를 추가한 후에

  3-2 선수 정보를 데이터베이스에 저장한다.

  3-3 마지막으로 /teams라는 uri로 전환하고 있다.

  3-4 /teams 페이지에서는 SessionAttributes를 사용할 경우 선수가 추가된 team 객체를 사용할 수 있다.

 

  @PostMapping
  public String processPlayerSkill(@Valid @ModelAttribute Player player, BindingResult errors, Model model,
    @ModelAttribute Team team) {

    if (errors.hasErrors()) {

      log.info("processPlayerSKill error info :::" + errors.toString());

      log.info(player.toString());

      setMenu(model);

      model.addAttribute("checked", 
        player.getSkills().stream().map(skill-> 
          skill.getId()
        ).collect(Collectors.toList())
      );
      return "player-setting";
    }

    log.info(player.toString());

    team.addPlayer(player);

    this.playerRepository.save(player);

    log.info(team.toString());
    return "redirect:/teams";
  }

 

4. 결과화면

  4-1 기술 몇개를 선택하여 저장한 후 player가 저장된 모습

 

 

  4-2 선수와 기술의 매핑테이블

    4-2-1 기술 테이블을 보면 이 선수는 1,5,9번 즉 Finshing, Vision, Interceptions가 지정됨을 알 수 있다.

 

 

 

728x90
댓글