Spring/공부

[Spring MVC] Spring Jdbc 정리 (NamedParameterJdbcTemplate예제)

하루인생 2021. 2. 4. 23:49

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 클래스에 대해 알아보겠다.