Spring/spring_old

07./ Spring Web MVC-DB연계

slow333 2023. 1. 12. 21:29

기존 memory(map class) 방식에서 데이터를 DB에 생성하고 가져오는 방식으로 변경

jdbc로 연동하던 중 팅김(애러 원인... => 초기화를 MemoryMemberRepository로 잘못 지정함...

@Controller에서 생성자 호출 시 memberService를 통해 구현해야함(C->S->R)

@Controller
public class MemberController {

  private MemberService memberService;

  @Autowired
  public MemberController(MemberService memberService) {
    this.memberService = memberService;
  }

 

1. build.gradle 설정

DB연계를 위해서는 우선 DB 접속을 위한 library를 graddle에서 implement해야함

dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-jdbc' // 추가
   implementation 'mysql:mysql-connector-java'   // 추가
}

jdbc는 자바에서 DB에 접속하기 위한 interface이고 mysql-connector는 연동하고자 하는 DB의 구현체로 DB 별로 구현체가 다르므로 검색해서 추가해야함.

JPA 사용을 위한 추가 설정

//  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

2. application.properties 설정

C:\study\hello-spring\src\main\resources\application.properties

DB 연결을 위한 driver-class-name, datasource.url, username, password 등을 설정해야함

(여기서 설정하면 실제 class에서 사용할때 지정하지 않아도됨 : 

  - Connection conn; 연결을 위한 객체를 spring에서 생성해줌)

# MySQL
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# DB Source URL
spring.datasource.url=jdbc:mysql://localhost:3306/memberservice
# DB username
spring.datasource.username=kalpa
# DB password
spring.datasource.password=dongwook0#

JPA 구현을 위한 추가 설정

# true 설정시 JPA 쿼리문 확인 가능
#spring.jpa.show-sql=true

# DDL(create, alter, drop) 정의시 DB의 고유 기능을 사용할 수 있다.
#spring.jpa.hibernate.ddl-auto=update

# JPA의 구현체인 Hibernate가 동작하면서 발생한 SQL의 가독성을 높여준다.
#spring.jpa.properties.hibernate.format_sql=true

3. JPA 없이 jdbc 만을 이용해서 DB CRUD 구현하기

DB 연동 시험(잘되면 java와 db간 연동은 되는 것임)

@SpringBootApplication
public class HelloSpringApplication {

 public static void main(String[] args) {
  SpringApplication.run(HelloSpringApplication.class, args);
  try {
   // MySQL DB용 드라이로드
   Class.forName("com.mysql.cj.jdbc.Driver");
   // DB연결
   Connection conn =
       DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db이름", "아이디", "패스워드");
   System.out.println("mysql db 연결 성공");

   conn.close();
   System.out.println("mysql 연결해제 성공");
  }
  catch(ClassNotFoundException error) {
   System.out.println("mysql driver 미설치 또는 드라이버 이름 오류");
  }
  catch(SQLException error) {
   System.out.println("DB접속오류");
  }
 }
}

Spring을 이용해서 기존의 MemoryRepository를 JdbcRepository로 변경해서 구현함

(기존 코드는 변경없이 Spring DI 구성만 변경해서 구현)

@Configuration
public class SpringConfig {

  DataSource dataSource;

  @Autowired
  public SpringConfig(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  @Bean
  public MemberService memberService(){
    return new MemberService(memberRepository());
  }

  @Bean
  public MemberRepository memberRepository(){
//    return new MemoryMemberRepository();
    return new JdbcMemberRepository(dataSource);
  }
}

다소 복잡하지만 jdbc만을 이용해서 DB연계 후에 데이터 가져오는 코드....

public class JdbcMemberRepository implements MemberRepository{

  private final DataSource dataSource;

  Connection conn = null; 
  PreparedStatement pstmt = null;
  ResultSet rs = null;

  public JdbcMemberRepository(DataSource dataSource) {
    this.dataSource = dataSource;
  }

  @Override
  public void save(Member member) throws ClassNotFoundException {

    String sql = "insert into member(name) values(?)";

    try {
      conn = getConnection();// 위에서 설정한 값으로 db와 연결을 생성해줌
      pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
      pstmt.setString(1, member.getName());
      pstmt.executeUpdate();
      rs = pstmt.getGeneratedKeys();

      if (rs.next()) {
        member.setId(rs.getLong(1));
      } else {
        throw new SQLException("id 조회 실패");
      }

    } catch (Exception e) {
      throw new IllegalStateException(e);
    } finally {
      close(conn, pstmt, rs);
    }
  }

  @Override
  public Optional<Member> findById(Long id) throws ClassNotFoundException {

    String sql = "select * from member where id = ?";

    try {
      conn = getConnection();// 위에서 설정한 값으로 db와 연결을 생성해줌
      pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
      pstmt.setString(1, String.valueOf(id));
      rs = pstmt.executeQuery();

      if (rs.next()) {
        Member member = new Member();
        member.setId(rs.getLong("id"));//db의 실제 컬럼 이름
        member.setName(rs.getString("name"));//db의 실제 컬럼 이름
        member.setRegDate(rs.getTimestamp("regdate")); //db의 실제 컬럼 이름

        return Optional.of(member);
      } else {
        return Optional.empty();
      }
    } catch (Exception e) {
      throw new IllegalStateException(e);
    }finally {
      close(conn, pstmt, rs);
    }
  }

  @Override
  public Optional<Member> findByName(String name) throws ClassNotFoundException {

    String sql = "select * from member where name = ?";

    try {
      conn = getConnection(); // 위에서 설정한 값으로 db와 연결을 생성해줌
      pstmt = conn.prepareStatement(sql);
      pstmt.setString(1, name);

      rs = pstmt.executeQuery();

      if (rs.next()) {
        Member member = new Member();
        member.setId(rs.getLong("id"));
        member.setName(rs.getString("name"));
        member.setRegDate(rs.getTimestamp("regdate"));

        return Optional.of(member);
      } else {
        return Optional.empty();
      }
    } catch (Exception e) {
      throw new IllegalStateException(e);
    } finally {
      close(conn, pstmt, rs);
    }
  }

  @Override
  public List<Member> findAll() throws ClassNotFoundException {

    String sql = "select * from member";

    try {
      conn = getConnection();
      pstmt = conn.prepareStatement(sql);
      rs = pstmt.executeQuery();

      List<Member> members = new ArrayList<>();

      while (rs.next()) {
        Member member = new Member();
        member.setId(rs.getLong("id"));
        member.setName(rs.getString("name"));
        member.setRegDate(rs.getTimestamp("regdate"));
        members.add(member);
      }

      return members;
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }finally {
      close(conn, pstmt, rs);
    }
  }
  private Connection getConnection(){
    return DataSourceUtils.getConnection(dataSource);
  }

  private void close(Connection conn, PreparedStatement pstmt, ResultSet rs) {
    try{
      if (rs != null) {
        rs.close();
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
    try{
      if (pstmt != null) {
        pstmt.close();
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
    try {
      if (conn != null) {
        conn.close();
      }
    } catch (SQLException e) {
      throw new RuntimeException(e);
    }
  }
  private void close(Connection conn) throws SQLException{
    DataSourceUtils.releaseConnection(conn, dataSource);
  }
}

findbyName으로 맴버를 찾는 것.. 잘 안되다가 겨우 됨

(html form action에 링크 수정하고, Optional에 대해서 isPresent로 검토하고...되내.. 쩜 3시간 삽질)

 @GetMapping("/members/member")
  public String memberByIdForm(){
    return "members/findOne";
  }
//  @PostMapping("/members/member")
//  public String findOne(MemberForm form) throws ClassNotFoundException {
//
//    Optional<Member> oneMember = memberService.findByName(form.getMemberName());
//    System.out.println("찾은 member : "+oneMember);
//    System.out.println("form name: " +form.getMemberName());
//
//    Member member = oneMember.get();
//
//    return "redirect:/members/oneMember";
////    return "redirect:/members/oneMember.html";
////    return "members/memberOneByName";
//  }
  @PostMapping("/members/oneMember")
  public String list(Model model,MemberForm form) throws ClassNotFoundException {

    Optional<Member> memberName = memberService.findByName(form.getMemberName());
    if (memberName.isPresent()) {
      Member member = memberName.get();
      model.addAttribute("member", member);
    } else {
      return "members/findOne";
    }
    return "members/oneMember";
  }