2.2 JDBC
- 자바의 데이터 액세스 기술의 기본이 되는 로우 레벨의 API
- JDBC는 표준 인터페이스를 제공하고 각 DB 벤더와 개발팀에서 이 인터페이스를 구현한 드라이버를 제공하는 방식으로 사용
- JDBC는 모든 자바의 데이터 액세스 기술의 근간
2.2.1 스프링 JDBC 기술과 동작원리
- 스프링 3.0에서는 다섯 가지 종류의 접근 방법을 제공
- JdbcTemplate는 그중에서 가장 오래된 기초적인 접근 방법
스프링의 JDBC 접근 방법
- 접근 방법 중에서 가장 사용하기 편하고 자주 이용되는 것은 다음 두가지
SimpleJdbcTemplate
- jdbcTemplate과 NameParameterJdbcTemplate에서 가장 많이 사용되는 기능을 통합하고 자바 5 이상의 장점을 최대한 활용할 수 있게 만든 방식
- 방대한 템플릿 메소드와 내장된 콜백을 제공
- JDBC의 모든 기능을 최대한 활용할 수 있는 유연성을 지님
SimpleJdbcInsert, SimpleJdbcCall
- 이 두가지 접근 방법은 DB가 제공해주는 메타정보를 활용해서 최소한의 코드만으로 단순한 JDBC 코드를 작성 가능
- 메타정보에서 컬럼 정보와 파라미터 정보를 가져와서 삽입용 SQL과 프로시저 호출 작업에 사용해주기 때문에 매우 편리
스프링 JDBC가 해주는 작업
Connection 열기와 닫기
- 스프링 JDBC를 사용하면 코드에서 직접 Connection을 열고 닫는 작업이 필요 없음
- Connection과 관련된 모든 작업은 스프링 JDBC가 필요한 시점에서 알아서 진행
- Connectio을 열고 닫는 시점은 스프링 트랜잭션 기능과 맞물려서 결졍
- 같은 코드와 DAO라고 해도 트랜잭션 경계를 어떻게 선언해뒀고 어떤 순서로 호출하느냐에 따라서 매번 Connection을 열고 닫는 위치가 달라짐
Statement 준비와 닫기
- SQL 정보가 담긴 Statement 또는 PreparedStatement를 생성하고 필요한 준비 작업을 해주는 것도 대부분 스프링 JDBC의 몫
- 준비된 Statement는 콜백 오브젝트가 전달받아서 사용
- 콜백을 직접 만들기보다는 스프링 JDBC가 미리 만들어 놓은 것을 사용하는 경우가 많음 그래서 직접 Statement를 사용하는 코드는 많지 않음
- Statement도 Connection과 마찬가지로 사용이 끝나고 스프링 JDBC가 알아서 반환
Statement 실행
- SQL이 담긴 Statement를 실행하는 것도 스프링 JDBC의 몫
ResultSet 루프
ResultSet
에 담긴 쿼리 실행 결과가 한 건 이상이라면 루프를 돌면서 각각의 로우들에 대해 처리가 필요
ResultSet
의 루프를 만들어 반복해주는 것도 스프링 JDBC가 해주는 작업
ResultSet
각 로우의 내용을 어떻게 오브젝트에 담을 것인지는 루프 안에서 실행되는 콜백으로 만들어 템플릿에 제공해주면 됌
- 어떤 경우 각 로우의 매핑을 위해 콜백을 직접 작성하기 보다는 스프링 JDBC가 미리 정해둔 포맷의 오브젝트나 컬렉션으로 전환하고 이를 전달받는 것이 편리
트랜잭션 처리
- 스프링 JDBC는 트랜잭션 동기화 기법을 이용해 선언적 트랜잭션 기능과 맞물려 실행
- 트랜잭션을 시작한 후에 스프링 JDBC의 작업을 요청하면 진행 중인 트랜잭션에 참여
- 트랜잭션이 없는 채로 호출된 경우 새로운 트랜잭션을 만들어서 사용
- 스프링 JDBC가 대부분 작업을 해주기 떄문에 데이터 액세스 로직만 정의해주면 됌
- 많은 경우 실행할 SQL과 바인딩할 파라미터를 넘겨주거나 쿼리의 실행 결과를 어떤 오브젝트에 어떻게 넣어서 받을지 지정해주는 역할만 해주면 됌
- SimpleSqlInsert를 사용하면 SQL도 생략 가능
- 스프링 개발자가 해야하는 한가지는 DB 커넥션을 가져올 DataSource를 정의해주는 것
2.2.2 SimpleJdbcTemplate
- 스프링 JDBC를 사용한다면 가장 많이 이용하게 될 JDBC용 템플릿
- SimpleJdbcTemplate이 제공하는 기능은 실행, 조회, 배치의 세가지 작업으로 구분
- 실행 작업은 INSERT, UPDATE와 같이 DB의 데이터 변경이 일어나는 작업
- 조회는 SELECT를 이용해 결과를 가져오는 작업
- 배치는 하나 이상의 실행 작업을 한 번에 수행해줘야하는 작업
SimpleJdbcTemplate 생성
SimpleJdbcTemplate
은DataSource
를 파라미터로 해서 다음과 같이 생성
SimpleJdbcTemplate template = new SimpleJdbcTemplate(dataSource);
DataSource
는 보통 빈으로 등록하므로 필요한 DAO에서DataSource
빈을 DI 받아서SimpleJdbcTemplate
를 생성해두고 사용
SimpleJdbcTemplate
은 멀티스레드 환경에서도 안전하게 공유해서 쓸 수 있기 때문에 DAO의 인스턴스 변수에 저장해두고 사용 가능
SimpleJdbcTemplate
자체를 싱글톤 빈으로 등록하고 모든 DAO에 공유해서 사용도 가능
- 스프링 개발자는 관례적으로 DAO의 코드에서
DataSource
를 받아SimpleJdbcTemplate
를 생성하는 방식을 선호- DataSource가 있으면 다른 스프링 JDBC 오브젝트를 만들어 사용이 가능하기 떄문
public class MemberDao { SimpleJdbcTemplate simpleJdbcTemplate; public void setDataSource(DataSource dataSource) { this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); } }
- 애노테이션 방식을 이용한다면 @Autowired나 @Resource를 붙이거나 메소드에 의한 주입 방법이나 생성자 주입을 이용해서도 사용 가능
@Autowired public void init(DataSource dataSource) { this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource); }
SQL 파라미터
SimpleJdbcTemplate
에 작업을 요청할 때는 문자열로 된 SQL 제공이 필요
- SQL에 매번 달라지는 값이 있는 경우에는 ‘?’와 같은 치환자를 넣어 파라미터 바인딩 방법을 사용하는 것이 편리
- 스프링 JDBC는 JDBC에서 제공하는 위치를 이용한 치환자인 ‘?’뿐 아니라 명시적으로 이름을 지정하는 이름 치환자도 지원
- 위치 치환자를 사용하는 경우에는 파라미터의 개수가 많으면 실수하기 쉬움
- 또한 치환자의 순서를 바꾸거나 추가, 삭제하는 작업도 어려움
- SQL을 사용하는 예시
//실행할 SQL
INSERT INTO MEMBER(ID, NAME, POINT) VALUES(1, 'Spring', 2.3);
//위치 치환자
INSERT INTO MEMBER(ID, NAME, POINT) VALUES(?, ?, ?);
//이름 치환자
INSERT INTO MEMBER(ID, NAME, POINT) VALUES(:id, :name, :point);
- 이름 치환자의 또 다른 장점은 맵이나 오브젝트에 담긴 내용을 키 값이나 프로퍼티 이름을 이용해 바인딩 할 수있다는 점
Map / MapSqlParameterSource
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", 1);
map.put("name", "Spring");
map.put("point", 3.5);
- map은 이름 치환자를 가진 SQL과 함께 SimpleJdbcTemplate에 전달돼서 바인딩 파라미터로 사용 가능
- 맵의 키 값과 일치하는 치환자에 맵의 값이 자동으로 삽입
- 코드를 이용해 맵에 정보를 직접 넣어야 한다면 Map 대신 스프링 JDBC의 MapSourceParameterSource를 사용하는 것이 편리
MapSqlParameterSource params = new MapSqlParameterSource()
.addValue("id", 1)
.addValue("name", "Spring")
.addValue("point", 3.5);
BeanPropertySqlParameterSource
- Map 대신 도메인 오브젝트나 DTO를 사용하게 해줌
- 오브젝트의 프로퍼티 이름과 SQL의 이름 치환자를 매핑해 파라미터 값을 넣어주는 방식
- 도메인 오브젝트의 파라미터와 SQL 치환자의 이름만 같게 만들면 매우 편리
public class Member {
int id;
String name;
double point;
public int getId() {return this.id;}
...
}
Member member = new Member(1, "Spring", 3.5);
BeanPropertySqlParameterSource params = new BeanPropertySqlParameterSource(member);
SQL 실행 메소드
- INSERT, UPDATE, DELETE와 같은 SQL을 실행할 때는 SimpleJdbcTemplate의 update()를 사용
- update()를 호출할 때는 SQL과 함께 바인딩할 파라미터를 다음 세가지 방식 중 하나로 전달
varargs
- 위치 치환자를 사용하는 경우 바인딩할 파라미터 순서대로 전달
simpleJdbcTemplate.update(
"INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES(?,?,?)", 1, "Spring", 1.5);
- 파라미터는 가변인자이므로 필요 없다면 생략 가능
simpleJdbcTemplate.update("delete from member");
Map
- 이름 치환자를 사용한다면 파라미터를 Map으로 전달
simpleJdbcTemplate.update(
"INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES(:id, :name, :point)", map);
SqlParameterSource
- 도메인 오브젝트나 DTO를 이름 치환자에 직접 바인당하려면 SqlParameterSource 타입인 BeanPropertySqlParameterSource를 사용해 update()를 호출
simpleJdbcTemplate.update(
"INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES(:id, :name, :point)",
new BeanPropertySqlParameterSource(member));
- 파라미터를 직접 지정하려면 MapSqlParameterSource의 addValue()를 이용
simpleJdbcTemplate.update(
"INSERT INTO MEMBER(ID, NAME, POINT, args) VALUES(:id, :name, :point)",
new MapSqlParameterSource()
.addValue("id", 1).addValue("name", "Spring").addValue("point", 3.5"));
- SimpleJdbcTemplate의 update()는 SQL 실행으로 영향을 받은 레코드의 개수를 리턴
SQL 조회 메소드
- SELECT를 이용하는 조회용 메소드는 단순 값이나 오브젝트를 가져오는 경우와 리스트를 가져오는 경우로 구분
int queryForInt(String sql, [SQL 파라미터])
- 하나의 int 타입 값을 조회할 때 사용
- [SQL 파라미터]에 사용할 수 있는 방법들
- 위치 치환자에 사용하는 Object 타입 가변인자(
Object... args
)
- 이름 치환자에 사용할 수 있는 맵(
Map<String, ?> args
)
- SQL 파라미터 소스(
SqlParameterSource args
)
- 위치 치환자에 사용하는 Object 타입 가변인자(
- 파라미터가 없는 단순한 쿼리라면 다음과 같이 SQL 파라미터 생략이 가능
int count = simpleJdbcTemplate.queryForInt("select count(*) from member");
simpleJdbcTemplate.queryForInt("select count(*) from member point > :min",
new MapSqlParameterSource("min", min));
queryForInt()
의 실행 결과는 한 개의 숫자 컬럼만 가져야하며, 아닌 경우 예외 발생
long queryForLong(String sql, [SQL 파라미터])
- 하나의 long 타입 값을 조회할 때 사용
- 리턴 타입만 다를 뿐 사용 방법은
queryForInt()
와 동일
<T> T queryForObject(String sql, Class<T> requiredType, [SQL 파라미터])
- 쿼리를 실행해서 하나의 값을 가져올 떄 사용, 결과 타입을 직접 지정 가능
- SQL 실행 결과는 하나의 컬럼을 가진 하나의 로우, 아닌 경우 예외 발생
String name = simpleJdbcTemplate.queryForObject(
"select name from member where id = ?", String.class, id);
- 검색된 로우가 없다면 EmptyResultDataAccessException 예외가 발생
- 쿼리의 결과가 없어도 예외가 던져지길 원하지 않는다면 DAO에서 직접 처리 필요
try {
return simpleJdbcTemplate.queryForObject(
"select name from member where id = ?", String.class, id);
} catch(EmptyResultDataAccessException) {
return null;
}
<T> T queryForObject(String sql, RowMapper<T> rm, [SQL 파라미터])
- SQL 실행 결과, 하나의 로우가 돌아오는 경우에 사용
- 단일 컬럼이 아니라 다중 컬럼을 가진 쿼리에 사용할 수 있다는 장점
- 다중 컬럼을 가진 결과이므로 도메인 오브젝트나 DTO처럼 여러 개의 프로퍼티 오브젝트로 전환 필요
- 테이블의 컬럼 이름과 매핑될 오브젝트의 프로퍼티 이름이 일치한다면 RowMapper를 직접 구현하는 대신 BeanPropertyRowMapper<T>를 사용하는 것이 편리
- 생성자에 매핑할 클래스를 넣어서 오브젝트를 생성하면 주어진 클래스의 프로퍼티 이름과 SQL 결과를 자동 매핑해주는 RowMapper 콜백 오브젝트로 사용
Member m = simpleJdbcTemplate.queryForObject( "select * from member where id=?" , new BeanPropertyRowMapper<Member>(Meber.class) , id)
- queryForObject()는 단일 조회, 결과가 없거나 여러 개의 로우로 돌아면 예외 발생
<T> List<T> query(String sql, RowMapper<T> rm, [SQL 파라미터])
- SQL 실행 결과로 여러 개의 컬럼을 가진 로우를 RowMapper 콜백을 이용해 도메인 오브젝트나 DTO에 매핑
- query()는 여러 개의 로우를 처리 가능, 리스트의 각 요소가 하나의 로우에 해당
- 로우의 개수에 제한은 없음 로우의 개수가 0이어도 예외가 발생 안함
List<Member> members = simpleJdbcTemplate.query(
"select * from member where point > ?",
new BeanPropertyRowMapper<Member>(Member.class),
point);
Map<String, Object> queryForMap(String sql, [SQL 파라미터])
- queryForObject() 처럼 단일 로우의 결과를 처리하는데 사용
- RowMapper를 이용해 도메인, DTO에 매핑하는 대신 맵에 로우 내용을 저장해서 리턴
- 저장할 오브젝트를 매번 정의하기 번거롭거나 맵의 사용이 편리한 환경에서 유용
- 맵의 키에는 컬럼 이름이 들어가고 값에는 컬럼의 값이 저장
Map<String, Object> map = simpleJdbcTemplate.queryForMap(
"select * from member where id = ?", id);
- SQL 실행 결과는 하나의 로우를 돌려주는 것이 보장 필요, 그 외엔 오류 발생
- 조회 결과를 서비스 계층의 비즈니스 로직에 활용할 경우 컬럼 값을 매번 적절한 타입으로 캐스팅해줘야 하는 번거로움 발생
List<Map<String, Object>> queryForList(String sql, [SQL 파라미터])
queryForMap()
의 다중 로우 버전, 로우의 내용을 Map에 넣고 리스트로 만들어 리턴
SQL 배치 메소드
- update()로 실행하는 SQL들을 배치 모드로 실행하게 해줌
- 내부적으로
JDBC Statement
의addBatch()
와executeBatch()
를 이용해 여러 개의 SQL을 한번에 처리
- 많은 SQL을 실행해야 하는 경우 배치 방식을 사용하면 DB 호출을 최소화하여 성능 향상
- 모든 배치 메소드는 하나의 SQL과 SQL 파라미터 배열을 파라미터로 가짐
int[] batchUpdate(String sql, Map<String, ?>[] batchValues)
- 이름 치환자를 가진 SQL과 파라미터 정보가 담긴 맵의 배열을 이용
- 배열의 개수만큼 SQL을 실행하며, 리턴 값은 영향받은 로우의 개수를 담은 배열
int[] batchUpdate(String sql, SqlParameterSource[] batchArgs)
- 맵 대신 SqlParameterSource 타입 오브젝트의 배열로 파라미터를 제공
- MapSqlParameterSource와 BeanPropertyParameterSource는 모두 SqlParameterSource 타입이므로 함꼐 사용 가능
dao.simpleJdbcTemplate.batchUpdate("update member set name = :name where id = :id",
new SqlParameterSource[] {
new MapSqlParameterSource().addValue("id", 1).addValue("name", "Spring3"),
new BeanPropertySqlParameterSource(new Member(2, "Book3"))
}
};
int[] batchUpdate(String sql, List<Object[]> batchARgs)
- 위치 치환자를 사용할 때 varags로 전달했던 파라미터를 Object 배열에 넣고 이를 List로 만들어서 전달 가능
JdbcTempate
에서 제공하는 좀 더 세밀한 템플릿/콜백 방식을 이용하고 싶다면getJdbcOperations()
를 이용, JdbcTemplate이 제공하는 메소드를 직접 이용 가능
2.2.3 SimpleJdbcInsert
- SQL을 이용하는 프로그래밍의 귀찮은 일은 비슷한 구조의 SQL을 반복적으로 만드는 점
- 특히 모든 컬럼을 다 적어야하는 INSERT 문의 작성은 가장 귀찮은 일
- SQL은 런타임 시에 DB에서 처리되기 떄문에 빌드 시점에서 미리 검증X (쿼리 오타 빈번)
- DB 메타정보를 활용해서 INSERT 문의 작성을 간편하게 만들어주는 것이
SimpleJdbcInsert
SimpleJdbcInsert 생성
- SimpleJdbcInsert는 테이블별로 만들어서 사용
- 하나의 DAO에서 여러 개의 SimpleJdbcInsert를 사용 가능
- SimpleJdbcTemplate은 멀티스레드 환경에서 안전하게 공유해서 사용 가능
- 생성할 떄는 DataSource가 필요
SimpleJdbcInsert jdbcInsert = new SimpleJdbcInsert(dataSource);
SimpleJdbcInsert withTableName(String tableName)
- 테이블 이름을 지정, DB로부터 테이블 메타정보를 읽어서 INSERT 문에 활용
SimpleJdbcInsert jdbcInsert =
new SimpleJdbcInsert(dataSource).withTableName("member");
SimpleJdbcInsert withSchemaName(String schemaName), SimpleJdbcInsert withCatalogName(String catalogName)
- 스키마와 카탈로그 이름을 지정해야 할 경우 사용
SimpleJdbcInsert usingColumns(String... columnNames)
- SimpleJdbcInsert는 기본적으로 테이블의 모든 컬럼을 사용해서 INSERT문을 작성
- usingColumns()를 사용하면 일부 컬럼만 사용해서 INSERT문을 작성
SimpleJdbcInsert usingGeneratedKeyColumns(String... columnNames)
- DB에 의해 자동으로 생성되는 키 컬럼을 지정 가능
- 지정된 컬럼은 INSERT 문장에서 제외, 자동 키 값은 INSERT 실행 후 가져오는 것 가능
SimpleJdbcInsertOperations withoutTableColumnMetaDataAccess()
- DB에서 테이블 메타 정보를 가져오지 않도록 만듬
- 일부 DB는 메타정보를 제공해주지 않는데 이때 사용
- DB의 메타정보 대신 파라미터로 제공된 정보를 활용해서 INSERT 문을 작성
SimpleJdbcInsert 실행
int execute([이름 치환자 SQL 파라미터])
- SimpleJdbcInsert가 내부적으로 생성하는 SQL은 이름 치환자를 가진 INSERT 문이라고 생각하면 됌
- 치환자를 넣을 SQL 파라미터 지정이 필요
- 파라미터 지정 방법은 맵(
Map<String, Object>
)과SqlParameterSource
타입 오브젝트 두 가지 종류 이용
SimpleJdbcInsert insert = new SimpleJdbcInsert(dataSource).withTableName("member");
Member member = new Member(1, "Spring", 3.5);
insert.execute(new BeanPropertySqlParameterSource(member));
- SimpleJdbcInsert 오브젝트는 테이블 설정이 같다면 재사용 가능, 새롭게 만들 필요 X
Number executeAndReturnKey([이름 치환자 SQL 파라미터])
- execute()와 동일한 작업을 수행하고, 자동생성된 키 값을 Number 타입으로 리턴
java.lang.Number
는 숫자 타입 클래스(Integer, 등)가 모두 상속하고 있는 상위 클래스
-- 테이블 생성
CREATE TABLE REGISTER (
ID INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(100) NOT NULL
)
-- INSERT문 (ID는 자동생성)
INSERT INTO REGISTER(NAME) VALUE('Spring')
SimpleJdbcInsert registerInsert = new SimpleJdbcInsert(dataSource)
.withTableName("register")
.usingGeneratedKeyColumns("id");
int ket = registerInsert.executeAndReturnKey(
new MapSqlParameterSource("name", "Spring")).intValue();
KeyHolder executeAndReturnKeyHolder([이름 치환자 SQL 파라미터])
- 하나 이상의 자동생성 키 컬럼을 갖는 테이블도 존재
Number
대신KeyHolder
타입으로 생성된 키 정보를 돌려주는executeAndReturnKeyHolder()
를 이용
usingGeneratedKeyColumns()
를 초기화할 때 하나 이상의 자동생성 키 컬럼을 넣음
KeyHolder
는 키 이름과 생성된 값이 담긴 List<Map<String, Object>> 타입으로 돌려주는getKeyList()
를 제공
2.2.4 SimpleJdbcCall
- DB에 생성해둔 저장 프로시저 또는 저장 펑션을 호출할 떄 사용
SimpleJdbcCall 생성
- dataSource를 이용해 생성
- 멀티스레드 환경에 안전하므로 인스턴스 변수에 저장해두고 공유해서 사용 가능
SimpleJdbcCallOperations withProcedureName(String procedureName)
- 실행할 프로시저 이름을 지정
SimpleJdbcCallOperations withFunctionName(String functionName)
- 실행할 펑션의 이름을 지정
SimpleJdbcCallOperations returningResultSet(String parameterName, parameterizedRowMapper rowMapper)
- 프로시저가 ResultSet을 돌려주는 경우에 이를 RowMapper를 이용해 매핑
- 하나 이상의 ResultSet이 돌아오는 경우라면 순차적으로 returningResultSet()을 지정
SimpleJdbcCall 실행
- DB의 메타정보를 이용해 필요한 파라미터 정보를 가져옴
- 프로시저나 펑션을 실행할 때는 이에 맞게 파라미터 값을 전달
<T> T executeFunction(Class<T> returnType, [SQL 파라미터])
- 저장 펑션을 실행해주는 메소드, 리턴 값의 타입과 SQL 파라미터를 전달해서 사용
create function find_name(in_id INT)
returns varchar(255)
begin
declare out_name varchar(255);
select name
into out_name
from member
where id = in_id;
return out_name;
end
SimpleJdbcCall call = new SimpleJdbcCall(dataSource).withFunctionName("find_name");
String ret = call.executeFunction(String.class, id);
<T> T executeObject(Class<T> returnType, [SQL 파라미터])
- 저장 프로시저를 호출할 때 사용
- 프로시저의 출력 파라미터가 하나일 때만 이용, 사용방법은
executeFunction()
과 동일
Map<String, Object> execute([SQL 파라미터])
- 하나 이상의 출력 파라미터를 가진 저장 프로시저를 호출할 때 사용
- 여러 개의 출력 파라미터 값이 맵의 형태로 리턴, 리턴 타입을 따로 지정할 필요 X
2.2.5 스프링 JDBC DAO
- 보통 DAO는 도메인 오브젝트 또는 DB 테이블 단위로 생성
JdbcTemplate
,SimpleJdbcTemplate
,SimpleJdbcInsert
등은 모두 멀티스레드 환경에서 안전하게 공유해서 사용 가능
- 각각을 싱글톤 빈으로 등록해두고 이를 DAO에서 DI 받아서 사용 가능, 하지만 스프링 개발자는
DataSource
타입의 빈을 DI 받은 뒤 DAO 코드에서 템플릿 오브젝트 등을 생성해서 자주 사용
- SimpleJdbcInsert와 SimpleJdbcCall은 테이블이나 저장 프로시저 단위로 오브젝트를 만들어야 하기 떄문에 DAO마다 다른 SimpleJdbcInsert가 사용되는 경향
- 모든 JDBC 오브젝트는 한번 만들어지면 반복적으로 사용 가능하기 떄문에 초기에 만들어 인스턴스 변수에 저장해두는 것이 편리
public class MemberDao {
private SimpleJdbcTemplate simpleJdbcTemplate;
private SimpleJdbcInsert memberInsert;
private SimpleJdbcCall memberFindCall;
@Autowired
public void init(DataSource dataSource) {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
this.memberInsert = new SimpleJdbcInsert(dataSource).withTableName("member");
this.memberFindCall =
new SimpleJdbcCall(dataSource).withFunctionName("find_member");
}
- SimpleJdbcTemplate을 생성하는 코드의 중복을 제거하는 방법은 두가지
- SimpleJdbcTemplate를 독립적인 빈으로 등록하고 주입받는 방법
- DAO의 공통 코드를 뽑아내 추상 클래스를 만들어두고 모든 DAO가 이를 상속하는 방법
- 스프링은 JdbcDaoSupport라는 JDBC를 이용한 DAO 작성에 사용할 수 있는 추상클래스를 제공
- DataSource의 DI와 JdbcTemplate의 생성과 같은 공통 기능을 담은 클래스
- SimpleJdbcDaoSupport는 SimpleJdbcTemplate을 사용하는 DAO를 만들때 사용
- AbstractSimpleJdbcDaoSupport는 JdbcDaoSupport를 확장해서 SimpleJdbcTemplate 생성과 초기화를 담당하는 메소드를 자동으로 실행해주는 기능을 가진 추상 클래스
- initJdbcObjects()를 오버라이드해서 SimpleJdbcInsert 등을 생성하는 코드를 넣으면 됌
public abstract class AbstractSimpleJdbcDaoSupport extends JdbcDaoSupport {
protected SimpleJdbcTemplate simpleJdbcTemplate;
//JdbcDaoSupport에서 제공하는 초기화용 메소드, dataSource가 준비 된 후 호출
protected void initTemplateConfig() {
this.simpleJdbcTemplate = new SimpleJdbcTemplate(getDataSource());
initJdbcObjects();
}
//SimpleJdbcDaoSupport를 상속받는 DAO에서 SimpleJdbcInsert 등을 생성할 때
//오버라이드해서 사용할 수 있는 초기화 메소드
protected void initJdbcObjects() {}
}
'개발서적 > 토비 스프링 3.1-Vol.2' 카테고리의 다른 글
[토비의 스프링 - Vol.2] 2장 - 2.4 JPA (0) | 2022.01.17 |
---|---|
[토비의 스프링 - Vol.2] 2장 - 2.3 iBatis SqlMaps (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 2장 - 2.1 공통개념 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.5.5 프로퍼티 소스 (0) | 2022.01.17 |
[토비의 스프링 - Vol.2] 1장 - 1.5.4 런타임 환경 추상화와 프로파일 (0) | 2022.01.17 |