80일차 Servlet DAO,VO

쿠우·2022년 7월 20일
0

데이터베이스를 연동하는 프로그램을 개발할 때 반드시 사용되는 2 가지 개발 패턴
DAO와 DTO가 있다.

Servelet과 JDBC 연동

웹 3계층

  • Persistence - 영구적 영속적으로 속해서 DB를 조작하는 개체
    ->DAO 객체, Mapper

  • Business - 비즈니스 로직을 수행하는 모든 객체
    ->서비스 객체

  • Presentation - Spring MVC 모델(화면에 보이는 영역)

(Persistence(DB) - Business -Presentation)

*디자인 패턴
특수한 기능을 수행하는 클래스를 최소개수로 설계하는 방법을 의미

- DAO

-모듈화 시켜 개발하게 되며 모듈화한 클래스들중에서 데이터베이스 처리하는 코드만을 관리하는 클래스를 작성할 수 있는데 이 클래스 파일을 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

- DAO를 하기 위해서는 DataSource 객체를 사용하는데 이를 정의해둬야함

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

- DTO

-데이터를 다른 logic에게 전송 및 반환할 때 효율적으로 데이터를 사용할 수 있게 클래스를 작성할 수 있는데 이 클래스를 DTO 클래스라고 함

-도메인객체(Domain Object), VO(Value Object)라고도 한다.

-멤버를 테이블 스키마와 일치 시켜서 생성 해야한다.

-domain 패키지 안에 class를 생성한다.

DTO와 VO의 차이점을 몰랐다.. 급하게 찾아보고 댓글에 적어놓음.

▼VO 예제
-알아본 점
(1)읽기 전용으로 생성 = 값이 변화면 안된다.

  • VO는 값 그 자체를 나태는 객체이다.DTO와 반대로 로직을 포함할 수 있으며, VO의 경우 특정 값 자체를 표현하기 때문에 불변성의 보장을 위해 생성자를 사용하여야 한다.
    (2)생성자를 통해 단 한번만 초기화하도록 작성
    (3)@Value의 위력

//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 고급

우리는 servlet 관련 3개의 패턴을 익혀야함
FrontController DTO Command

FrontController

(1)FrontController란 무엇인가?

  • 웹 어플리케이션 개발시 사용자의 요청을 처리하기 위한 최초 진입점(initial point)을 정의하
    고 사용하는 패턴을 의미
  • 요청을 분산시켜 발생되는 중복된 코드를 제거 / 요청을 일관된 방법으로 관리할 수 있는 장점

(2)FrontController를 사용했을 때와 안했을 때 차이점

  • 무분별한 요청과 DAO 부하의 관하여

들어오는 정보에 대해 일관된 방법 처리가능하고 DAO에 무분별한 접근을 해소

(3)식별값이란

  • FrontController 패턴의 고려사항은 사용자가 어떤 동작을 요청 했는지를 식별하는 것
  • 확장자 패턴은 지금은 잘안쓰지만 알고는 있자 = (*.do)
  • context명을 얻고 / 식별값을 얻는 메소드와 방법

서블릿에서는 ‘식별값’을 비교하여 어떤 요청인지를 구별

▼식별값 사용예제
-알아본 점
(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

profile
일단 흐자

1개의 댓글