JdbcTemplate 을 이용한 쿼리 실행
JdbcTemplate 은 JDBC 코어 패키지의 중심 클래스이며 자원 생성 및 반환처리로 JDBC 사용을 단순화한다.(데이터 저장을 위해 도와주는 API라고 보면 된다.) 가장 먼저 JdbcTemplate 객체를 생성하고
package spring;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
public class MemberDao {
private JdbcTemplate jdbcTemplate;
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
스프링 설정에 MemberDao 빈 설정을 추가한다.
@Bean
public MemberDao memberDao() {
return new MemberDao(dataSource());
}
package org.springframework.jdbc.core;
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
// RowMapper 객체를 query 메서드의 매개변수로 넘겨주게 되면 스프링 컨테이너는 sql을 수행 후 자동으로 RowMapper 객체의 mapRow()를 호출한다
결과가 1행인 경우 사용할 수 있는 queryForObject() 메서드를 제공한다.주요 메서드는 다음과 같다.
public int count() {
// 두번째 파라미터는 컬럼을 읽어올 때 사용할 타입 지정
Integer count = jdbcTemplate.queryForObject(
"select count(*) from MEMBER", Integer.class
);
return count;
}
package org.springframework.jdbc.core
import java.sql.Connection;
import java.sql.PreparedStatement
import java.sql.SQLException
public interface PreparedStatementCreator {
PreparedStatement createPreparedStatement(Connection con) throws SQLException;
}
PreparedStatementCreator 인터페이스의 createPreparedStatement() 메서드는 Connection 타입의 파라미터를 갖는다. PreparedStatementCreator를 구현한 클래스는 파라미터인 con 을 이용해 PreparedStatement 객체를 생성하고 인덱스 파라미터를 알맞게 설정한 뒤에 리턴하면 된다.
public void insert(Member member) {
// GeneratedKeyHolder 객체를 생성(자동 생성된 키값을 구해주는 KeyHolder 구현클래스임)
KeyHolder keyHolder = new GeneratedKeyHolder();
//PreparedStatementCreator 객체와 KeyHolder 객체를 파라미터로 가짐
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
// 파라미터로 전달받은 Connection을 이용해서 PreparedStatement 생성
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER(EMAIL, PASSWORD, NAME, REGDATE) values (?,?,?,?)",
new String[]{"ID"}
);
// 인덱스 파라미터의 값 설정
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
// 생성한 PreparedStatement 객체 리턴
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey();
member.setId(keyValue.longValue());
}
테스트
MemberDao.java
package spring;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import java.sql.*;
import java.util.List;
@Component
public class MemberDao {
private JdbcTemplate jdbcTemplate;
public MemberDao(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
public Member selectByEmail(String email) {
List<Member> results = jdbcTemplate.query(
"select * from MEMBER where EMAIL = ?",
/* RowMapper 구현 클래스를 만들어 해당 코드 삭제
new RowMapper<Member>() {
@Override
public Member mapRow(ResultSet rs, int rowNum) throws SQLException {
Member member = new Member(
rs.getString("EMAIL"),
rs.getString("PASSWORD"),
rs.getString("NAME"),
rs.getTimestamp("REGDATE").toLocalDateTime());
member.setId(rs.getLong("ID"));
return member;
}
},
*/
new MemberRowMapper(),
email);
return results.isEmpty() ? null : results.get(0);
}
public void insert(Member member) {
// GeneratedKeyHolder 객체를 생성(자동 생성된 키값을 구해주는 KeyHolder 구현클래스임)
KeyHolder keyHolder = new GeneratedKeyHolder();
//PreparedStatementCreator 객체와 KeyHolder 객체를 파라미터로 가짐
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
// 파라미터로 전달받은 Connection을 이용해서 PreparedStatement 생성
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER(EMAIL, PASSWORD, NAME, REGDATE) values (?,?,?,?)",
new String[]{"ID"}
);
// 인덱스 파라미터의 값 설정
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
// 생성한 PreparedStatement 객체 리턴
return pstmt;
}
}, keyHolder);
Number keyValue = keyHolder.getKey();
member.setId(keyValue.longValue());
/* 람다식 사용
jdbcTemplate.update((Connection con) -> {
PreparedStatement pstmt = con.prepareStatement(
"insert into MEMBER (EMAIL, PASSWORD, NAME, REGDATE)" + "values (?,?,?,?)",
new String[]{"ID"});
pstmt.setString(1, member.getEmail());
pstmt.setString(2, member.getPassword());
pstmt.setString(3, member.getName());
pstmt.setTimestamp(4, Timestamp.valueOf(member.getRegisterDateTime()));
return pstmt;
}, keyHolder);
*/
}
public void update(Member member) {
jdbcTemplate.update(
"update Member set NAME=?, PASSWORD=? where EMAIL=?",
member.getName(), member.getPassword(), member.getEmail()
);
}
public List<Member> selectAll() {
List<Member> results = jdbcTemplate.query("select * from MEMBER", new MemberRowMapper());
return results;
}
public int count() {
// 두번째 파라미터는 컬럼을 읽어올 때 사용할 타입 지정
Integer count = jdbcTemplate.queryForObject(
"select count(*) from MEMBER", Integer.class
);
return count;
}
}
MainForMemberDao
package main;
import config.AppCtx;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import spring.Member;
import spring.MemberDao;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
public class MainForMemberDao {
/*
static 필드에 autowired 안되는 이유는
스프링 컨테이너가 올라갈때 등록된 빈을 가지고 설정에 따른 의존성 주입을 하는데
static이면 컨테이너가 올라가기도 전에 관련 의존성을 찾기 때문에
memberDao 클래스가 빈으로 생성되기 전이므로 nullpointer가 발생
*/
private static MemberDao memberDao;
public static void main(String[] args) {
// 자바 어노테이션을 이용한 클래스로부터 객체 설정정보를 가져옴
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppCtx.class);
memberDao = ctx.getBean(MemberDao.class);
selectAll();
updateMember();
insertMember();
ctx.close();
}
private static void selectAll() {
System.out.println("-----------------selectAll");
int total = memberDao.count();
System.out.println("전체 데이터 : " + total);
List<Member> members = memberDao.selectAll();
for (Member m : members) {
System.out.println(m.getId() + ":" + m.getEmail() + ":" + m.getName());
}
}
private static void updateMember() {
System.out.println("-----------------updateMember");
Member member = memberDao.selectByEmail("eeun95@gmail.com");
String oldPw = member.getPassword();
String newPw = Double.toHexString(Math.random());
member.changePassword(oldPw, newPw);
memberDao.update(member);
System.out.println("암호 변경 : " + oldPw + ">" + newPw);
}
private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMddHHmmss");
private static void insertMember() {
System.out.println("-----------------insertMember");
String prefix = formatter.format(LocalDateTime.now());
Member member = new Member(prefix + "@test.com", prefix, prefix, LocalDateTime.now());
memberDao.insert(member);
System.out.println(member.getId() + " 데이터 추가");
}
}
🙋♀️ 순서가 당최 어떻게 되나요?
💡 AnnotationConfigApplicationContext 생성자를 이용해 컨텍스트 객체를 생성합니다 (스프링 컨테이너 초기화 시점, 설정정보를 읽어와 빈 객체 생성 및 의존 주입 수행) -> memberDao 객체에 빈 객체를 담아줍니다 -> 각각의 메서드가 실행됩니다