[Spring] JDBC Template
JDBC Template 사용하기
기존의 JDBC를 이용해 DBCP를 만들고, 커넥션풀에서 매번 Connection을 받아오고,반납하는 과정은 번거롭다.
JDBC Template를 이용하면 커넥션 할당과 반납등 연동과 관련된 동작 및 Preparestatement SQL 쿼리 조작등을
JDBC 템플릿 메소드를 통해 손쉽게 해결할수있다.
JDBC TEMPLATE을 사용하기위해 해당 디펜던시를 추가해준다.
JDBC Template를 사용하기위해 먼저, DB 연결정보인 DataSource 객체를 스프링 빈에 등록해야한다.
프로젝트 파일 안에서 빈에 주입하고싶을경우, root-context.xml 파일에 등록해주면되지만,
Tomcat WAS서버의 CONTEXT 파일에 설정해주면 모든 프로젝트에서 DataSource 객체를 빈으로 사용할수있다.
해당파일의 <Resouce> 태그에 DBCP 관련 설정과 DataSource 설정정보를 입력해준다.
DBCP 설정 정보 :
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver" maxIdle="30" maxTotal="10" maxWaitMillis="10000" name="jdbc/oracle" password="PW" type="javax.sql.DataSource" url="DB URL" username="NAME"/>
스프링 빈에 JdbcTemplate, DataSource 객체 등록
기존의 JDBC DB접근은 매번 DB에 접근할때마다 getConnection메소드를 통해 커넥션을 얻어와 연결했었다.
그러나 스프링 빈을 통해 DataSource 객체와 JdbcTemplate 객체를 등록해놓으면, DB 접근시 DataSource 정보를 참조하고있는 JdbcTemplate 객체가 주입되면서 자동으로 관련 처리를 해주기 때문에 굉장히 편리하다.
JDBC vs JdbcTemplate 차이
다음은 기존의 JDBC와 DBCP를 이용한 DB접근 Dao 메소드이다.
public class BDao {
DataSource dataSource;
public BDao() {
// dbcp 방식으로 oracle 연결
try {
Context ctx = new InitialContext();
// dataSource : 조회,저장,수정,삭제 쿼리에 모두 사용가능
dataSource =(DataSource) ctx.lookup("java:comp/env/jdbc/oracle");
} catch (Exception e) {
e.printStackTrace();
}
}
// 게시판 목록 조회
public ArrayList<BDto> list(){
ArrayList<BDto> list = new ArrayList<BDto>();
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
connection =dataSource.getConnection();
String sql = "select bid,bname,btitle,bcontent,bdate,bhit from mvc_board";
pstmt = connection.prepareStatement(sql);
rs= pstmt.executeQuery();
while(rs.next()) {
int bid = rs.getInt("bid");
String bname = rs.getString("bname");
String btitle = rs.getString("bname");
String bcontent = rs.getString("bname");
Timestamp bdate = rs.getTimestamp("bdate");
int bhit = rs.getInt("bhit");
// 하나의 게시글 객체
BDto bDto = new BDto(bid,bname,btitle,bcontent,bdate,bhit);
list.add(bDto);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(rs!=null) rs.close();
if(pstmt!=null) pstmt.close();
if(connection!=null) connection.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
return list;
}
기본생성자로 매번 datasource에 dbcp관련 설정을 할당하고 각 메소드마다 커넥션을 얻어오고, 사용한뒤 자원을 반납하는 과정을 반복하고있는것을 볼수있다.
public class BDao {
JdbcTemplate template=null;
public BDao() {
// 기본 생성자로 Constant의 static template 객체를 주입 (constant.template는 컨트롤러로부터 주입받음)
template = Constant.template;
}
// 게시판 목록 조회
public ArrayList<BDto> list(){
// ArrayList<BDto> list = null;
String sql = "select bid,bname,btitle,bcontent,bdate,bhit from mvc_board";
//query() : jdbc template 쿼리 처리함, RowMapper(BDto클래스)를 통해 쿼리결과값으로 날라온 ResultSet이 자동으로 BDto에 매핑됨
return (ArrayList<BDto>)template.query(sql, new BeanPropertyRowMapper(BDto.class));
}
public void write(final String bname,final String btitle,final String bcontent) {
// update(): jdbctemplate로 insert( PreparedStatementCreator 객체 사용)
// 메소드 파라미터에 PreparedStatementCreator객체를 만들어 쿼리에 동적변수할당 (제이쿼리와 유사한 문법)
template.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con) throws SQLException {
String sql = "insert into mvc_board(bid,bname,btitle,bcontent,bhit)"
+ " values(mvc_board_seq.nextval,?,?,?,0)";
PreparedStatement pstmt = con.prepareStatement(sql);
// 익명클래스에서 메소드의 파라미터 변수를 받기위해 파라미터 변수를 final로 선언해야함
pstmt.setString(1, bname);
pstmt.setString(2, btitle);
pstmt.setString(3, bcontent);
return pstmt;
}
});
}
public BDto contentView(String strID) {
upHit(strID);
BDto dto = null;
String sql = "select bid,bname,btitle,bcontent,bdate,bhit from mvc_board where bid="+strID;
// dto = (BDto)template.query(sql, new BeanPropertyRowMapper(BDto.class));
return (BDto)template.queryForObject(sql,new BeanPropertyRowMapper<BDto>(BDto.class));
}
public void upHit(final String bid) {
String sql = "update mvc_board set bhit=bhit+1 where bid=?";
template.update(sql,new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1,Integer.parseInt(bid));
}
});
}
// 게시글 수정
public void modify(final String bid,final String bname,final String btitle,final String bcontent) {
String sql = "update mvc_board set bname=?,btitle=?,bcontent=? where bid =?";
template.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, bname);
ps.setString(2, btitle);
ps.setString(3, bcontent);
ps.setInt(4,Integer.parseInt(bid));
}
});
}
// 게시글 삭제
public void delete(final String bid) {
String sql = "delete from mvc_board where bid =?";
template.update(sql, new PreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1,Integer.parseInt(bid));
}
});
}
}
다음은 Jdbc 템플릿을 사용하여 똑같은 기능을 구현한 코드이다.
DB연동과 관련된 코드를 매번 작성할 필요없이 필드멤버로 JdbcTemplate를 선언해준뒤, 의존성을 주입받기만 하면 된다!
JdbcTemplate 메소드
1. update()
: DB에 update,insert,delete를 하고싶을때, JdbcTemplate의 update() 메소드를 이용한다.
기존에 동적쿼리는 PrepareStatement 객체를 통해 ? 매핑으로 할당해주었는데,
아쉽게도 JDBC Template또한 비슷하게 동적쿼리를 할당해주어야한다.
update(new PreparedStatementCreator()) 메소드의 파라미터로 new PreparedStatementCreator() 인터페이스 객체를 생성하고, 해당 인터페이스를 익명클래스로 createPreparedStatemet를 구현하면서, 내부에 sql 매핑을 하는 예제이다.
코드를 꼼꼼히 살펴보면 기존의 과정과 굉장히 비슷한데,
마치 자바스크립트의 익명함수 function()을 사용하는 형태와 유사하다.
2. Query()
조회(Select)를 하고싶을때 사용하는 메소드로,
기본 형태는 query( sql구문, Rowmapper() 또는 결과행의 데이터타입 클래스 , 동적쿼리 할당할 컬럼명 ) 이다.
기존에 일일이 DTO 객체를 생성해서 ResultSet으로 받아와서 객체에 값을 넣어준뒤 ArrayList에 넣어줬던 과정을
BeanPropertyRowMapper를 통해 한방에 해결할수있다!
RowMapper는 쿼리결과 DB에서 날라온 결과값들을 JAVA의 객체에 매핑시켜주는 역할로, 파라미터 내부에 메소드 선언과 동시에 사용하거나, 따로 필요에 맞게 수정하여 RowMapper 메소드를 선언해놓은뒤 꺼내써도된다.
그리고, DB에 조회한 결과행이 단건조회일때에는 QueryForObject()를 사용하여 객체에 매핑 시킬 필요가없이 기본 데이터타입으로 받아온다면 String.class 나 Integer.class등으로 받아올수도 있다!