본문 바로가기
  • 실행력이 모든걸 결정한다
Spring 사전 준비/JSP Servlet

[JSP Servlet] 데이터베이스(2) - DTO/DAO/ConnectionPool

by 김코더 김주역 2020. 12. 3.
반응형

1. DAO와 DTO

DAO : Data Access Object, 데이터베이스의 데이터에 접근을 하기 위한 오브젝트로, 유지보수에 좋음


DTO : Data Transfer Object, 데이터 베이스에서 데이터 교환을 위한 javabean들을 의미한다. getter, setter 메소드로 구성된다.

 

 

아래 코드는 저번 포스팅에 사용했던 코드를 그대로 가져온 것이며, DAO/DTO 클래스 없이 데이터베이스와 연동하는 코드이다.

<%@page import="java.sql.DriverManager"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.Statement"%>
<%@page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
    <%!
        Connection connection;
        Statement statement;
        ResultSet resultSet;

        String driver = "oracle.jdbc.driver.OracleDriver";
        String url = "jdbc:oracle:thin:@localhost:1521:xe";
        String uid = "scott";
        String upw = "오라클 비밀번호";
        String query = "쿼리문";
    %>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<%
	Class.forName(driver); //메모리에 오라클 드라이버를 로드한다.
	connection = DriverManager.getConnection(url, uid, upw); //연결 수행
	statement = connection.createStatement();
	resultSet = statement.executeQuery(query);
				
	while(resultSet.next()){
		String id = resultSet.getString("id");
		String pw = resultSet.getString("pw");
		out.println("id : "+id+"<br>");
		out.println("password : "+pw+"<br>");
	}
%>
</body>
</html>

 

이제 위의 코드를 DAO/DTO로 나눠서 처리하도록 바꿔볼 것이다.

 

<test1.jsp - 메인 실행문>

<%@page import="com.javalec.cp.MemberDAO"%>
<%@page import="com.javalec.cp.MemberDTO"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.sql.DriverManager"%>
<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<%
    MemberDAO mDAO = new MemberDAO();
    ArrayList<MemberDTO> members = mDAO.memberSelect();
    for(int i=0;i<members.size();i++){
        out.println(i+1+". ID : "+members.get(i).getId()+", PW : "+members.get(i).getPw()+"<br>");
    }
%>
</body>
</html>

 

<MemberDAO.java>

데이터 베이스에 접속하여 데이터를 가져오는 역할을 한다.

예외 처리의 경우에는 아래 코드처럼 예외를 직접 잡아도 되지만, 메소드에 throws를 선언해서 메소드 밖으로 던지는 방법도 많이 사용된다.

package com.javalec.cp;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;

public class MemberDAO {
	private String uid="scott";
	private String upw="오라클 비밀번호";	
	private String url = "jdbc:oracle:thin:@localhost:1521:xe";
	
	public MemberDAO() {
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public ArrayList<MemberDTO> memberSelect(){
		ArrayList<MemberDTO> mList = new ArrayList<MemberDTO>();
		
		Connection con = null;
		Statement sta = null;
		ResultSet res = null;
		
		try {
			con = DriverManager.getConnection(url, uid, upw);
			sta = con.createStatement();
			res = sta.executeQuery("select * from member");
			
			while(res.next()){
				String id = res.getString("id");
				String pw = res.getString("pw");
				MemberDTO mDTO = new MemberDTO(id,pw);
				mList.add(mDTO);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 모두 close()가 이루어지도록 따로 try, catch 반복
			// 반환은 생성 순서의 반대로 하는 것이 원칙이다.
			if(res!=null){
				try {res.close(); }
				catch (SQLException e2) {}
			}
			if(sta!=null){
				try {sta.close(); }
				catch (SQLException e2) {}
			}
			if(con!=null){
				try {con.close(); }
				catch (SQLException e2) {}
			}
		}
		return mList;
	}
}

 

<MemberDTO.java>

데이터 베이스의 데이터를 javabean으로 만든 클래스이다

package com.javalec.cp;

public class MemberDTO {
	private String id;
	private String pw;
	
	public MemberDTO(String id, String pw) {
		this.id = id;
		this.pw = pw;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getPw() {
		return pw;
	}

	public void setPw(String pw) {
		this.pw = pw;
	}	
}

 

실행 결과

참고로, 필자는 데이터 베이스에 3개의 레코드를 미리 저장해두었다.

 

데이터가 잘 나오는 모습을 볼 수 있다.

 

+ 보충 개념) PreparedStatement 객체

위에서 사용했던 Statement 객체는 중복 코드가 많아져서 이를 보완하기 위한 객체이다.

Statement 객체는 query문을 미리 정해두지만,

PreparedStatement 객체는 query문에 "?" 를 넣어두고 "?" 를 동적으로 채워나갈 수 있기 때문에 중복코드를 확실히 줄일 수 있다.

 

백문이 불여일견, 예제를 보도록 하자

Connection con = null;
PreparedStatement psta = null;
ResultSet res = null;

try{
    int n;
    con = DriverManager.getConnection(url, uid, upw);
  
    String query = "insert into table1 (name, age, phone) values (?,?,?)";
  
    psta = con.prepareStatement(query);
  
    psta.setString(1,"jooyeok");
    psta.setString(2,"23");
    psta.setString(3,"010-0000-0000");
    
    n = psta.executeUpdate();
    
    psta.setString(1,"lamb");
    psta.setString(2,"16");
    psta.setString(3,"010-1111-1111");
    
    n = psta.executeUpdate();
    
    ...

query문을 보면 3개의 ?(물음표)가 있고, 이후에 setString 메소드를 이용하여 ? 에 원하는 데이터를 채워넣는 모습이다.

이렇게 되면 query문이 접근에 따라 동적으로 바뀌게 되어 코드 중복이 상당히 해소될 것이다.

executeUpdate으로 쿼리를 내보내게 되면 query문의 "?"이 모두 자동으로 초기화 되어 계속 데이터 쌍을 채워 넣을 수 있다.

 

 

 

2. ConnectionPool

1) ConnectionPool 이란?

웹 서버는 데이터베이스에 접근해서 동작마다 connection 객체를 생성하는데, connection 객체가 너무 많이 생성되면 효율적이지 않다. 그래서 connection 객체를 미리 만들어놓고 그것을 가져다 쓰고 반환하는 방법이 고안된 것인데 그것이 ConnectionPool이다.

 

 

2) 설정 방법

Connection 객체를 생성하는건 웹 서버이다.

그렇기 때문에 tomcat 컨테이너가 데이터베이스 인증을 할 수 있게 Servers에 있는 context.xml 파일에 알맞게 추가 해주어야 한다.

