1. Spring JDBC란?
- Spring JDBC는 DriverManager에 드라이버를 로드하고, Connection, Statement, ResultSet 관련 작업, 자원 해제 작업과 트랜잭션 작업을 모두 담당 해주기 때문에 상당한 소스코드를 줄일 수 있게 된다.
- Spring JDBC는 JDBC 작업 중에 발생하는 모든 예외를 처리해준다. DB별 에러 코드를 참고해서 SQLException 예외를 DataAccessException 타입의 계층구조 내의 예외로 변환해준다.
- Spring JDBC는 멀티 쓰레드 환경에 안전하다.
- Spring JDBC의 접근 방법 중에서 JdbcTemplate이 가장 대표적인데, JdbcTemplate 기능을 모두 갖고 있으면서 더 발전되고 편리한 사용 방법을 제공해주는 접근 방법들이 생겨났으며, 대표적으로 SimpleJdbcTemplate, SimpleJdbcInsert, SimpleJdbcCall이 있다.
※ JdbcTemplate 사용법 참고 : https://kimcoder.tistory.com/241
※ JdbcTemplate에서 제공하는 세밀한 템플릿/콜백 방식을 직접 이용하고 싶다면 getJdbcOperation() 메소드를 이용하면 된다.
2. SimpleJdbcTemplate
1) SimpleJdbcTemplate란?
- JdbcTemplate의 기능을 향상시킨 것
- 실행, 조회, 배치(일괄 작업)으로 구분되어 제공된다.
2) SimpleJdbcTemplate의 생성
public class UserDao {
SimpleJdbcTemplate simpleJdbcTemplate;
public void setDataSource(DataSource dataSource) {
this.simpleJdbcTemplate=new SimpleJdbcTemplate(dataSource);
}
}
3) 동적 SQL
- JdbcTemplate에서는 '?' 치환자를 이용하여 동적으로 SQL을 완성하는데, 이 방법은 SQL 주입 공격에 위험하다.
- SimpleJdbcTemplate에서는 더 편한 이름 치환자 기능을 제공한다. 이름을 이용하기 때문에 파라미터 바인딩에는 신경을 안써도 된다.
INSERT INTO USER(ID, NAME, AGE) VALUES (:id, :name, :age);
- 이름 치환자를 사용하면 오브젝트나 맵을 활용할 수 있다. 오브젝트의 속성이나 맵의 key를 이용하여 이름을 바인딩 할 수 있는 것이다.
(1) BeanPropertySqlParameterSource
여기에서 말하는 bean은 spring bean이 아닌 java bean을 말한다.
public class User {
int id;
String name;
int age;
// getter, setter 메소드 생략
}
User user = new User(1, "Jooyeok", 25);
BeanPropertySqlParameterSource beanPropertySqlParameterSource
= new BeanPropertySqlParameterSource(user);
(2) MapSqlParameterSource
MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource()
.addValue("id", 1)
.addValue("name", "Jooyeok")
.addValue("age", 25)
4) SQL 실행 메소드
- INSERT, UPDATE, DELETE문을 사용할 때는 SimpleJdbcTemplate의 update() 메소드를 사용한다. update() 메소드는 SQL 실행으로 영향 받은 레코드의 개수를 리턴해준다.
(1) 위치 치환자 사용
- args를 추가적으로 작성해주면 된다.
simpleJdbcTemplate.update("INSERT INTO USER(ID, NAME, AGE, args) VALUES(?,?,?)", 1, "Jooyeok", 25);
(2) Map
Map<String, Object> map = new HashMap<String, Object>();
...
simpleJdbcTemplate.update("INSERT INTO USER(ID, NAME, AGE, args) VALUES(:id, :name, :age)", map);
(3) MapSqlParameterSource
simpleJdbcTemplate.update("INSERT INTO USER(ID, NAME, AGE, args) VALUES(:id, :name, :age)", mapSqlParameterSource);
(4) BeanPropertySqlParameterSource
simpleJdbcTemplate.update("INSERT INTO USER(ID, NAME, AGE, args) VALUES(:id, :name, :age)", beanPropertySqlParameterSource);
5) SQL 조회 메소드
- SELECT문을 사용할 때는 사용하는 메소드
- 하나의 레코드를 조회하는 메소드의 경우에는 검색된 결과가 없다면 EmptyResultDataAccessException 예외가 발생하고, 그 외의 부적절한 결과의 수에 대해서는 IncorrectResultSizeDataAccessException 예외가 발생한다.
※ 전자의 예외는 후자의 예외를 상속 받음
(1) queryForInt
- 하나의 int 값을 조회할 때 사용
simpleJdbcTemplate.queryForInt("SELECT AGE FROM USER WHERE NAME=:name", mapSqlParameterSource);
(2) queryForLong
- 하나의 long 값을 조회할 때 사용
(3) quertForObject
- 하나의 레코드를 조회할 때 사용
- 결과를 가져올 오브젝트 타입을 직접 지정할 수 있음
<1> 클래스 사용
- 형식 : <T> T queryForObject(String, sql, Class<T> requiredType, [SQL parameters])
String name = simpleJdbcTemplate.queryForObject("SELECT NAME FROM USER WHERE ID=?", String.class, id);
- 하나의 결과값을 가져올 Object 타입을 직접 지정할 수 있음
<2> RowMapper 사용
- 형식 : <T> T queryForObject(String, sql, RowMapper<T> rm, [SQL parameters])
- 레코드를 하나의 오브젝트로 전환할 때 사용
※ RowMapper을 사용한 예시는 위에 올려둔 JdbcTemplate 사용법 포스팅에서 다뤘다.
(4) query
- 형식 : <T> List<T> query(String, sql, RowMapper<T> rm, [SQL parameters])
- 여러 개의 레코드를 조회할 때 사용
- List를 통해 여러 오브젝트를 저장할 수 있음
- 결과 레코드의 개수에 따른 예외는 발생하지 않음
(5) queryForMap
- 형식 : Map<String, object> queryForMap(String sql, [SQL parameters])
- 레코드를 하나의 Map으로 전환할 때 사용
- Map의 key에 컬럼 이름이 저장됨
(6) queryForList
- 형식 : List<Map<String, Object>> queryForList(String sql, [SQL parameters])
- 여러 개의 레코드를 여러 개의 Map으로 전환할 때 사용
- List를 통해 여러 Map을 저장할 수 있음
- 결과 레코드의 개수에 따른 예외는 발생하지 않음
6) SQL 배치 메소드
- update()로 실행하는 SQL문들을 일괄적으로 실행함으로써 DB 호출을 최소화하여 성능을 향상시킬 때 사용
- batchUpdate() 메소드를 사용하며, 내부적으로는 JDBC Statement의 addBatch()와 executeBatch() 메소드를 이용함
- 동일한 SQL을 파라미터만 바꿔가면서 실행하는 원리이기 때문에, 배치 메소드는 하나의 SQL과 SQL 파라미터 배열을 파라미터로 가짐
- 각 SQL을 실행했을 때 영향받은 레코드의 개수를 배열로 반환함
(1) Map 배열 사용
- 형식 : int[] batchUpdate(String sql, Map<String, ?>[] batchValues)
- 각 SQL에 대한 파라미터 정보를 Map 배열로 만듬
(2) SqlParameterSource 배열 사용
- 형식 : int[] batchUpdate(String sql, SqlParameterSource[] batchArgs)
- 각 SQL에 대한 파라미터 정보를 SqlParameterSource 타입 오브젝트로 만듬. SqlParameterSource 타입이기만 하면 섞어서도 사용 가능함
(3) List 사용
- 형식 : int[] batchUpdate(String sql, List<Object[]> batchArgs)
- 위치 치환자를 사용할 때 args로 전달 했던 SQL parameter들을 Object 배열에 넣고 이들을 List로 만듬
3. SimpleJdbcInsert
1) SimpleJdbcInsert란?
- 비슷한 구조의 SQL을 반복적으로 만들어야 하고, SQL이 일반 텍스트로 되어있기 때문에 빌드 시점에서 검증할 수 없고 실수가 나기 쉽다는 문제를 극복하기 위해 제공되는 클래스다.
- DB의 메타 정보를 활용하여 INSERT문의 작성을 간편하게 만들어준다.
2) SimpleJdbcInsert의 생성
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource);
- SimpleJdbcInsert는 테이블별로 생성하는 클래스이기 때문에 오브젝트를 생성하고 난 후에 어떤 테이블을 적용할지 초기화해야 하며, 하나의 DAO에서 여러 개의 SimpleJdbcInsert를 사용할 수 있다.
- 초기화 메소드는 모두 SimpleJdbcInsert 자신을 반환하기 때문에 체인 형식으로 코드를 작성할 수 있다. 이제 초기화 메소드로는 어떤 것들이 있는지 살펴보자.
(1) withTableName
- 형식 : SimpleJdbcInsert withTableName(String tableName)
- 테이블 이름을 지정하여, 내부적으로 지정된 테이블 이름을 참고해서 DB의 테이블 메타 정보를 활용하도록 하는 메소드이다.
SimpleJdbcInsert simpleJdbcInsert = new SimpleJdbcInsert(dataSource).
withTableName("user");
(2) withSchemaName
- 형식 : SimpleJdbcInsert withSchemaName(String schemaName)
- 스키마의 이름을 지정한다.
(3) withCatalogName
- 형식 : SimpleJdbcInsert withCatalogName(String catalogName)
- 카탈로그의 이름을 지정한다.
(4) usingColumns
- 형식 : SimpleJdbcInsert usingColumns(String... columnNames)
- 일부 컬럼들만 사용해서 INSERT문을 작성할 때 사용된다.
- 컬럼들의 이름을 파라미터에 String 타입의 가변 인자를 이용하여 작성한다.
(5) usingGeneratedKeyColumns
- 형식 : SimpleJdbcInsert usingGeneratedKeyColumns(String... columnNames)
- DB에 의해 자동으로 생성되는 key 컬럼을 지정하여, 지정된 컬럼을 INSERT문에서 제외시킬 때 사용한다.
(6) withoutTableColumnMetaDataAccess
- 형식 : SimpleJdbcInsertOperations withoutTableColumnMetaDataAccess()
- DB에서 테이블 메타데이터를 가져오지 않도록 한다.
- DB의 메타정보를 이용하지 않고 파라미터로 제공된 정보를 활용하고 싶을 때 사용한다.
3) SQL 실행 메소드
(1) execute
- 형식 : int execute(Map<String, Object> map) 또는 int execute(SqlParameterSource sqlParameterSource)
- SQL 파라미터 정보를 파라미터로 받아서 INSERT 작업을 수행한다.
(2) executeAndReturnKey
- 형식 : Number executeAndReturnKey(Map<String, Object> map) 또는 Number executeAndReturnKey(SqlParameterSource sqlParameterSource)
- execute() 작업을 수행하고 자동생성된 key값을 java.lang.Number 타입으로 반환해준다.
※ 자동생성 key 설정의 대표적인 예로, MySQL의 AUTO_INCREMENT 옵션이 있다.
- executeAndReturnKey() 메소드를 이용하려면 usingGeneratedKeyColumns() 메소드로 자동생성 key 컬럼을 지정해줘야 한다.
- 자동생성되는 id 컬럼에 대한 파라미터 정보는 생략하면 된다.
(3) executeAndReturnKeyHolder
- 형식 : KeyHolder executeAndReturnKeyHolder(Map<String, Object> map) 또는 KeyHolder executeAndReturnKeyHolder(SqlParameterSource sqlParameterSource)
- execute() 작업을 수행하고 2개 이상의 자동생성 key값을 반환받기 위해 사용된다.
- KeyHolder 클래스의 getKeyList() 메소드는 자동생성 key의 <컬럼명,값> 쌍들을 List<Map<String, Object>> 타입으로 반환해준다.
4. SimpleJdbcCall
1) SimpleJdbcCall 이란?
- DB에 저장된 procedure이나 function을 호출할 때 사용한다.
2) SimpleJdbcCall의 생성
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource);
- 기본적으로 실행할 procedure 또는 function 중 하나로 초기화해야 한다.
- 초기화 메소드는 모두 SimpleJdbcCall 자신을 반환하기 때문에 체인 형식으로 코드를 작성할 수 있다. 이제 초기화 메소드로는 어떤 것들이 있는지 살펴보자.
(1) withProcedureName
- 형식 : SimpleJdbcCallOperations withProcedureName(String procedureName)
- 실행할 procedure의 이름을 지정한다.
(2) withFunctionName
- 형식 : SimpleJdbcCallOperations withFunctionName(String functionName)
- 실행할 function의 이름을 지정한다.
(3) returningResultSet
- 형식 : SimpleJdbcCallOperations returningResultSet(String parameterName, ParameterizedRowMapper rowMapper)
- Procecure가 ResultSet을 반환하는 경우에 사용
3) 실행 메소드
(1) executeFunction
- 형식 : <T> T executeFunction(Class<T> returnType, [SQL parameters])
- Function을 실행할 때 사용
(2) executeObject
- 형식 : <T> T executeObject(Class<T> returnType, [SQL parameters])
- 출력 파라미터가 하나인 Procedure에 사용
(3) execute
- 형식 : Map<String, Object> execute([SQL parameters])
- 출력 파라미터가 하나 이상인 Procedure에 사용
5. JdbcDaoSupport
- JDBC를 이용하여 DAO를 만들 때 도움이 되는 추상 클래스로, DataSource의 주입과 JdbcTemplate의 생성과 같은 공통 기능을 담은 클래스다.
예시
public abstract class AbstractSimpleJdbcTemplate extends JdbcDaoSupport {
protected SimpleJdbcTemplate simpleJdbcTemplate;
protected void initTemplateConfig() {
this.simpleJdbcTemplate=new SimpleJdbcTemplate(getDataSource());
initJdbcObjects();
}
protected void initJdbcObjects() {} // 상속 받는 클래스에서 사용하는 초기화 메소드
}
● 참고 자료 : 토비의 스프링 3.1 Vol.2
'Spring Series > Spring Framework' 카테고리의 다른 글
[Spring] Hibernate (0) | 2022.07.06 |
---|---|
[Spring] Spring이 제공하는 JPA (0) | 2022.07.02 |
[Spring] .gitignore 적용하기 (0) | 2022.06.29 |
[Spring] Bean의 id와 name (0) | 2022.06.23 |
[Spring] Scope Proxy (0) | 2022.06.23 |
댓글