Spring/error2

Spring Boot JdbcTemplate 이용해서 데이터 읽고 쓰기

비뀨_ 2022. 3. 26. 08:27

JdbcTemplate란??

JdbcTemplate JDBC Core 패키지의 중심 클래스다.

JDBC 사용을 단순화하고 일반적인 오류를 방지하는 데 도움을 주고, SQL을 제공하고 결과를 추출하는 애플리케이션 코드를 남긴다. 

JdbcTemplate는 SQL 쿼리 또는 업데이트를 실행하여 ResultSets에 대한 반복을 시작하고 JDBC 예외를 포착하고 org.springframework.dao패키지에 정의된 일반적이고 보다 유익한 예외 계층으로 변환해준다.

 

공식 문서

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html

 

말 그대로 Jdbc Template는 Jdbc + Template(틀)이다. Jdbc를 쉽게 쓸 수 있게 도와주는 도구라고 볼 수 있다.

 

그렇다면 JDBC 란??

JDBC는 Java Database Connectivity로 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API이다.

JDBC는 데이터베이스에서 자료를 쿼리하거나 업데이트하는 방법을 제공한다. - 위키백과

 

public void joinMember(String id, String pw, String nickname) throws SQLException {
    Connection conn = null;
    PreparedStatement ps = null;
    try {
      conne = dataSource.getConnection();
      String sql = "insert into member values(?,?,?)";
      ps = connection.prepareStatement(sql);
      ps.setString(1,id);
      ps.setString(2,pw);
      ps.setString(3,nickname);
      
      ps.execute();
    } catch (SQLException e) {
      // 예외처리
    } finally {
      // 자원반납
      if (ps != null) ps.close();
      if (conn != null) conn.close();
    }
}

위는 JdbcTemplate를 사용하지 않을 때의 코드이다.

JSP로 웹 페이지를 개발할 때 보통 이렇게 썼었다.

 

JdbcTemplate를 쓴다면 위의 코드는 어떻게 될까

@Repository
public class JdbcMemberRepository implements MemberRepository {
    private JdbcTemplate jdbc;

    @Autowired
    public JdbcIngredientRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    public void joinMember(Member member) throws SQLException {
        jdbc.update(
            "insert into Member(id,pw,nickname) values (?, ?, ?)",
            member.getId(),
            member.getPw(),
            member.getNickname();
        );
    }
}

위처럼 코드가 깔끔해진다.

 

다시 말하자면 JdbcTemplate 자바에서 JDBC를 쉽게 사용할 수 있게 도와주는 것이다.

 

Spring에서 Jdbc 사용하기 위한 설정

Spring에서 JDBC 지원은 JdbcTemplate 클래스에 기반한다.

를 사용하려면 두가지를 해야한다.

1. pom.xml에 jdbc 의존성을 추가해준다.

<!-- Jdbc 추가 -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jdbc -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

maven repository에서 spring starter jdbc를 검색하면 나온다.

 

2. 데이터를 저장할 데이터베이스의 의존성을 추가해 준다.

데이터베이스는 H2를 사용하도록 한다.

<!-- H2 데이터베이스 추가 -->
<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

 

또는 Spring boot를 쓰고 있다면 Add Starter를 이용해 추가하면 된다.

3. H2를 사용한다면 버전 정보도 추가해야 함.

maven repository 사이트가서 h2를 검색해 버전을 찾아 properties에 추가하자.

pom.xml

 

JdbcTemplate를 이용한 데이터 추가

  • 테이블 생성
  • Query(질의)문을 정의할 Java의 인터페이스를 만들고, 실제 질의문을 수행할 클래스를 만든다.
  • 결과를 반환 받는다.

테이블 생성

create table if not exists Member (
  id varchar(25) not null ,
  pw varchar(25) not null ,
  nickname varchar(25) not null
);

위의 sql문이 적힌 파일을 Application의 classpath의 루트 경로에 있으면 

실행시 파일의 SQL문이 자동 실행된다.

src/main/resources 안에 '파일명.sql'로 만들자.

 

더미(가짜) 데이터 생성