    <Resource
     auth="Container"
     driverClassName = "oracle.jdbc.driver.OracleDriver"
     url = "jdbc:oracle:thin:@localhost:1521:xe"
     username = "scott"
     password = "오라클 비밀번호"
     name = "jdbc/Oracle11g"
     type = "javax.sql.DataSource"
     maxActive = "40"
     maxWait = "1000"
    />

* maxActice : 동시에 사용할 수 있는 최대 Connection

* maxWait : ConnectionPool에 연결 가능한 Connection이 없을 경우 반납되는 Connection을 대기하는 시간이다. 값을 설정하지 않는 경우에는 응답이 올 때 까지 쭉 기다린다.

 

모두 설정했다면 이 파일을 저장하고, 서버를 멈추고 publish 를 해준다.

 

마지막으로 MemberDAO.java 의 내용 수정도 필요하다.

context.xml 에서 접속에 필요한 데이터들을 대부분 세팅해놓았기 때문에 코드는 간결해진다.

DataSource를 통해 접속하는 코드로 바뀌었다.

 

<MemberDAO.java>

* 31줄 "con = dataSource.getConnection(); " 이후론 바뀐 내용이 없다.

package com.javalec.cp;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class MemberDAO {	
	private DataSource dataSource;	
    
	public MemberDAO() {	
		try {
			Context context = new InitialContext();
			dataSource = (DataSource)context.lookup("java:comp/env/jdbc/Oracle11g");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public ArrayList<MemberDTO> memberSelect(){
		ArrayList<MemberDTO> mList = new ArrayList<MemberDTO>();
		
		Connection con = null;
		PreparedStatement sta = null;
		ResultSet res = null;
		
		try {
			con = dataSource.getConnection();
			sta = con.prepareStatement("select * from member");
			res = sta.executeQuery();
			
			while(res.next()){
				String id = res.getString("id");
				String pw = res.getString("pw");
				MemberDTO mDTO = new MemberDTO(id,pw);
				mList.add(mDTO);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 모두 close()가 이루어지도록 따로 try, catch 반복
			// 반환은 생성 순서의 반대로 하는 것이 원칙이다.
			if(res!=null){
				try {res.close(); }
				catch (SQLException e2) {}
			}
			if(sta!=null){
				try {sta.close(); }
				catch (SQLException e2) {}
			}
			if(con!=null){
				try {con.close(); }
				catch (SQLException e2) {}
			}
		}
		
		return mList;
	}
}

 

 

3) 실무에서 쓰이는 ConnectionPool

(1) DBCP

- 가장 유명한 오픈소스 DB connection pool 라이브러리다.

- 애플리케이션 레벨의 소스단에 설정된다.

 

(2) JNDI

- 서버가 제공하는 DB connection pool을 사용하기 위해 사용된다.

- WAS단에 설정되며, WAS에 naming된 connection pool을 가져오는 방식이다.

 

반응형

댓글