본문 바로가기
개발, IT

서블렛 + JDBC 연동시 코딩 고려사항 -제3탄-

by Nabi™ 2008. 5. 22.

질문이 구체적이지 않아, 무얼 묻고 있는지 추측으로 답변드리겠습니다.

질문추측:
JDBC를 이용하여 DB작업을 하는데, 여러개의 클래스로 각각의 몇몇 SQL문장을
실행하는 구조인데, 지금은 하나의 클래스에서 개별적으로 Connection을 얻어
Statement를 실행하고 Connection을 닫는 구조입니다.
그러나, 주된 메인클래스에서 그렇게 만들어진 여러 클래스의 메소드를 호출하여
전체가 하나의 DB Transaction으로 연결되게 하고 싶습니다. 어떻게하면 됩니까?

답변:
Servlet/JSP를 이용한 프로젝트에서, 국내에서 유통(?)되고 있는 대표적인
DB핸들링 기법은 (제 생각으로는) 다음과 같은 유형으로 축약되는 듯 합니다.

첫째, "DbBean"의 형태 입니다. 즉, Entity Data를 저장하는 역할인 Entity 클래스와
java.sql.Connection을 얻고  SQL 문장을 통해 DB로부터 Data를 가져오는 부분인
DBWrapper 클래스가 하나로 결합되어 있는 형태입니다.

public class ABean
{
    private String empno = null;
    private String ename = null;
    public ABean() {}
    public ABean(String empno, String ename){
        this.empno = empno; this.ename=ename;
    }
    public void setEmpno(String empno){this.empno = empno;}
    public void setEname(String ename){this.ename = ename;}
    public String getEmpno(){return empno;}
    public String getEname(){return ename;}
    public void restore() throws Exception{
        Connection conn = null;
        Statement  stmt = null;
        try{
            conn = <getConnection()>
                  //... get java.sql.Connection from JDBC Driver directly
                  // or DBConnection Pool.
            stmt = conn.createStatement();
            ResultSet rs = stmt.executeQuery("select empno, ename from ...");
            if ( rs.next() ){
                empno = rs.getString(empno);
                ename = rs.getString(ename);
            }
        }
        finally {
            if ( stmt != null ) try{stmt.close();}catch(Exception e){}
            if ( conn != null ) try{
               <releaseConnection()>
               // ... conn.close() or  release "conn" to DB Connection Pool
            }catch(Exception e){}
        }
    }
}

위 클래스를 사용하는 방법은 보통 다음과 같게 됩니다.
.....
ABean a = new ABean();
a.restore();
String ename = a.getEmpno();
String ename = a.getEname();
.....

JSP에서는 다음처럼 사용할 수 있겠죠.

<jsp:useBean id="aBean"  class="ABean" scope="page" />
<% aBean.restore() %>
...
<%= aBean.getEmpno() %> ... <%= aBean.getEname() %> ...


혹은 Default Constructor에서 restore()까지 자동으로 실행케 한 후,
    public ABean(){
        try{ restore();}catch(Exception e){....}
    }
즉, Constructor 에서 초기화 작업까지 함으로써 이를 JSP에서 다음과 같이
사용하기도 합니다.

<jsp:useBean id="aBean"  class="ABean" scope="page" />
...
<%= aBean.getEmpno() %> ... <%= aBean.getEname() %> ...


때론 이러한 구조에서 매번 반복되는 "Connection을 얻고 맺는 부분"을 별도로
뽑아 내기도 합니다. 뽑아 내는 방식도 (전 바람직하지 않은 구조라고 생각하는)
부모클래스의 abstract class에서 메소드를 제공하여 처리하는 방식과, 별도의
Connection Adapter클래스를 사용하는 방식으로 사용하곤 합니다.
(Connection Adapter클래스에 관해서는 본게시판에서 아래글을 참조하세요.
"서블렛 + JDBC 연동시 코딩 고려사항 -제2탄-" )

어쨌거나 이러한 "DbBean" 구조는 다른 그 어떤 구조보다 "Data"에 촛점이 마추어져
있습니다. 당연히 "insert, update"보다는 "select"성의 Query문장이 많이 사용됩니다.
그러나 이러한 "DbBean"의 구조에서 insert나 update성의 SQL 문장을 실행하여
DB에 저장하는 구조를 찾고자 하는 욕구가 자연스럽게 생깁니다.

예를 들어, 위의  ABean 클래스에 save() 메소드를 추가하여 DB에 저장케 할 수도
있습니다.

