오늘도 JDBC에 대한 내용을 다뤄본다.
어제 했던 회원관리 프로그램을 더 다듬어서 표현해보겠다. 이전 포스팅을 보고오면 코드가 상당이 긴 것을 확인할 수 있다. try / catch 가 엄청 많이 사용됐기도 하고, 또 모든 값을 하나하나 넣어주려그러니 쓸데없이 코드만 길어진다. 이걸 보완하면서 코드를 한번 짜보도록 해보겠다.
클래스를 이용해서 하나하나 정리해보자.
이번에는 클래스를 더 여러개를 만들어서 정리하며 사용해본다.
사용할 클래스들과 클래스의 역할을 정리하자면
- JDBCTemplate : db관련 공통코드 처리역할
- MemberController : 화면담당 / 사용자의 요청을 받는 역할 / 결과처리
- MemberService : 트랜잭션처리 / 데이터 편집
- MemberDao : db와 데이터를 주고받는 역할
- Member : 회원 1명정보를 저장하기위한 데이터의 형태(Value Object -> vo)
- Start : 프로젝트를 실행
각 클래스를살펴보면서 어떤 역할을 하는지 자세하게 알아보자.
들어가기 전에 정해놓을게 있다.
Connection conn
PreparedStatement pstmt
ResultSet rset
필자가 지금부터 사용하는 모든 코드는 위의 선언을 따른다.
이 클래스는 db관련 코드들을 짜놓는 역할을 한다.
Connection은 db와 연결해주고 값을 주고받게 도와주는 역할을한다. 그런데 연결하는 모든 메소드마다 Connection객체를 생성해준다면 메모리의 낭비가 심해지게된다.
그래서 public static (static method) 를 이용해준다.
static method
static 메소드를 이용하면 프로그램을 시작하는 순간에 java가 딱 한번 메모리를 할당하며 어떤 패키지에서든 불러서 사용할 수 있게된다. 그래서 자주 사용하는 db와 연결하는 객체를 static method로 선언한 것 이다.
이렇게 따로 만들어주는 이유는 또 있다. 사실은 메모리보다 이게 훨씬 중요하다.
JDBCTemplate을 사용하는 이유
- 우리가 하나의 메소드를 사용할때마다 하나의 쿼리만을 사용하는 것이 아니다.
3개의 쿼리를 하나의 메소드에서 사용한다고 가정해보자.
그 3개의 쿼리 중 하나의 쿼리만 틀렸다고 가정하고, 모든 쿼리에 Connection 객체를 따로 만들었다고 생각하면, 틀린 하나의 쿼리를 제외한 2개의 쿼리는 아주 잘 실행되서 결과값이 commit된다.
그러한 상황을 막아주기 위해서 JDBCTemplate을 만들어주고 모든 쿼리를 끝내고 사이좋게 한꺼번에 commit 또는 rollback을 해주는 것 이다. 동기 버리고 갈 수 없다.
static method로 선언해줄 메소드들은 Connection / commit / rollback / close 이다.
public static Connection getConnection() {
Connection conn = null;
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe","jdbc","1234");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
원래는 db와 연결해줄때마다 Connection 객체를 생성해야했는데, 그걸 한번만 할 수 있도록 만들어줬다. 이제 Connection 객체를 생성하는 것 대신에
Connection conn = JDBCTemplate.getConnection();
을 선언하면 된다.
public static void commit(Connection conn) {
try {
//매개변수로 받은 conn이 정상인지 체크
if(conn != null && !conn.isClosed()) {
conn.commit();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//commit end
데이터를 성공적으로 변환했을 때 commit을 해서 db에도 데이터를 승인했다고 알려주면서 저장해야하는데 그걸 해주는게 이 commit이다.
JDBCTemplate.commit(conn);
이렇게 사용해주면 된다.
public static void rollback(Connection conn) {
try {
if(conn != null && !conn.isClosed()) {
conn.rollback();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//rollback end
rollback은 데이터의 변환에 실패했을때, 값을 가장 최근에 commit한 값으로 돌려주는 기능이다.
메소드에 선언한 쿼리 중 하나라도 틀리면 모두 같이 rollback하기위해 선언했다.
JDBCTemplate.rollback(conn);
사용법이다.
public static void close('자원' '?') {
try {
//매개변수로 받은 conn이 정상인지 체크
if('?' != null && !'?'.isClosed()) {
'?'.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//close end
'?' == conn / pstmt / rset
'자원' == Connection / PreparedStatement / ResultSet
close는 코드가 3개다. 3개의 코드 모드 '자원' 부분과 '?'만 다르다.
자원을 사용하고 다 사용하면 컴퓨터한테 자원을 반납해야하지 않겠는가? 그래서 사용한다.
DBCTemplate.close(conn);
DBCTemplate.close(pstmt);
DBCTemplate.close(rset);
사용법이다.
이렇게 JDBCTemplate을 마치겠다.
MemberController는 화면담당 / 사용자의 요청을 받는 역할 / 결과처리 만 수행하고 나머지 과정들은 모두 service클래스에 맡긴다. 그래서 사용하는 변수가 Scanner와 service밖에 없다.
private Scanner sc;
private MemberService service;
public MemberController() {
super();
sc = new Scanner(System.in);
service = new MemberService();
}
변수는 위와같이 선언한다.
MemberService는 트랜잭션 처리와 데이터의 편집을 담당한다.
이 클래스의 진행 순서를 정리해보자.
- Connection 생성하기
- 현재 변수값을 편집해야하는지?
- dao를통한 db작업 (dao가 필됴한 conn및 데이터를 매개변수로 처리)
- commit,rollback 여부
- 자원 반환
- 결과 리턴
MemberController 클래스에서 해야할 일들을 받아오고, db와 직접적으로 통신할 수 있도록 자료를 정리해서 MemberDao클래스에 보내준다. 이 클래스에서 commti과 close가 끝난다.
MemberDao는 db와 데이터를 직접적으로 주고받는 클래스이다. MemberService에서 사용할 자료를 다 정리해서 보내주면 MemberDao클래스가 db와 자료를 주고받고, 결과값을 리턴해준다.
이 과정에서 데이터를 집어넣기 때문에 여기서 Member객체를 넣는 메소드를 만든다.
이 클래스의 진행 순서를 정리하고 코드를 보자.
- 필요한 변수 선언
- PreparedStatement 객체 생성 (+query문 문법검사)
- query에 있는 위치홀더(?)에 값 대체 // 위치홀더가 없으면 이 돠정을 생략한다.
- 쿼리문 수행 -> 조호결과 저장
- 사용한 자원 반환
public Member getMember(ResultSet rset) {
Member m = new Member();
try {
m.setAddress(rset.getString("address"));
m.setAge(rset.getInt("age"));
m.setEmail(rset.getString("email"));
m.setEnrollDate(rset.getDate("enroll_date"));
m.setGender(rset.getString("gender"));
m.setMemberId(rset.getString("member_id"));
m.setMemberName(rset.getString("member_name"));
m.setMemberNo(rset.getInt("member_no"));
m.setMemberPw(rset.getString("member_pw"));
m.setPhone(rset.getString("phone"));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return m;
}//getMember end
이제 Member객체에 데이터를 집어넣을 때
Member m = getMember(rset);
를 하면 된다.
이 클래스에서 PreparedStatement와 ResultSet을 사용하고 모두 반환해준다.
PreparedStatement pstmt = null;
ResultSet rset = null;
finally {
JDBCTemplate.close(rset);
JDBCTemplate.close(pstmt);
}
아까 JDBCTemplate에서 만들어줬던 close매소드를 사용하면된다.
Member클래스는 데이터베이스에서 선언한 테이블의 변수와 같은 변수를 넣어주면 된다. 같은 형을 사용하도록 주의하자! 여기서 Getter Setter도 만들고, 생성자도 만든다.
public class Start {
public static void main(String[] args) {
MemberController mc = new MemberController();
mc.main();
}
}
그냥 여태 짰던 코드 실행해주는 코드이다.
물론 클래스의 이름을 달리해도 괜찮다. 그냥 의미만 알아듣게 잘 설정하자.
JDBC를 더 깔끔하게 정리하는 방법을 알아보았다.
여기까지!