데이터베이스를 연동하는 프로그램을 개발할 때 반드시 사용되는 2 가지 개발 패턴
DAO와 DTO가 있다.
Persistence - 영구적 영속적으로 속해서 DB를 조작하는 개체
->DAO 객체, MapperBusiness - 비즈니스 로직을 수행하는 모든 객체
->서비스 객체Presentation - Spring MVC 모델(화면에 보이는 영역)
(Persistence(DB) - Business -Presentation)
*디자인 패턴
특수한 기능을 수행하는 클래스를 최소개수로 설계하는 방법을 의미
-모듈화 시켜 개발하게 되며 모듈화한 클래스들중에서 데이터베이스 처리하는 코드만을 관리하는 클래스를 작성할 수 있는데 이 클래스 파일을 DAO 클래스라고 함 ->사용하면 DAO 패턴이라고 부른다.
-DAO 클래스안에는 특정 테이블에서 수행할 작업을 메소드로 정의해서 구현
-presentation logic에서는 DAO 클래스의 메소드를 호출하면서 원하는 작업을 구현
-persistence 영속성 패키지 안에 만든다.
▼DAO를 사용한 예제
-알아본 점
(0)static으로 데이터영역에서 공유가능하게 함.
(1)static 블럭으로 초기화 해준다 -> 단 한번의 초기화로 고정된 데이터를 만들기 위해
(2)InitialContext()를 사용한다. -> servlet이 아닌 DAO에서 어노테이션을 이용 불가
(3)JNDI(Java Naming and Directory Interface)는 디렉터리 서비스에서 제공하는 데이터 및 객체를 발견(discover)하고 참고(lookup) 하기 위한 자바 API다.
DAO부분
@Log4j2
@NoArgsConstructor
public class EmpDAO {
private static DataSource dataSource;
static { // JNDI lookup을 통해
try {
// -- WAS가 생성한 DataSource 객체를 획득 하는 방법 #2----
// WAS의 표준 API인, JNDI API를 이용해서, 설정에 의해 자동생성된 데이터 소스 획득
// 1. JNDI tree 의 뿌리(root)에 접근하게 해주는 객체를 획득
Context ctx = new InitialContext(); // 100% 성공 (Web App 안에서 수행만 된다면)
//2. Context 객체를 가지고, 지정된 이름을 가지는 리소스=열매를 찾아서 획득
//java:comp/env/ 는 리소스에 접근하는 규약(루트)
dataSource =(DataSource)ctx.lookup("java:comp/env/jdbc/OracleCloudATP");
log.info("\t this.dataSource: {}",EmpDAO.dataSource);
} catch (Exception e) {
e.printStackTrace();
} // try- catch
} //static initializer
public List<EmpVO> select() throws SQLException{
log.trace("select() invoked");
// Scott 스키마의 `emp` 테이블을 모두 조회해서, 리스트로 반환
// 리스트 컬렉션 요소는 EmpVO 이어야함.
Connection conn = EmpDAO.dataSource.getConnection();
String sql = "SELECT * FROM emp ORDER BY empno";
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
//Vector 쓰는 이유는 쓰레드가 많아졌을때를 고려하여 사용한다. 안전성
List<EmpVO> list = new Vector();
try (conn; pstmt; rs;){
while(rs.next()) {
Integer empno = rs.getInt("empno");
String ename = rs.getString("ename");
String job = rs.getString("job");
Integer mgr = rs.getInt("mgr");
Date hireDate = rs.getDate("hireDate");
Double sal = rs.getDouble("sal");
Double comm = rs.getDouble("comm");
Integer deptno = rs.getInt("deptno");
EmpVO vo =new EmpVO(empno, ename, job, mgr, hireDate, sal, comm, deptno);
list.add(vo);
}//while
}// try-with-resources
return list;
} // select
}// end class
servlet
@WebServlet("/EmpSelectDAO")
public class EmpSelectDAOServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("service(req,res) invoked");
try {
EmpDAO dao =new EmpDAO();
@Cleanup("clear")
List<EmpVO> list = dao.select();
response.setContentType("text/html; charset=utf-8");
@Cleanup
PrintWriter out = response.getWriter();
out.println("<ol>");
for(EmpVO vo : list) {
out.println("<li>" +vo +"</li>");
}//for
out.println("</ol>");
out.flush();
} catch (Exception e) {
throw new IOException(e);
}
}//
}// end class
javax.sql.Source ==> Connection Pool의 모델링 타입
그럼 어떻게 데이터 소스와 풀을 만드는가?
설정을 통해서 만듬 context.xml을 META-INF 안에다가 넣어줌
<Resource
name="jdbc/OracleCloudATP" -- Pool기능을 포함하는 DataSource 객체를 얻기 위한 name값을 지정
auth="Container" -- 데이터베이스 접속 인증을 tomcat 컨테이너가 처리하도록 한다.
type="javax.sql.DataSource" -- DAO 클래스에서 실제로 사용하는 API인 DataSource를 지정
username="scott"
password="Oracle12345678"
driverClassName="oracle.jdbc.OracleDriver"
url="jdbc:oracle:thin:@db202205101319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP"
maxTotal="8" --Connection의 최대 개수를 지정
maxIdle="2" --사용 안하고 있는 커넥션의 최대수
/>
▼WAS가 생성한 DataSource 객체를 획득 하는 방법
-알아본 점
(1)WAS가 생성한 DataSource 객체를 획득 하는 방법 #1 -@Resource
(2)WAS가 생성한 DataSource 객체를 획득 하는 방법 #2 -InitialContext()
@WebServlet("/DataSource")
public class DataSourceServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// -- WAS가 생성한 DataSource 객체를 획득 하는 방법 #1---- 인젝션이라고함
// 서블릿 클래스 이외의 클래스에서는 작동이 안됌. 즉 DAO에서 동작불가
@Resource(name="jdbc/OracleCloudATP")// context.xml 설정한 정보를 어노테이션으로 불러옴
private DataSource dataSource;
@Override
public void init(ServletConfig config) throws ServletException {
log.trace("DataSource init({}) invoked",config );
super.init(config);
// -- WAS가 생성한 DataSource 객체를 획득 하는 방법 #2----
// WAS의 표준 API인, JNDI API를 이용해서, 설정에 의해 자동생성된 데이터 소스 획득
try {
// 1. JNDI tree 의 뿌리(root)에 접근하게 해주는 객체를 획득
Context ctx = new InitialContext(); // 100% 성공 (Web App 안에서 수행만 된다면)
//2. Context 객체를 가지고, 지정된 이름을 가지는 리소스=열매를 찾아서 획득
//java:comp/env/ 는 리소스에 접근하는 규약(루트)
dataSource =(DataSource)ctx.lookup("java:comp/env/jdbc/OracleCloudATP");
log.info("\t this.dataSource: {}",this.dataSource);
} catch (Exception e) {
throw new ServletException(e);
} // try- catch
}// init
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.trace("DataSource service() invoked");
try {
//Connection Pool 에서 1개의 JDBC Connection 대여
@Cleanup
Connection conn = this.dataSource.getConnection();
//~~ 사용
log.info("\t conn: {}", conn);
} catch(SQLException e) {
throw new IOException(e);
} catch (Exception e) {
throw new ServletException(e);
} //try-catch
}// service
@Override
public void destroy() {
log.trace("DataSource destroy() invoked");
}// destroy
}// end class
-데이터를 다른 logic에게 전송 및 반환할 때 효율적으로 데이터를 사용할 수 있게 클래스를 작성할 수 있는데 이 클래스를 DTO 클래스라고 함
-도메인객체(Domain Object), VO(Value Object)라고도 한다.
-멤버를 테이블 스키마와 일치 시켜서 생성 해야한다.
-domain 패키지 안에 class를 생성한다.
DTO와 VO의 차이점을 몰랐다.. 급하게 찾아보고 댓글에 적어놓음.
▼VO 예제
-알아본 점
(1)읽기 전용으로 생성 = 값이 변화면 안된다.
//VO는 읽기 전용으로만 만듬 한 번 생성되면 바뀌면 안된다.
//웹계층의 로직에 따라 화면에서 변경되기 때문에 영속성영역은 조회만 해서 알려줌
@Value //-> 따라서 매개변수 없는 생성자와 set은 없게 만듬
public class EmpVO {
//필드의 타입은 다 참조 타입으로 해야함. => 결측치로 인해
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hireDate; //import util
// private Timestamp hireDate;
// private LocalDateTime hireDate;
private Double sal;
private Double comm;
private Integer deptno;
} // end class
코드가 간결하게 변화하는 과정 @Value의 위력
우리는 servlet 관련 3개의 패턴을 익혀야함
FrontController DTO Command
들어오는 정보에 대해 일관된 방법 처리가능하고 DAO에 무분별한 접근을 해소
서블릿에서는 ‘식별값’을 비교하여 어떤 요청인지를 구별
▼식별값 사용예제
-알아본 점
(1)getRequestURI() 요청 URI얻는 메소드
(2)getContextPath() 요청 Context까지의 주소를 얻는 메소드
(3) 위에 (1),(2)를 이용하여 식별값을 얻는다는 것
(4) 요청에 대한 식별값을 if문으로 구분하여 비즈니스 로직 진행 한다는점
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>MVC, FrontController, VO, DTO, DAO 패턴 실습</h1>
<a href="insert.do">저장하기</a><br>
<a href="/08Chapter/delete.do">삭제하기</a>
<a href="http://localhost:8090/08Chapter/update.do">수정하기</a><br>
<a href="select.do">조회하기</a><br>
</body>
</html>
servlet
@WebServlet("*.do")
public class FrontControllerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String requestURI = request.getRequestURI();
String contextPath = request.getContextPath();
String command = requestURI.substring(contextPath.length());
if(command.equals("/insert.do")) {
log.info("insert 요청");
} else if(command.equals("/delete.do")) {
log.info("delete 요청");
} else if(command.equals("/updete.do")) {
log.info("updete 요청");
}else {
log.info("select 요청");
} // if -else
}// service
}// end class
DTO와 VO의 차이점
https://maenco.tistory.com/entry/Java-DTO%EC%99%80-VO%EC%9D%98-%EC%B0%A8%EC%9D%B4