m : model → 데이터를 관리하는 기능/역할을 하는 클래스를 모아놓은 것
v : veiw → 사용자에게 보여주는 화면을 출력하는 역할/기능 클래스들
c : controller → 클라이언트(프로그램 이용자)가 요청한 서비스를 진행하는 역할/기능하는 클래스들
💡 클래스를 설계할 때는 하나의 클래스는 하나의 역할을 수행한다는 단일 책임의 원칙을 따른다.
객체 단일 책임의 원칙에 따라 각 책임을 구분하기 위해서 MVC에 맞춰서 패키지를 구분해서 구조를 짠다.
패키지를 구분하면 각 역할에 따라 나뉘어있기 때문에 해당 역할에 문제가 생겼을 경우 유지 보수가 편리해진다. 또한, 동일한 이름의 클래스가 있다고 하더라도 패키지로 구분할 수 있기 때문에 클래스명의 충돌을 방지할 수 있다.
패키지 이름을 모두 소문자로 정의하고, 마침표로 구분한다는 것이 패키지 명명 규칙이다. 패키지의 구분은 필요에 따라 나눌 수 있고 이 필요는 구현하려는 서비스에 따라, 회사의 틀에 따라 달라질 수 있다.
애플리케이션 실행을 위해 main 메소드를 갖고 있는 패키지
사용자에게 보여주는 화면을 구현하는 패키지
View에서 전달 받은 데이터를 처리해서 DAO에 전달하고 DAO로부터 받은 결과에 따라 View를 결정하여 인코딩 후 데이터를 전송하는 패키지
DBMS에서 전송된 데이터나 전송할 데이터를 보관할 수 있는 클래스, DMBS와 통신하면서 데이터를 주고 받는 클래스를 포함한 패키지로 DTO, DAO, VO로 구분한다.
값 그 자체를 갖는 패키지로 로직을 구현할 수 있다. getter만 존재하는 데이터(읽기 전용)를 저장해야 값의 신뢰성이 보장되는 패키지로 데이터를 저장하기 위한 틀을 갖는 느낌의 DTO와는 다른 점이 있다.
DB의 각 컬럼 개체 저장용 클래스가 있는 패키지가 있고 도메인과 일치하지 않으며 어떤 로직에 사용되는 영속적이지 않은 정보를 구현한다. 단순하게 말해서 로직이 없는 순수한 데이터 자체를 갖는 패키지라고 볼 수 있다.
💡보통 DTO, VO 모두 데이터를 저장하는 객체를 위한 패키지로 구분해서 사용하지 않는다.
말 그대로 DBMS에 접속하여 실제 데이터를 입출력 받는 클래스가 있는 패키지를 말한다. Repository라고도 하며 DB와 Service의 연결고리 역할을 한다. 각 패키지를 구조화함에 따라 Controller에서 DB의 데이터를 불러와서 바로 사용하지 않고 DB의 데이터에 접근할 때는 DAO를 사용하게 된다.
SQL구문에 변수를 대입할 때 문자열에 리터럴 형태에 맞춰서 작성해야 하기 때문에 테이블의 컬럼이 많아 자료가 복잡해질수록 번거롭다는 단점이 있다. 이 때, 변수를 자료형에 맞춰서 대입할 수 있는 PreparedStatement 이용할 수 있다.
PreparedStatemnet는 Statement를 상속 받은 인터페이스로 new 생성자를 사용하지 않고 Connection의 prepareStatement(”SQL문”)
메소드를 이용해서 사용한다.
Statement와 다르게 SQL문에 물음표(위치홀더)로 변수의 위치를 지정해두고 해당 SQL문을 생성할 때부터 쿼리문을 대입해서 사용해야 한다.
위치홀더(placeholder) 표시에 값을 대입할 때는 set자료형(위치값, 대입값)
메소드 사용한다. 위치홀더는 문자열의 왼쪽부터 인덱스 번호가 부여된다. 해당 문장 역시 SQL문이기 때문에 인덱스 번호의 시작은 1부터 한다.
💡위치홀더를 표시한 값이 존재하면 반드시 값을 대입해줘야 한다. 대입하지 않으면 예외가 발생한다.
⚠️ORA-17041: 인덱스에서 누락된 IN 또는 OUT 매개변수:: 1 →해당 인덱스 자리의 매개변수에 값이 없다.
//입력 받은 ID와 일치하는 객체를 찾아 반환하는 메소드
public MemberDto selectMemberById(Connection conn, String id) {
PreparedStatement pstmt=null;
ResultSet rs=null;
MemberDto m=null;
//SQL문의 변수 대입 위치에 ? 작성
String sql="SELECT * FROM MEMBER WHERE MEMBER_ID=?";
try {
//생성할 때부터 쿼리문을 대입
pstmt=conn.prepareStatement(sql);
//위치 값, 대입 값을 타입에 맞는 메소드에 대입
pstmt.setString(1, id);
//위에서 작성한 pstmt가 SQL문을 갖고 있기 때문에 그냥 실행하면 된다.
rs=pstmt.executeQuery();
//반환 받을 값이 하나인 경우 if문을 사용해서 출력
if(rs.next()) m=getMember(rs);
}catch(Exception e) {
e.printStackTrace();
}finally {
try {
if(rs!=null) rs.close();
if(stmt!=null) stmt.close();
if(conn!=null) conn.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
return m;
}