DBCP(DataBase Connection Pool)
데이터베이스를 연동하고, 쿼리를 날려서, 결과를 가져오는 과정은
전체 처리 시간 중에서 꽤 시간을 많이 소요한다.
즉, 효율적으로 데이터베이스와 연결하는 것이 빠른 처리에 영향을 많이 준다!
데이터베이스에 연결하는 Connection은 객체 = 새롭게 만들어질 때 시스템의 자원을 요구한다.
메모리에 객체를 할당하고, 객체가 사용할 여러 자원들에 대한 초기화 작업을 거친다.
그리고 객체 사용이 끝나면 객체를 다시 회수해야 한다.
→ 품이 많이 듭니다!
더군다나 기존의 JDBC는 Connection을 생성한 후 바로 해제되지 않아 메모리 누수가 발생한다.
그렇게 데이터베이스에 연결을 많이 반복할수록 서버에 부하가 가고, 쉽게 서버가 다운된다.
→ Connection 생성을 제한하고 재사용할 수 있도록 해 주도록 기술이 생겨났고, 이것이 DBCP!
Connection 객체를 여러 개 만들어 두고 필요할 때마다 가져다 쓰고,
사용이 끝난 객체를 반납하고 또 가져다 사용하는 식으로 작동한다!
또한 미리 데이터베이스에 연결해 두었기 때문에 또 연결하는 시간을 아낄 수 있다.
그래서 어떻게 쓰는거냐면...
Commons DBCP(https://mvnrepository.com/artifact/commons-dbcp/commons-dbcp/1.4)
Commons Pool(https://mvnrepository.com/artifact/commons-pool/commons-pool/1.6)
위 두개의 링크에서 각각의 jar파일 다운로드해서 apache-tomcat-9.0.64\lib 에 두개의 jar파일을 갖다놓는다.
그리고 데이터베이스에 연결해야 하는 프로젝트를 실행시킨 후
Servers에 있는 server.xml 을 열어 DBCP를 설정해 준다.
* Tomcat을 사용한다는 가정 하에 *
아래 정보를 server.xml의 해당 프로젝트 위치의 Context 태그 안에 작성한다.
1. Driver, URL, usename, password
2. auth(=Author, 누가 Connection을 만들어 줄 것인가?)
3. 생성된 Connection을 찾을 수 있게 이름 부여
4. 몇개 생성할지
maxActive : 동시에 사용할 수 있는 Connection의 최대 개수
maxIdle : 최대로 유지할 수 있는 Connection
maxWait : 접속이 늦어질 경우 최대로 기다리는 시간 지정
server.xml
<Context docBase="0722DBCPProject" path="/0722DBCPProject"
reloadable="true" source="org.eclipse.jst.jee.server:0722DBCPProject">
<!-- DBCP 요청하는 부분 -->
<Resource
driverClassName="oracle.jdbc.driver.OracleDriver"
url="jdbc:oracle:thin:@localhost:1521:XE"
username="username"
password="pwd"
auth="Container"
name="jdbc/oracle"
type="javax.sql.DataSource"
maxActive="10"
maxIdle="10"
maxWait="-1"
/>
</Context>
이처럼 server.xml에만 서버 정보가 작성되어 다른 코드에서 서버 정보가 노출되지 않기 때문에 보안에 뛰어나다!
그럼 연결한 데이터베이스를 한 번 써 보자.
아래와 같이 서울 여행정보를 이름, 이미지링크, 내용, 주소, 번호 정보를 데이터베이스에서 사용할 것이다.
LocationVO.java
package dao;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class LocationVO {
private int no;
private String title, poster, msg, address;
}
이제 데이터를 받아 와 보자.
JDBC를 쓰는 것과 getConnection() 외에는 동일하다.
DBCP를 이용해서 생성한 연결 객체는 java://comp/env라는 곳에 저장된다.
탐색기를 열어 이름으로 객체를 찾고, 저장된 폴더에서 연결 객체를 읽어 와 사용한다.
LocationDAO.java
package dao;
import java.util.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
public class LocationDAO {
private Connection conn;
private PreparedStatement ps;
//DBCP -> POOL(Connection을 저장해서 모아 놓고 관리하는 것)
//연결 객체 가지고 오기
public void getConnection() {
try {
//저장된 위치를 가지고 온다.(JNDI)-> java://comp/env에 Connection 객체가 저장된다.
//1. 탐색기를 연다.
Context init = new InitialContext();
//2. 드라이버 열기 (lookup = 이름으로 객체를 찾아 오는 것)
Context c = (Context) init.lookup("java://comp/env");
//3. 저장된 폴더에서 Connection을 읽어 온다.
DataSource ds = (DataSource) c.lookup("jdbc/oracle");
conn = ds.getConnection();
} catch (Exception ex) {
}
}
//다 쓰고 나서 반환하기(닫기가 아님!!)
//-> commons-dbcp.jar에서 지원해준다. 다른 사용자가 재사용할 수 있게 해줌.
public void disConnection() {
try {
if (ps != null) ps.close();
if (conn != null) conn.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
LocationDAO.java
웹페이지를 구성할 데이터를 형식에 맞게 가져 오는 기능을 구현한다.
package dao;
import java.util.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
public class LocationDAO {
//기능 구현
public List < LocationVO > locationListData(int page) {
List < LocationVO > list = new ArrayList < LocationVO > ();
try {
getConnection(); //미리 생성된 Connection 객체 주소를 얻어 온다
String sql = "SELECT no, title, poster, num " +
"FROM (SELECT no, title, poster, rownum as num " +
"FROM (SELECT no, title, poster " +
"FROM seoul_location ORDER BY no ASC)) " +
"WHERE num BETWEEN ? AND ?";
//rownum을 이용하면 Top-N만 가능하고 중간에서 잘라올 수 없다.
ps = conn.prepareStatement(sql);
int rowSize = 12;
int start = (rowSize * page) - (rowSize - 1);
int end = rowSize * page;
ps.setInt(1, start);
ps.setInt(2, end);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
LocationVO vo = new LocationVO();
vo.setNo(rs.getInt(1));
vo.setTitle(rs.getString(2));
vo.setPoster(rs.getString(3));
list.add(vo);
}
} catch (Exception ex) {
} finally {
disConnection();
}
return list;
}
//총 페이지 구하기
public int locationTotalPage() {
int total = 0;
try {
getConnection();
String sql = "SELECT CEIL(COUNT(*)/12.0) " +
"FROM seoul_location";
ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery();
rs.next();
total = rs.getInt(1);
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
disConnection();
}
return total;
}
그리고 페이지를 아래와 같이 구성하면...
seoul.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="java.util.*, dao.*"%>
<jsp:useBean id="dao" class="dao.LocationDAO"/>
<%
//1. page받기
String strPage = request.getParameter("page");
if(strPage==null)
strPage = "1";
int curPage = Integer.parseInt(strPage);
//현재 페이지에 해당되는 데이터 읽어 오기
List<LocationVO> list = dao.locationListData(curPage);
//총페이지 구하기
int totalPage = dao.locationTotalPage();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<style type="text/css">
.container{
margin-top:50px;
}
.row{
margin: 0px auto;
width: 960px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<%
for(LocationVO vo:list){
%>
<div class="col-md-3">
<div class="thumbnail">
<a href="#">
<img src="<%=vo.getPoster() %>" alt="" style="width:220px; height:150px">
<div class="caption">
<p style="font-size:9px"><%=vo.getTitle() %></p>
</div>
</a>
</div>
</div>
<%}%>
</div>
<div class="row">
<div class="text-center">
<a href="seoul.jsp?page=<%=curPage>1?curPage-1:curPage %>" class="btn btn-sm btn-success">이전</a>
<%=curPage %>page/<%=totalPage %>pages
<a href="seoul.jsp?page=<%=curPage<totalPage?curPage+1:curPage %>" class="btn btn-sm btn-info">다음</a>
</div>
</div>
</div>
</body>
</html>
짜잔! 이렇게 된다~
Reference :
'부트캠프(END) > Web' 카테고리의 다른 글
EL/JSTL (0) | 2022.07.25 |
---|---|
JSP : 내장 객체 / session (0) | 2022.07.21 |
jspBean (0) | 2022.07.21 |