개발자가 "코드 복사하기" 를 했다면, "코드를 더 줄일 수는 없을까?" 라는 고민을 하게 된다.
그래서 만든 패턴을 "Template 패턴" 이라고 한다.
Template 패턴이란 ?
유사하거나 반복되는 알고리즘을 캡슐화 하여 재사용하는 패턴을 "Template 패턴" 이라고 함.
코딩 순서가 정해져 있는(정형화된) 기술에서 특히 유용하게 활용됨 (JDBC, 트랜잭션, Mybatis, JPA, ...)
그래서 오늘은 JDBCTemplate 클래스를 적용하여 DAO를 구성하는 방법에 대해 알아보려고 한다.
JDBCTemplate을 적용하기 위해선 먼저 .jar 파일이 2개 필요하다. 따라서 pom.xml 파일에 아래의 코드를 추가해야 한다.
pom.xml
<!-- Spring JDBC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- DBCP -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
추가가 된 것을 확인하려면 Maven Dependencies에 추가가 되어 있는지를 확인하면 된다.
* DBCP란 ?
DBCP (DB 커넥션 풀) == 커넥션(Connection)들을 대신 관리해주는 주체(객체)를 말함.
DB연결은 Connection을 DB로부터 확보하는 것에서부터 시작하는데 JDBCTemplate 클래스가 DataSource 객체를 통해 Connection을 확보 및 관리한다.
따라서, DataSource 객체를 생성해야한다. 즉, 스프링 컨테이너가 생성하도록 해야한다. [ <bean> 태그를 사용할 예정 ]
또한 DataSource 객체를 <bean> 등록 시에 Setter 의존 주입을 함께 해야한다. 객체화하는 방법은 아래와 같다.
1. DataSource 객체를 통해 JDBCTemplate 클래스가 Connection을 확보할 것이므로 삭제한다.
2. JBDCTemplate 객체를 선언한다.
3-1. boolean 타입의 반환값을 가진 CUD의 경우 (insert를 예시로 사용)
- 변경 전 코드
- 변경 후 코드
update() 메소드를 사용하고 첫번째 인자로는 들어가야할 쿼리문(insert, update, delete)를 작성하고, 그 뒤의 인자로는 쿼리문을 실행할 때 필요한 값들을 작성한다.
jdbcTemplate.update(SQL, [필요한 값1], [필요한 값2], ...);
3-2. 반환 값으로 객체를 가진 R의 경우 (selectOne, selectAll)
- selectOne의 경우 queryForObject()를 사용하는데, queryForObject()는 update()와 달리 메소드 시그니처가 정해져 있다.
- SQL을 실행할 때 필요한 INPUT과, SQL이 실행완료한 후의 OUTPUT을 지정해야한다.
- 이 때 INPUT은 배열의 형태로, OUTPUT은 객체의 형태로 지정한다.
queryForObject() 사용법은 아래와 같다.
jdbcTemplate.queryForObject(SQL, INPUT, OUTPUT);
또한, INPUT 타입에는 무엇이 들어갈지 모르기 때문에 최상위 클래스인 Object 배열로 선언한다. OUTPUT에는 RowMapper라는 클래스가 들어가야 한다. 즉, 클래스를 따로 선언해야한다.
여기서 RowMapper 란?
어떤 ResultSet(DB)을 어떤 자바객체(POJO)와 매핑해야 하는지 강제해주는 역할은 하는 인터페이스를 말한다.
RowMapper 에서는 어떤 VO인지를 알아야 하므로 '<> 제네릭' 설정이 필수이다.
현재 DB와 매핑되는 자바객체는 "BoardVO"이기 때문에 제네릭 자리에 "BoardVO"가 들어가야 한다.
즉 새로운 클래스의 코드는 아래와 같다.
class BoardRowMapper implements RowMapper<BoardVO> {
@Override
public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
BoardVO data = new BoardVO();
data.setBid(rs.getInt("BID"));
data.setCnt(rs.getInt("CNT"));
data.setContent(rs.getString("CONTENT"));
data.setTitle(rs.getString("TITLE"));
data.setWriter(rs.getString("WRITER"));
return data;
}
}
SelectOne() 메소드의 코드는 아래와 같이 작성한다.
public BoardVO selectOne(BoardVO bVO) {
System.out.println("BoardDAO2 로그 selectOne() 메서드");
Object[] args = { bVO.getBid() };
return jdbcTemplate.queryForObject(selectOne, args, new BoardRowMapper());
}
INPUT에 들어가야할 값은 Controller에서 받아오는 값을 작성하면 된다. (현재는 글 번호이므로 bVO.getBid()로 작성)
또한, OUTPUT에는 방금 위에서 작성한 클래스를 new로 객체화하여 작성한다.
- selectAll의 경우 query() 메소드를 사용하는데, INPUT값이 필요하지 않다면 query() 메소드에서는 생략 가능하다.
jdbcTemplate.querty(SQL, new BoardMapper());
selectAll()의 메소드는 아래와 같이 작성한다.
public List<BoardVO> selectAll(BoardVO bVO) {
System.out.println("BoardDAO2 로그 selectAll() 메서드");
return jdbcTemplate.query(selectAll, new BoardRowMapper());
}
마지막으로 JdbcTemplate이 주체가 되므로 DI(의존주입)이 필요하다.
@Autowired
private JdbcTemplate jdbcTemplate;
근데 여기서 DI를 해줄 객체가 없으므로 applicationContext.xml(루트 컨테이너)에 등록해야한다. 따라서 DataSource를 Setter 형식으로 주입한다.
applicationContext.xml
<!-- JdbcTemplate에 DI할 객체 생성 -->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
진짜 마지막으로 ServiceImpl.java에서 변경된 DAO로 멤버변수를 변경하고, 사용할 DAO 클래스에 @Repository 의존 주입할 객체를 생성하면 끝 !
BoardServiceImpl.java
@Autowired
private BoardDAO boardDAO;
↓ ↓ ↓
@Autowired
private BoardDAO2 boardDAO;
BoardDAO2.java
@Repository("boardDAO")
public class BoardDAO
↓ ↓ ↓
@Repository("boardDAO")
public class BoardDAO2
'Spring' 카테고리의 다른 글
[Spring] MultipartFile을 활용한 파일 업로드 (0) | 2023.08.17 |
---|---|
[Spring] NULL 업데이트 이슈 (@SessionAttributes, @ModelAttribute) (0) | 2023.08.16 |
[Spring] AOP (AfterReturning & AfterThrowing) (0) | 2023.08.14 |
[Spring] AOP (Aspect Oriented Programming) (0) | 2023.08.11 |
[Spring] 2 - Layerd 아키텍처 스타일 (0) | 2023.08.11 |