D36 ~37 JDBC (MVC 분류)

Yoon Whimyong·2024년 11월 13일

20241113~1114

Template?

    • JDBC과정 중 반복적으로 쓰이는 구문들을 메소드로 정의한 클래스
  • 정의할 메소드들
    1. DB와 접속하여 Connection을 생성하는 기능.
    1. close 메소드들.
    1. commit/rollback 메소드들.
  • */
    public class JDBCTemplate {
    // 1. db접속 및 Connection 생성
    public static Connection getConnection() {
    	/*
    	 * JDBC Driver, 접속할 URL, 계정명, 비밀번호를 자바 소스코드내에
    	 * 명시적 작성하는 경우 보안상 위험할 수 있다. 
    	 * 
    	 * DB와 관려된 정보들을 별도로 보관하는 외부파일을 생성하여 보안성을 향상
    	 *  */
    	Properties prop = new Properties();
    	
    	Connection conn = null;
    	try {
    		prop.load( new FileInputStream("resources/driver.properties"));
    		
    		// 1) Driver 등록
    		Class.forName(prop.getProperty("driver"));
    		
    		// 2) Connection 생성
    		conn = DriverManager.getConnection(
    				prop.getProperty("url")
    				,prop.getProperty("username")
    				,prop.getProperty("password"));
    		// 3) autocommit설정
    		conn.setAutoCommit(false);
    	} catch (ClassNotFoundException e) {
    		e.printStackTrace();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} catch (FileNotFoundException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    	return conn;
    }
    
    // 2. jdbc용 객체들을 대신 반납해주는 메소드들.
    public static void close(Connection conn) {
    	try {
    		conn.close();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    }
    
    public static void close(Statement stmt) {
    	try {
    		stmt.close();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    }
    
    public static void close(ResultSet rset) {
    	try {
    		if(rset != null && !rset.isClosed())
    			rset.close();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    }
    
    // 3. 전달받은 Connection을 통해 대신 트랜잭션을 수행하는 메소드.
    public static void commit(Connection conn) {
    	try {
    		conn.commit();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    }
    
    public static void rollback(Connection conn) {
    	try {
    		conn.rollback();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    }

}

View : 사용자가 보게될 시각적인 요소를 담당.(화면 => 입력, 출력)

  • */
    public class MemberView {

    	private Scanner sc = new Scanner(System.in);
    	private MemberController mc = new MemberController();
    
    	public void mainMenu() {
    		while(true) {
    			System.out.println("#### 회원 프로그램 ####");
    			System.out.println("1. 회원 추가");
    			System.out.println("2. 회원 전체 조회");
    			System.out.println("3. 회원 아이디로 검색");
    			System.out.println("4. 회원 이름 검색");
    			System.out.println("5. 회원 정보 변경");
    			System.out.println("6. 회원 탈퇴 기능");
    			System.out.print("이용할 메뉴 선택 : " );
    			int menu = Integer.parseInt(sc.nextLine());
    		
    			switch(menu) {
    			case 1: insertMember(); break;
    			case 2: selectAll(); break;
    			case 3: selectByUserId(); break;
    			case 4: selectByUserName(); break;
    			case 5: updateMember(); break;
    			case 6: deleteMember(); break;
    			case 9: return;			
    			}
    		}
    	
    	
    	}
    
    	/**
    	 * 사용자에게 탈퇴할 회원의 아이디를 입력받은 후 삭제 요청하는 메소드
    	 *  */
    	private void deleteMember() {
    		System.out.print("삭제할 회원 아이디 : ");
    		String id = sc.nextLine();
    	
    		mc.deleteMember(id);
    	}
    
    	/**
    	 * 사용자에게 변경할 회원의 아이디, 변경할 정보들(비번, 이메일, 휴대폰번호, 주소)를
    	 * 입력 받은 후 변경요청하는 메소드
    	 *  */
    	private void updateMember() {
    		System.out.print("정보를 변경할 회원의 아이디 : ");
    		String id = sc.nextLine();
    	
    		System.out.print("변경할 비밀번호 : ");
    		String pwd = sc.nextLine();
    	
    		System.out.print("변경할 이메일 : ");
    		String email = sc.nextLine();
    	
    		System.out.print("변경할 핸드폰번호 : ");
    		String phone = sc.nextLine();
    	
    		System.out.print("변경할 주소 : ");
    		String address = sc.nextLine();
    	
    		mc.updateMember(id, pwd, email, phone, address);
    	}
    
    	/** 
    	 * 사용자에게 검색할 회원의 "이름"을 입력 받은 후 조회 요청하는 메소드
    	 * + 이름에 입력값이 포함된 경우 모두 조회
    	 * */
    	private void selectByUserName() {
    		System.out.print("검색할 회원 이름을 입력하세요 : ");
    		String name = sc.nextLine();
    	
    		List<Member> list = mc.selectByUserName(name);
    		for(Member m : list) {
    			System.out.println(m);
    		}
    	}
    
    	/** 
    	 * 사용자에게 검색할 회원의 아이디를 입력 받은 후 조회 요청하는 메소드.
    	 * */
    	private void selectByUserId() {
    		// view -> controller -> service -> dao -> sql
    		System.out.print("검색할 회원 아이디 : ");
    		String id = sc.nextLine();
    	
    		System.out.println(mc.selectByUserId(id));		
    	}
    
    	private void selectAll() {
    		System.out.println("----- 회원 전체 조회 -----");
    	
    		// 회원 정보 조회 요청
    		List<Member> list = mc.selectAll();
    		list.stream()
    			.forEach( m -> System.out.println(m) );
    	}
    
    	/**
    	 * 회원 추가용 화면(view)
    	 * 추가하고자 하는 회원의 정보를 입력받아 controller에 회원 추가 요청을 할 수 있는 화면.
    	 *  */
    	private void insertMember() {
    	
    		System.out.println("---- 회원 추가 ----");
    	
    		System.out.print("아이디 : ");
    		String memberId = sc.nextLine();
    	
    		System.out.print("비밀번호 : ");
    		String memberPwd = sc.nextLine();
    	
    		System.out.print("이름 : ");
    		String memberName = sc.nextLine();
    	
    		// 입력받은 정보를 통해 회원가입을 할 수 있도록, controller에게 넘긴다.
    		mc.insertMember(memberId, memberPwd, memberName);
    	}
    
    	public void displaySuccess(String string) {
    		System.out.println("서비스 요청 결과 : "+string);
    	}
    
    	public void displayFail(String string) {
    		System.out.println("서비스 요청 결과 : "+string);
    	}

}

DAO(Data Access Object)

    • Service에 의해 호출되며, 맡은 기능을 수행하기 위해 db에 직접 접근하여 sql문을 호출한 후
  • 처리 결과값을 반환시켜주는 객체. *

  • */
    public class MemberDao {

    private Properties prop = new Properties();
    
    {
    	try {
    		prop.loadFromXML(new FileInputStream("resources/query.xml"));
    	} catch (InvalidPropertiesFormatException e) {
    		e.printStackTrace();
    	} catch (FileNotFoundException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
    }
    
    public int insertMember(Connection conn, Member m) {
    	
    	// 1) 변수들 초기화
    	int result = 0;
    	PreparedStatement pstmt = null;
    	String sql = prop.getProperty("insertMember");
    	try {
    		pstmt = conn.prepareStatement(sql);
    		
    		pstmt.setString(1, m.getMemberId());
    		pstmt.setString(2, m.getMemberPwd());
    		pstmt.setString(3, m.getMemberName());
    		
    		result = pstmt.executeUpdate();
    		
    		//pstmt = conn.prepareStatement("");
    		//result = pstmt.executeUpdate();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} finally {
    		JDBCTemplate.close(pstmt);
    	}
    	
    	return result;		
    }
    
    public List<Member> selectAll(Connection conn) {
    	// select -> ResultSet
    	List<Member> list = new ArrayList<>(); // 반환시킬 값들을 저장할 컬렉션
    	PreparedStatement pstmt = null;
    	ResultSet rset = null;
    	String sql = prop.getProperty("selectAll");
    	
    	try {
    		pstmt = conn.prepareStatement(sql);
    		rset = pstmt.executeQuery();
    		
    		while(rset.next()) {
    			Member m = new Member();
    			m.setMemberId(rset.getString("MEMBER_ID"));
    			m.setMemberName(rset.getString("MEMBER_NAME"));
    			m.setEnrollDate(rset.getDate("ENROLL_DATE"));
    			list.add(m);
    		}
    		
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} finally {
    		JDBCTemplate.close(rset);
    		JDBCTemplate.close(pstmt);
    	}
    	
    	return list;
    }
    
    public Member selectByUserId(Connection conn, String id) {
    	
    	Member m = null;
    	PreparedStatement pstmt = null;
    	ResultSet rset = null;
    	String sql = prop.getProperty("selectByUserId");
    	
    	try {
    		pstmt = conn.prepareStatement(sql);
    		pstmt.setString(1, id);
    		
    		rset = pstmt.executeQuery();
    		
    		if(rset.next()) {
    			m = new Member();
    			m.setMemberName( rset.getString("MEMBER_NAME")  );
    			m.setAge( rset.getInt("AGE") );
    			m.setPhone( rset.getString("PHONE") );
    		}
    		
    		
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    	
    	return m;
    }
    
    public List<Member> selectByUserName(Connection conn, String name) {
    	List<Member> list = new ArrayList();
    	Member m = null;
    	PreparedStatement pstmt = null;
    	ResultSet rset = null;
    	String sql = prop.getProperty("selectByUserName");
    	
    	try {
    		pstmt = conn.prepareStatement(sql);
    		pstmt.setString(1, name);
    		
    		rset = pstmt.executeQuery();
    		
    		while(rset.next()) {
    			m = new Member();
    			m.setMemberName( rset.getString("MEMBER_NAME")  );
    			m.setAge( rset.getInt("AGE") );
    			m.setPhone( rset.getString("PHONE") );
    			
    			list.add(m);
    		}
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} finally {
    		JDBCTemplate.close(rset);
    		JDBCTemplate.close(pstmt);
    	}
    	return list;
    }
    
    public int updateMember(Connection conn, Member m) {
    	int updateCount = 0;
    	PreparedStatement pstmt = null;
    	String sql = prop.getProperty("updateMember");
    	
    	try {
    		pstmt = conn.prepareStatement(sql);
    		pstmt.setString(1, m.getMemberPwd());
    		pstmt.setString(2, m.getPhone());
    		pstmt.setString(3, m.getEmail());
    		pstmt.setString(4, m.getAddress());
    		pstmt.setString(5, m.getMemberId());
    		
    		updateCount = pstmt.executeUpdate();
    		
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} finally {
    		JDBCTemplate.close(pstmt);
    	}
    	return updateCount;
    }
    
    public int deleteMember(Connection conn, String id) {
    	int updateCount = 0;
    	PreparedStatement pstmt = null;
    	String sql = prop.getProperty("deleteMember");
    	
    	try {
    		pstmt = conn.prepareStatement(sql);
    		pstmt.setString(1, id);
    		
    		updateCount = pstmt.executeUpdate();
    		
    	} catch (SQLException e) {
    		e.printStackTrace();
    	} finally {
    		JDBCTemplate.close(pstmt);
    	}
    	return updateCount;
    }
    
    
    //JDBC클래스 안의 *(모든) 스테틱

    import static com.kh.chap03_MVC.model.template.JDBCTemplate.close;
    import static com.kh.chap03_MVC.model.template.JDBCTemplate.commit;
    import static com.kh.chap03_MVC.model.template.JDBCTemplate.getConnection;
    import static com.kh.chap03_MVC.model.template.JDBCTemplate.rollback;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

import com.kh.chap03_MVC.model.dao.MemberDao;
import com.kh.chap04_template.refactoring.Template;
import com.kh.jdbc.model.vo.Member;

Service

    • 컨트롤러에 의해 실행되는 메소드들
    • Dao에 존재하는 메소드들을 호출하여 논리적으로 연관이 있는 여러개의 메소드들을 묶음으로 호출할 수 있다.
    • 처리 결과값을 항상 컨트롤러에게 반환해주는 역할을 한다.
  • */
    public class MemberService {

    private MemberDao dao = new MemberDao();
    
    public int insertMember(Member m) {
    	// 커넥션 객체 생성
    	Connection conn = getConnection();
    	// 회원가입 서비스
    	// Member테이블에 1행의 데이터(m) 추가
    	int rows = dao.insertMember(conn, m);
    	
    	// 사용자의 회원가입 일자를 저장하는 테이블이 있다고 가정
    	// MemberEnroll 테이블에 1행의 데이터 추가
    	// rows = dao.insertMemberEnroll(conn, m);
    	
    	// 사용자의 포인트 정보를 보관하는 테이블이 있다고 가정
    	// MemberPoint 테이블에 1행의 데이터 추가.
    	// rows = dao.insertMemberPoint(conn, m);
    	
    	if(rows > 0) {
    		commit(conn);
    	}else {
    		rollback(conn);
    	}
    	// 커넥션 반환
    	close(conn);
    	
    	return rows;
    }
    
    public List<Member> selectAll() {
    	Connection conn = null;
    	try {
    		conn = Template.getConnection();
    	} catch (FileNotFoundException e) {
    		e.printStackTrace();
    	} catch (IOException e) {
    		e.printStackTrace();
    	} catch (SQLException e) {
    		e.printStackTrace();
    	}
    	
    	List<Member> list = dao.selectAll(conn);
    	
    	return list;
    }
    
    public Member selectByUserId(String id) {
    	Connection conn = getConnection();
    	
    	Member m = dao.selectByUserId(conn, id);
    	
    	return m;
    }
    
    public List<Member> selectByUserName(String name) {
    	Connection conn = getConnection();
    	
    	List<Member> list = dao.selectByUserName(conn, name);
    	
    	close(conn);
    	
    	return list;
    }
    
    public int updateMember(Member m) {
    	Connection conn = getConnection();
    	
    	int result = dao.updateMember(conn, m);
    	
    	if(result > 0) {
    		commit(conn);
    	}else {
    		rollback(conn);
    	}
    	return result;
    }
    
    public int deleteMember(String id) {
    	Connection conn = getConnection();
    	
    	int updateCount = dao.deleteMember(conn , id);
    	
    	if(updateCount == 1) {
    		commit(conn);
    	}else {
    		rollback(conn);
    	}
    	
    	return updateCount;
    }

    }

chap4 Refactoring

/*
Refactoring
- 기존의 코드를 변경하여 코드를 더 이해하기 쉽고, 유지보수하기 쉽게 만드는 과정.
- 코드의 중복을 제거, 공용메서드나 클래스 추출, 변수의 이름을 직관성 있게 변경작업 등,
코드의 가독성과 유지보수성을 향상시키는 수행작업.
* */

/*	
 * Connection 객체 생성 방식
 * 1) 자바 어플리케이션에서 DBMS의 드라이버를 통해 커넥션을 조회.
 * 2) DBMS 드라이버는 DBMS와 TCP/IP 커넥션을 연결한다. (3 way handshake 발생)
 * 		1. 어플리케이션에서 DB에 접속요청
 * 		2. DB는 요청을 받고 요청 수락메시지를 보낸다.
 * 		3. 어플리케이션에서는 DB 서버에 다시 접속한다는 요청을 보낸다. 이떄 아이디,비밀번호를 전달한다
 * 3) DBMS드라이버는 TCP/IP커넥션이 연결되면 아이디와,패스워드 기타 부가정보를 DBMS에 전달
 * 4) 오라클서버는 아이디, 패스워드를 통해 인증작업을 거친 후 DBMS커넥션을 생성
 * 5) 오라클서버는 커넥션 생성이 완료되었다는 응답 메시지를 보낸다
 * 6) DBMS드라이버는 커넥션 객체를 생성해서 클라이언트에게 반환해준다.
 * 
 * 미리 커넥션을 만들어두고(커넥션풀) 요청시마다 공급
 * 
 * 커넥션은 객체를 생성 및 종료하는 작업은 시간이 많이 드는 작업이며, 커넥션 객체를 자주 생성하는
 * 것을 효율적이지 못하다.
 * 객체를 자주 생성하는 것이 문제, 객체를 사전에 미리 생성(준비)해두고 필요 할 떄 마다 사용하는 방식
 * 커넥션풀(Connection Pool)
 * */


/*
 * 커넥션풀-Connection Pool
 * 	- 커넥션 객체를 미리 생성하여 보관해두는 커넥션 저장소.
 * 	- 커넥션객체를 생성하는 작업은 비용이 많이 드는 작업이므로, 사용자의 요청이 있을 때마다 생성하는것이 
 * 		아니라 미리 커넥션풀에 생성해 두고, 사용자의 요청시마다 생성해둔 커넥션을 전달해주는 방식이다.
 *  - 대표 커넥션풀 구현해둔 대표 라이브러리 : DBCP, HikariCP, Tomcat DataSource
 *  - DBCP 라이브러리 다운로드 방법
 *  	1) 메이븐 레파지토리 이용
 *  	2) Apache 공식 사이트 다운로드 
 * */

public static Connection getConnection() throws SQLException, FileNotFoundException, IOException {
	
	Connection conn = null;
	/*
	 * BasicDataSource 
	 * 	- DBMS 와의 연결 커넥션풀 관리, 트랙잭션 관리 등의 기능을 구현한 클래스
	 * 	- 데이터베이스에 연결 및 커넥션 풀 생성. 쿼넥션 생성 및 소멸 관리와 예외처리등을 
	 * 		효율적으로 수행하는 메서드를 제공한다
	 * */
	BasicDataSource dataSource = new BasicDataSource();

	Properties prop = new Properties();
	
	
		prop.load(new FileInputStream("resource/driver.properties"));
	
		//생성하고자 하는 커넥션에 대한 정보를 기술.
		dataSource.setDriverClassName(prop.getProperty("driver"));
		dataSource.setUrl(prop.getProperty("url"));
		dataSource.setUsername(prop.getProperty("username"));
		dataSource.setPassword(prop.getProperty("password"));
		
		//커넥션풀 설정
		dataSource.setInitialSize(10); //초기 커네션풀의 사이즈.
		dataSource.setMaxTotal(50); // 커넥션풀의 최대 사이즈.
		dataSource.setDefaultAutoCommit(false); // 커넥션들의 자동커밋 설정
		dataSource.setMaxWaitMillis(10000); // 커넥션풀에서 커넥션이 없는경우 대기 최대시간 설정
		dataSource.setRemoveAbandonedTimeout(300); //사용하고 있지 않은 커넥션

		conn = dataSource.getConnection();
		
		return conn;
		
		/* --적절한 풀을 생성하는게 중요하다.
		 * 커넥션풀의 장접 
		 * 	- 성능향상 : 커넥션풀은 데이터베이스와 미리 연결을 완료한 Connection객체를 생성해두고,
		 * 	풀에 유지 시킴으로써 어플리케이션의 향상시킬 수 있다.
		 * 	- 메모리 누수 방지 : 사용하고 닫아두지 않은 커넥션들을 일정시간이 지났을때 
		 * 		             자동으로 삭제시키는 기능이 추가되어 있다. 
		 * 	- 확장성 : 커넥션풀 클래스에서 만들어 높은 다양한 기능들을 추가하여 
		 * 			 원하는 형태의 커넥션들을 생성할수 있다
		 * 
		 * 커넥션풀의 단점
		 * 	- 설정이 번거롭다.
		 * 	- 초기 로딩이 오래 걸린다
		 * 	- 메모리를 항상 점유하고 있으므로 낭비가 심하다.
		 * */

}

}

0개의 댓글