1. JDBC와 SPRING JDBC API
JDBC SPRING JDBC
DriverManager / DataSource DataSource
Statement / PreparedStatement / CallableStatement JdbcTemplate / SQLObject
ResultSet / RowSet POJO List OR Map/SQLRowSet
2. MyJdbcDaoSupport 개발
Spring 프레임워크는 모든 DAO 클래스에서 공통적으로 필요한 속성과 기능들(EX : connection 등)을 포함하고 있는 Base 클래스로
org.springframewrok.jdbc.core.support.JdbcDaoSupport를 지원한다. 이 클래스는 DataSource를 관리 할 뿐 아니라 JdbcTemplate 클래스에
접근 가능하도록 지원하기도 한다.
이 책에서는 예제 쿼리를 MessageSource를 이용하여 구현하고 있고, 따라서 모든 DAO클래스가 이 MessageSource에 모두 접근해야 하므로
이처럼 공통적으로 필요로 하는 속성들을 가지는 Base클래스를 위 클래스를 상속하여 만든다.
package net.javajigi.common.dao;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
public class MyJdbcDaoSupport extends JdbcDaoSupport {
private MessageSourceAccessor messageSourceAccessor;
private DataFieldMaxValueIncrementer incrementer;
public void setIncrementer(DataFieldMaxValueIncrementer incrementer) {
this.incrementer = incrementer;
}
protected final DataFieldMaxValueIncrementer getIncrementer() {
return incrementer;
}
public void setMessageSourceAccessor(
MessageSourceAccessor newMessageSourceAccessor) {
this.messageSourceAccessor = newMessageSourceAccessor;
}
protected final MessageSourceAccessor getMessageSourceAccessor() {
return messageSourceAccessor;
}
}
- 메시지소스 접근 위한 메소드와 시퀀스를 사용하기 위한 메소드를 추가 구현
3. BoardDAO, BoardFileDAO 클래스
- JdbcTemplate나 SQLObjects 둘 중 하나를 이용하여 개발.
1) JdbcTemplate 기반
- 모든 쿼리를 JdbcTempate 클래스가 가지고 있는 메소드를 이용하여 실행
public class SpringJDBCWithTemplateBoardDAO extends MyJdbcDaoSupport implements
BoardDAO {
public Board insert(Board board) throws DataAccessException {
String sql = getMessageSourceAccessor().getMessage("board.sql.insert");
int boardNo = getIncrementer().nextIntValue();
board.setBoardNo(boardNo);
Object[] values = { new Integer(boardNo), board.getTitle(),
board.getName(), board.getEmail(), board.getPassword(),
board.getCreateDate(), board.getContent() };
getJdbcTemplate().update(sql, values);
return board;
}
....
public Board findBoard(int boardNo) throws DataAccessException {
String sql = getMessageSourceAccessor().getMessage(
"board.sql.select.byboardno");
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rownum) throws SQLException {
Board board = new Board();
board.setTitle(rs.getString("title"));
board.setName(rs.getString("name"));
board.setEmail(rs.getString("email"));
board.setPassword(rs.getString("password"));
board.setCreateDate(rs.getString("createdate"));
board.setContent(rs.getString("content"));
board.setHitCount(rs.getInt("hitCount"));
return board;
}
};
Board board = (Board) getJdbcTemplate().queryForObject(sql,
new Object[] { new Integer(boardNo) }, rowMapper);
board.setBoardNo(boardNo);
return board;
}
....
public List findBoardList(int currentPage, int countPerPage)
throws DataAccessException {
int start = (currentPage - 1) * countPerPage;
String sql = getMessageSourceAccessor().getMessage(
"board.sql.select.list");
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rownum) throws SQLException {
Board board = new Board();
board.setBoardNo(rs.getInt("boardNo"));
board.setTitle(rs.getString("title"));
board.setName(rs.getString("name"));
board.setEmail(rs.getString("email"));
board.setCreateDate(rs.getString("createdate"));
board.setHitCount(rs.getInt("hitCount"));
return board;
}
};
List list = getJdbcTemplate().query(sql,
new Object[] { new Integer(start), new Integer(countPerPage) },
rowMapper);
return list;
}
}
2) Insert, Update, Delete
- int update(String sql)
- int update(String sql, Object[] args) -- 매핑 데이터
- int update(String sql, Object[] args, int[] argTypes)
3) Select
- int queryForInt(String sql)
- int queryForObject(String sql, Object[] args, RowMapper rowMapper) -- RowMapper innerClass에서 각 컬럼과 pojo를 매핑하여 놓고
이를 이 메소드에 넘겨서 board pojo클래스에 매핑한다. Object에는 게시판번호를 넘겨준다.
... 외 다수
- List queryForList(String sql, Object[] args, RowMapper rowMapper) -- object에 start row, countPerpage를 주면
그 만큼만 넘겨준다. 물론 rowMapper에 맞춰서
4) 콜백메소드 ?? (위 rowMapper 메소드..) - 그냥 뭔가 실행되고 호출되는 메소드
5) SQLObjects를 이용하여 구현
- 별도의 클래스를 이용하는 방법으로 하나의 쿼리를 실행하기 위하여 Spring JDBC에서 지원하는 클래스를 상속하는
새로운 클래스를 생성하는 방법 (지원하는 클래스 정보는 : org.springframework.jdbc.object)
/**
* 이 소스는 Spring 프레임워크 워크북에서 사용한 예제 소스입니다.
* 이 소스는 모든 개발자들이 자유롭게 수정 및 배포할 수 있습니다.
* 단, 이 소스를 기반으로 새로운 애플리케이션을 개발할 경우 출처를 명시해 주시면 됩니다.
*/
package net.javajigi.board.dao;
public class SpringJDBCWithMSBoardDAO extends MyJdbcDaoSupport implements
BoardDAO {
private Insert insert; // 내부 클래스
private Update update; // 내부 클래스
private Delete delete; // 내부 클래스
private UpdateHitCount updateHitCount; // 내부 클래스
private SelectByBoardNo selectByBoardNo; // 내부 클래스
private SelectList selectList; // 내부 클래스
protected void initDao() throws Exception {
super.initDao();
insert = new Insert(getDataSource());
update = new Update(getDataSource());
updateHitCount = new UpdateHitCount(getDataSource());
delete = new Delete(getDataSource());
selectByBoardNo = new SelectByBoardNo(getDataSource());
selectList = new SelectList(getDataSource());
} //내부 클래스 초기화
public int delete(int boardNo) throws DataAccessException {
return delete.update(boardNo);
}
public Board findBoard(int boardNo) throws DataAccessException {
Board board = (Board) selectByBoardNo.findObject(boardNo);
board.setBoardNo(boardNo);
return board;
}
public List findBoardList(int currentPage, int countPerPage)
throws DataAccessException {
int start = (currentPage - 1) * countPerPage;
return selectList.execute(start, countPerPage);
}
public int getTotalNo() throws DataAccessException {
String sql = getMessageSourceAccessor().getMessage(
"board.sql.totalcount");
return getJdbcTemplate().queryForInt(sql);
}
public Board insert(Board board) throws DataAccessException {
int boardNo = getIncrementer().nextIntValue();
board.setBoardNo(boardNo);
Object[] values = { new Integer(boardNo), board.getTitle(),
board.getName(), board.getEmail(), board.getPassword(),
board.getCreateDate(), board.getContent() };
insert.update(values);
return board;
}
public Board update(Board board) throws DataAccessException {
Object[] values = { board.getTitle(), board.getName(),
board.getEmail(), board.getContent(),
new Integer(board.getBoardNo()) };
update.update(values);
return board;
}
public int updateHitCount(int boardNo) throws DataAccessException {
return updateHitCount.update(boardNo);
}
protected class SelectList extends MappingSqlQuery { //리스트를 가져오는 쿼리는 MappingSqlQuery를 상속
protected SelectList(DataSource ds) { // 위에서 넘겨준 datasource
super(ds, getMessageSourceAccessor().getMessage(
"board.sql.select.list"));
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.INTEGER));
}
protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
Board board = new Board();
board.setBoardNo(rs.getInt("boardNo"));
board.setTitle(rs.getString("title"));
board.setName(rs.getString("name"));
board.setEmail(rs.getString("email"));
board.setCreateDate(rs.getString("createdate"));
board.setHitCount(rs.getInt("hitCount"));
return board;
}
}
protected class SelectByBoardNo extends MappingSqlQuery {
protected SelectByBoardNo(DataSource ds) {
super(ds, getMessageSourceAccessor().getMessage(
"board.sql.select.byboardno"));
declareParameter(new SqlParameter(Types.INTEGER));
}
protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
Board board = new Board();
board.setTitle(rs.getString("title"));
board.setName(rs.getString("name"));
board.setEmail(rs.getString("email"));
board.setPassword(rs.getString("password"));
board.setCreateDate(rs.getString("createdate"));
board.setContent(rs.getString("content"));
board.setHitCount(rs.getInt("hitCount"));
return board;
}
}
class Insert extends SqlUpdate { // 트랜잭션이 있는 쿼리 실행은 SqlUpdate를 상속 update(Object) 메서드는 parent것.
public Insert(DataSource dataSource) { //여기서는 생성자를 통해 쿼리와 타입만 재설정 해준다.
super(dataSource, getMessageSourceAccessor().getMessage(
"board.sql.insert"));
declareParameter(new SqlParameter(Types.INTEGER));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
}
}
class Update extends SqlUpdate { // 트랜잭션이 있는 쿼리 실행은 SqlUpdate를 상속 update(Object) 메서드는 parent것.
public Update(DataSource dataSource) { //여기서는 생성자를 통해 쿼리와 타입만 재설정 해준다.
super(dataSource, getMessageSourceAccessor().getMessage(
"board.sql.update"));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.VARCHAR));
declareParameter(new SqlParameter(Types.INTEGER));
}
}
class UpdateHitCount extends SqlUpdate { // 트랜잭션이 있는 쿼리 실행은 SqlUpdate를 상속 update(Object) 메서드는 parent것.
public UpdateHitCount(DataSource dataSource) { //여기서는 생성자를 통해 쿼리와 타입만 재설정 해준다.
super(dataSource, getMessageSourceAccessor().getMessage(
"board.sql.update.hitcount"));
declareParameter(new SqlParameter(Types.INTEGER));
}
}
class Delete extends SqlUpdate { // 트랜잭션이 있는 쿼리 실행은 SqlUpdate를 상속
public Delete(DataSource dataSource) {
super(dataSource, getMessageSourceAccessor().getMessage(
"board.sql.delete"));
declareParameter(new SqlParameter(Types.INTEGER));
}
}
}
//excute와 update 메소드는 상속되어진 메소드인듯함..
10.4.2. MappingSqlQuery
private class CustomerMappingQuery extends MappingSqlQuery {
public CustomerMappingQuery(DataSource ds) {
super(ds, "SELECT id, name FROM customer WHERE id = ?");
super.declareParameter(new SqlParameter("id", Types.INTEGER));
compile();
}
public Object mapRow(ResultSet rs, int rowNumber) throws SQLException {
Customer cust = new Customer();
cust.setId((Integer) rs.getObject("id"));
cust.setName(rs.getString("name"));
return cust;
}
}
//모든 파라미터가 compile 메소드를 호출해서 정의
public Customer getCustomer(Integer id) {
CustomerMappingQuery custQry = new CustomerMappingQuery(dataSource);
Object[] parms = new Object[1];
parms[0] = id;
List customers = custQry.execute(parms);
if (customers.size() > 0)
return (Customer) customers.get(0);
else
return null;
}
10.4.3. SqlUpdate
import java.sql.Types;
import javax.sql.DataSource;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;
public class UpdateCreditRating extends SqlUpdate {
public UpdateCreditRating(DataSource ds) {
setDataSource(ds);
setSql("update customer set credit_rating = ? where id = ?");
declareParameter(new SqlParameter(Types.NUMERIC));
declareParameter(new SqlParameter(Types.NUMERIC));
compile();
}
/**
* @param id for the Customer to be updated
* @param rating the new value for credit rating
* @return number of rows updated
*/
public int run(int id, int rating) {
Object[] params =
new Object[] {
new Integer(rating),
new Integer(id)};
return update(params);
}
}
4. 쿼리 관리
- applicationContext.xml
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>DBQuery</value>
<value>Messages</value>
</list>
</property>
</bean>
<bean id="messageSourceAccessor" class="org.springframework.context.support.MessageSourceAccessor">
<constructor-arg>
<ref local="messageSource" />
</constructor-arg>
</bean>
- applicationContext-jdbc.xml
<bean id="myJdbcDaoSupport" abstract="true"
class="net.javajigi.common.dao.MyJdbcDaoSupport">
<property name="dataSource">
<ref local="dataSource"/>
</property>
<property name="messageSourceAccessor">
<ref bean="messageSourceAccessor"/>
</property>
</bean>
- applicationContext-board.xml
<bean id="boardFileDAOTarget" parent="myJdbcDaoSupport" class="net.javajigi.board.dao.SpringJDBCWithMSBoardFileDAO">
<property name="incrementer">
<ref local="boardFileIncrementer" />
</property>
</bean
<bean id="boardDAOTarget" parent="myJdbcDaoSupport" class="net.javajigi.board.dao.SpringJDBCWithTemplateBoardDAO">
<property name="incrementer">
<ref local="boardIncrementer" />
</property>
</bean>
TIP ref local 과 local는 같은 xml에 존재하느냐 아니냐의 차이임
공통정보를 명시해준다.
의문은 class 구현시에 extends를 이미 시켜주고 있는데
이런 설정이 필요할까 하는것..
중복으로 써주는게 아닐까..
-- 출처 : 나