public class ABean
{
    ....
    public void save() throws Exception{
        Connection conn = null;
        Statement  stmt = null;
        try{
            conn = <getConnection()>
                        //... get java.sql.Connection from JDBC Driver directly
                        // or DBConnection Pool.
            stmt = conn.createStatement();
            stmt.executeUpdate("insert into emp(empno,ename) values" +
                 "('" + empno + "','" + ename + "')");
        }
        finally {
            if ( stmt != null ) try{stmt.close();}catch(Exception e){}
            if ( conn != null ) try{
               <releaseConnection()>
               // ... conn.close() or  release "conn" to DB Connection Pool
            }catch(Exception e){}
        }
    }
}

이제 이것을 다음처럼 사용할 수 있겠죠...

....
ABean aBean = new ABean("7904", "이원영");
aBean.save();
....

혹은 JSP라면 Http Request에서 날아온 "empno" 와 "ename"을 곧바로
DB에 저장케 할 수 있습니다.

.....
<jsp:useBean id="aBean"  class="ABean" />
<jsp:setProperty name="aBean" property="*" />
<% aBean.save() %>
...

문제는, ....
이러한 구조에서 ABean과 유사한 BBean이 존재하고, ABean과 BBean을
DB에 동시에 저장시키면서 단일의 Transaction으로 처리되기를 원할  때
발생합니다.

ABean aBean = new ABean("7904", "이원영");
BBean bBean = new BBean("7904", "홍길동");
aBean.save();
bBean.save();

왜냐면, aBean만 저장하고, bBean은 저장되지 않았을 경우, aBean작업도
rollback 되어야 하는데, 이와 같은 Transaction처리를 이 구조에서는 할 수
없기 때문이죠....

사실 이러한 근본적인 문제를 해결한 것이 산업계에서 EJB의 EntityBean의
구조입니다.
그러나, 여기선 EJB를 사용하고 있지도 않고, 각 Bean내부에서 별도의
java.sql.Connection을 사용하고 있으니, 같은 java.sql.Connection을 사용하지
않는 이상 해결할 수 없는 문제입니다.

따라서, 어떻게 하든, 같은 java.sql.Connection을 사용하도록 "Option"을 제공
해 주어야 합니다. 위의 "DbBean"의 개념을 최대한 살리는 입장에서 한가지
방법을 제시하면 다음과 같이 추가적인 save 메소드를 제공할 수 있습니다.


public class ABean
{
    ....
    public void save() throws Exception{
        Connection conn = null;
        try{
            conn = <getConnection()>
            save(conn);
        }
        finally {
            if ( conn != null ) try{
               <releaseConnection()>
            }catch(Exception e){}
        }
    }      
    public void save(java.sql.Connection conn) throws Exception{
        Statement  stmt = null;
        try{
            stmt = conn.createStatement();
            stmt.executeUpdate("insert into emp(empno,ename) values" +
                 "('" + empno + "','" + ename + "')");
        }
        finally {
            if ( stmt != null ) try{stmt.close();}catch(Exception e){}
        }
    }
}

이처럼 save() 뿐만 아니라 save(Connection conn) 메소드를 Option으로 제공하면
다음처럼 Transaction을 하나로 묶을 수가 있죠...

ABean aBean = new ABean("7904", "이원영");
BBean bBean = new BBean("7904", "홍길동");
java.sql.Connection conn = null;
try{
    conn = <getConnection()>
    conn.setAutoCommit(false);
    aBean.save(conn);
    bBean.save(conn);
    conn.commit();
}
catch(Exception e){
    if ( conn != null ) try{conn.rollback();}catch(Exception e){}
    throw e;
}
finally {
    // conn.setAutoCommit(true); or re-initialized by Connection Pool.
    if ( conn != null ) try{ <releaseConnection()> }catch(Exception e){}
}

그러나, 위의 코딩 유형은 어쩔 수 없이 만든 궁여지책의 방법입니다.
이렇게 사용하는 순간  "DbBean"의 "simplicity" 는 이미 사라져 버리기
때문입니다. 이미 프로젝트가 진행되었다면, 이렇게라도 기능을 제공하여
사용할 수 밖에 없겠지요...

두번째로, 많이들 사용하는 DB핸들링 기법중에 "DbHandle"과 같은 클래스를
사용하는 방식입니다. DB관련작업을 간단하게 하기 위해 모든 DB작업을
처리케 할 수 있는 "DB Master Class" 즉, 통상 "DbHandle" 같은 이름의
클래스를 사용하는 경우이죠.


(오늘은 여기까지.... 병원에 있는 출산한 아내가 자꾸 빨리 오라고 재촉하는 군요.
 다음에 계속해서 적겠습니다.
 현재의 생각으로는 DbHandle 방식과 그 문제성에 대해서, 그리고, Entity클래스와
 DBWrapper를 이용하는 방식을 설명하면서 Transaction처리를 어떻게 해야하는가에
 대한 생각을 적을 생각입니다.
)

좋은정보가 되셨다면 아래 한번 클릭해주세요^^


댓글