[Spring MVC] Spring Jdbc 정리 (NamedParameterJdbcTemplate예제)
NamedParameterJdbcTemplate
NamedParameterJdbcTemplate이 제공하는 메서드는 JdbcTemplate과 비슷하다.
하지만 sql문에서 파라미터로 값을 받는 방법에 차이가 있다.
JdbcTemplate은 인덱스 기반의 파라미터를 설정하는 반면에
NamedParameterJdbcTemplate는 이름 기반의 파라미터를 설정한다.
이름 기반의 파라미터를 입력받기 위해서는 두 가지 방법이 있다.
바로 Map과 SqlParameterSource를 이용하는 것이다.
아래 사진과 같이 각 메서드의 파라미터 유형을 보면 Map과 SqlParameterSource를 지원해주는 것을 볼 수 있다.
이 두 가지 방법에 대해 알아보자
Map사용
1. queryForObject
sql문에 따로 입력될 부분이 없을 경우 아무 값도 가지지 않는 Collections.emptyMap()을 넣어주면 된다.
public int getStudentNum() {
return jdbcTemplate.queryForObject("select count(*) from student"
, Collections.emptyMap()
, Integer.class);
}
sql문에 입력될 값이 있는 경우 Map을 만들어줘야 한다. 그리고 다음과 같이 Map의 <key, value>에 맞게 값을 넣어준다. 그리고 이 Map객체를 queryForObject의 파라미터 값으로 넣어주면 된다.
public Student getStudent(String name) {
Map<String, String> map = new HashMap<String, String>();
map.put("name", name);
return jdbcTemplate.queryForObject("SELECT * FROM student WHERE name = :name"
,map
, (rs,rowNum)-> {Student student = new Student();
student.setAge(rs.getInt("age"));
student.setName(rs.getString("name"));
return student;
});
}
위와 같이 key-value쌍이 단 하나인 경우에는 Collections.singletonMap을 사용해도 된다.
public Student getStudent(String name) {
Map<String, String> map = Collections.singletonMap("name", name);
return jdbcTemplate.queryForObject("SELECT * FROM student WHERE name = :name"
,map
, (rs,rowNum)-> {Student student = new Student();
student.setAge(rs.getInt("age"));
student.setName(rs.getString("name"));
return student;
});
}
여기서 궁금증이 생겼다. Map에 String형과 int형을 모두 저장하고 싶을 땐 어떻게 해야 할까?
아래 코드처럼 Map 생성시 value의 값을 Object형으로 받으면 된다.
public Student getStudent(String name, int age) {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("age",age);
return jdbcTemplate.queryForObject("SELECT * FROM student WHERE name = :name and age = :age"
,map
, (rs,rowNum)-> {Student student = new Student();
student.setAge(rs.getInt("age"));
student.setName(rs.getString("name"));
return student;
});
}
2. query
아래 코드는 여러 레코드를 가져오는 qeury에 적절한 예제는 아니지만 이해를 위해 작성해봤다.
queryForObject와 차이점은 메서드 반환타입을 List로 변경한 것 밖에 없다.
public List<Student> getStudents(String name, int age){
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", name);
paramMap.put("age",age);
return jdbcTemplate.query("select * from student where name = :name and age = :age"
, paramMap
, (rs, rowNum)-> {Student student = new Student();
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
return student;});
}
그래서 모든 학생의 정보를 가져오는 코드를 추가해봤다. sql문에 전달할 파라미터가 없으므로 Collections.emptyMap을 사용하였다.
public List<Student> getStudents(){
return jdbcTemplate.query("select * from student"
, Collections.emptyMap()
, (rs, rowNum)-> {Student student = new Student();
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
return student;});
}
3. update
update문에서 사용할 수 있는 insert, update, delete를 알아보자.
사용방법이 모두 비슷해서 코드로만 간단하게 작성하였다.
(1) insert
sql문만 잘 작성하면 다음과 같이 간단하게 작성할 수 있다.
public int insertStudent(Student student) {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", student.getName());
paramMap.put("age", student.getAge());
int result = jdbcTemplate.update("insert into student values(:name, :age)"
, paramMap);
return result;
}
(2) update
public int updateStudent(String name, int age) {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("name", name);
paramMap.put("age", age);
int result = jdbcTemplate.update("update student set age = :age where name = :name"
, paramMap);
return result;
}
(3) delete
public int deleteStudent(String name) {
Map<String, String> paramMap = Collections.singletonMap("name", name);
int result = jdbcTemplate.update("delete from student where name = :name"
, paramMap);
return result;
}
다음은 테스크 출력 코드와 결과이다.
SqlParameterSource
SqlParameterSource는 인터페이스이다. 그래서 구현한 클래스를 사용해서 파라미터 값을 전달해야한다. 구현된 클래스는 두 가지가 있다. 바로 BeanPropertySqlParameterSource와 MapSqlParameterSource이다. BeanPropertySqlParameterSource는 쿼리의 파라미터(:name)에 빈 객체의 프로퍼티 값을 이용해서 설정된다. 코드를 보면 이해가 된다.
각각의 메서드에 이 두 클래스를 적용해 보겠다.
1. queryForObject
메서드에서 Student객체를 받아서 그 객체를 BeanPropertySqlParameterSource의 파라미터 값으로 넣었고 변수명을 param이라고 지었다. queryForObject코드에 param을 넣어주면 student객체 내부에 설정된 name과 age에 접근해서 쿼리의 파라미터에 값을 넣어준다.
public Student getStudent(Student student) {
SqlParameterSource param = new BeanPropertySqlParameterSource(student);
return jdbcTemplate.queryForObject("select * from student where name = :name"
, param
,(rs,rowNum)->{
Student st = new Student();
st.setName(rs.getString("name"));
st.setAge(rs.getInt("age"));
return st;
});
}
MapSqlParameterSource는 Map과 비슷하다.
addValue를 통해 <key, value>쌍을 넣어주면 쿼리 파라미터로 사용 가능하다.
public Student getStudent(String name) {
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("name", name);
return jdbcTemplate.queryForObject("select * from student where name = :name"
, param
,(rs,rowNum)->{
Student st = new Student();
st.setName(rs.getString("name"));
st.setAge(rs.getInt("age"));
return st;
});
}
2. update
(1) insert
insert문에서는 객체를 그대로 넣어주면 되므로 BeanPropertySqlParameterSource가 간결하다.
public int insertStudent(Student student) {
SqlParameterSource param = new BeanPropertySqlParameterSource(student);
int result = jdbcTemplate.update("insert into student values(:name, :age)"
, param);
return result;
}
MapSqlParameterSource를 이용했다.
public int insertStudent(Student student) {
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("name", student.getName());
param.addValue("age", student.getAge());
int result = jdbcTemplate.update("insert into student values(:name, :age)"
, param);
return result;
}
(2) update
조건이 많은 쿼리인 경우 MapSqlParameterSource를 사용하는게 코드를 작성하기에 좋을 것 같다.
public int updateStudent(Student student) {
SqlParameterSource param = new BeanPropertySqlParameterSource(student);
int result = jdbcTemplate.update("update student set age = :age where name = :name"
, param);
return result;
}
public int updateStudent(String name, int age) {
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("name", name);
param.addValue("age", age);
int result = jdbcTemplate.update("update student set age = :age where name = :name"
, param);
return result;
}
(3) delete
public int deleteStudent(Student student) {
SqlParameterSource param = new BeanPropertySqlParameterSource(student);
int result = jdbcTemplate.update("delete from student where name = :name"
, param);
return result;
}
public int deleteStudent(String name) {
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("name", name);
int result = jdbcTemplate.update("delete from student where name = :name"
, param);
return result;
}
오늘의 결론
NamedParameterJdbcTemplate의 사용방법은 정말 다양하다. 자신의 상황에 맞게 코드를 사용하면 될 것 같다.
지금까지 JdbcTemplate과 NamedParameterJdbcTemplate의 사용법을 알아봤다.
다음에는 SimpleJdbcInsert 클래스에 대해 알아보겠다.