delete from Member;
insert into Member(id, pw, nickname)
                values ('user1', '123456789', '테유저1');
insert into Member(id, pw, nickname)
                values ('user2', '123456789', '테유저2');
insert into Member(id, pw, nickname)
                values ('user3', '123456789', '테유저3');
insert into Member(id, pw, nickname)
                values ('user4', '123456789', '테유저4');

해당 파일은 data.sql로 테이블 생성과 마찬가지로 작성하고 역시 애플리케이션이 실행될 때 SQL문이 자동 실행된다.

 

Jdbc를 이용한 Member 레포지토리 구현 클래스 정의

@Repository
public class JdbcMemberRepository implements MemberRepository {
	
	private JdbcTemplate jdbc;
	
	@Autowired
	public JdbcMemberRepository(JdbcTemplate jdbc) {
		this.jdbc = jdbc;
	}

	@Override
	public List<Member> getMemberList() {
		return jdbc.query("select * from member",this::mapRowToMember);
	}
	
	public Member mapRowToMember(ResultSet rs,int row) throws SQLException {
		return new Member(rs.getString("id"),rs.getString("pw"),rs.getString("nickname"));
	}
	
}

JdbcTemplate를 @Autowired를 통해 생성자로 주입받고 

getMemberList() 를 호출했을 때 jdbc로 질의문을 호출한다. 

첫번째 인자는 sql문을 작성하고,

두번째 인자는 결과 목록을 가져오기 위해 각 Row를 돌 때마다 mapRowToMember()를 호출해

Member 객체를 받아온다. 

그 결과 List<Member>의 형태로 전체 결과 값이 반환된다.

 

Controller에서 Get 요청시 수행하는 list의 모습

@GetMapping("/list")
public String getList(Model model) {
    List<Member> memList = memberRepo.getMemberList();
    model.addAttribute("memList",memList);

    return "list";
}

Controller에서 localhost:8081/member/list를 호출하게 되면

해당 메서드가 수행해 그 결과를 model 객체에 담고 list.html 호출하게 된다.

 

회원 목록 html ( Thymeleaf )

<body>
	<h1>회원 목록!</h1>
	<div class="grid">
		<div class="member-group" id="wraps">
			<h3>회원 이름과 닉네임 목록:</h3>
			<div th:each="member : ${memList}">
				<span th:text="${member.id}">INGREDIENT</span>  
				<span th:text="${member.nickname}">INGREDIENT</span><br />
			</div>
		</div>
	</div>
	
</body>

Thymeleaf를 통해 받아온 member의 목록을 th:each를 통해 화면에 출력하라고 한다.

 

최종적으로 

형태의 목록이 나오게 된다.

 

 

SimpleJdbcInsert 이용해 키 가져오

지금까지 Member 객체를 사용(참조)하는 테이블이 없었지만 다른 테이블에서 회원가입시

다른 곳의 테이블에 회원을 참조하는 일이 있을 수 있다.

그렇기 때문에 관리가 조금 쉽게 회원은 아이디가 PK가 아니라 회원 번호가 PK라고 가정하자.

회원 가입할 때 이벤트로 2등급 멤버쉽을 공짜로 구매하게 한 것으로 해준다면

멤버쉽 등급은 2등급으로 정해져 있지만, 회원번호는 계속 바뀌기 때문에 멤버쉽 구매 테이블에 

회원 번호가 필요할 것이다.

설명만을 위한 예시임. 절대 이렇게 짜지 않음

그렇다면 회원가입을 하고 또 회원테이블에 조회해서 방금 가입한 사람의 번호를 조회할 것인가?

매우 불편하고, 자원낭비일 것이다.

KeyHolder라는 것을 쓸 수도 있지만 그것보다 간단한게 SimpleJdbcInsert이다.

 

	@Override
	public long joinMemberAndGetKey(Member member) {
		@SuppressWarnings("unchecked")
		Map<String,Member> values = objectMapper.convertValue(member, Map.class);
		//execute(실행)하고 ReturnKey(키를 받아온다)
		return memberInserter.executeAndReturnKey(values).longValue();
	}

이렇게하면, 회원가입과 동시에 키를 받아오게 된다.