JDBC

HoJeong Im·2022년 8월 13일
0
post-thumbnail
post-custom-banner

Apache Tomcat, Eclipse

https://tomcat.apache.org/download-90.cgi

https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/2022-06/R/eclipse-jee-2022-06-R-win32-x86_64.zip&mirror_id=1273

TOMCAT

Eclipse

  • CREATE A Dynamic Web Project

  • Tomcat 9.0

  • Dynamic Web Module Version을 2.5로 낮추어주어야 합니다.

  • CTRL+SHIFT+ +/- : 글씨 커지고 작아짐

  • CTRL + F11 : 실행 ==> JAVA Application 선택

  • ALT + SHIFT + T ==> Extract interface

Polymorphism (다형성)

  • 클라이언트에 따라 코드를 계속 수정할 것인가?

    • 성능이 떨어져도 바꾸기가 힘드니까? 그냥 써?

  • Interface : 사용하려는 메소드를 재정의해야함
    • Overriding을 해서 사용해야 함

다형성 = 상속 + 메소드재정의 + 형변환

  • 인터페이스를 상속한 자식은 반드시 메소드를 재정의해야 함

  • 자동형변환 : promotion (ex-LgTV=>TV)

  • 오래 살아남는 시스템 = 유지보수가 편한 것

  • CTRL + SPACE : 자동완성 ==> IMPORT해서 사용

  • CTRL + Click : 어떠한 Exception을 던지는 지 알 수 있음

추가 내용

  • 메소드가 예외를 던지기 때문
// new org.h2.Driver에서 에러 발생 
  • Window에서 h2Driver를 찾아야 함

  • 추가하고, 확인해보면 Libraries에 추가된 것을 확인할 수 있습니다.

  • DriverManager의 registerDriver에 에러 발생

    • try-catch를 사용해 예외를 처리해준다.
    try {
    // JDBC 1단계 : 드라이버 객체 등록
    	DriverManager.registerDriver(new org.h2.Driver());
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      	e.printStackTrace();
      }
    
  • Connection : 고속도로라고 이해

  • DriverManager.getConnection의 3가지의 parameter가 필요하게 됩니다.

  • 위 사진처럼, 3개의 정보를 복사해서 가져온다.
    1) JDBC URL, 2) ID, 3) PW

  • syso + CTRL + ENTER : 자동완성

  • 그 후에...

// JDBC 1단계 : 드라이버 객체 등록
			DriverManager.registerDriver(new org.h2.Driver());
			
			// JDBC 2단계 : Connection 연결 
			Connection conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
			
			if(conn != null)
				System.out.println("연결 성공 : " + conn.toString());
			else 
				System.out.println("연결 실패");
			

// JDBC 3단계 => Statement : 고속버스 연결 
Statement stmt = conn.createStatement();

// JDBC 4단계 => SQL(승객) 전송 
String sql = "INSERT INTO board(seq, title, writer, content) " +
					"VALUES((SELECT NVL(MAX(seq),0)+1 FROM board), " + 
					"'임시 제목', '테스터', '임시 내용....')";

JDBC
  • 3가지 정보를 가져와서, 특정 DBMS와 연결에 성공한다.

int count = stmt.executeUpdate(sql); // sql을 DBMS에 전송해주는 명령 
  • CTRL + SHIFT + O : 자동 IMPORT, 쓸데없는 IMPORT를 제거해주기도 합니다.

    • Connection, Statement는 중복이 되어 있어서 선택을 해주어야 한다.
  • CTRL + SHIFT + F : 들여쓰기

  • CTRL + / : 주석 설정 및 해제

	// JDBC 관련 변수 선언 
		Connection conn = null; 
		Statement stmt = null;
		
			
		try {
			// JDBC 1단계 : 드라이버 객체 등록
			DriverManager.registerDriver(new org.h2.Driver());
			
			// JDBC 2단계 : Connection 연결 (고속도로 연결) 
			conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
			
//			if(conn != null)
//				System.out.println("연결 성공 : " + conn.toString());
//			else 
//				System.out.println("연결 실패");
			
			// JDBC 3단계 : Statement(고속버스) 생성
			stmt = conn.createStatement();
			
			// JDBC 4단계 : SQL(승객) 전송
			String sql = "INSERT INTO board(seq, title, writer, content) " +
					"VALUES((SELECT NVL(MAX(seq),0)+1 FROM board), " + 
					"'임시 제목', '테스터', '임시 내용....')";
			
			int count = stmt.executeUpdate(sql);
			System.out.println(count + "데이터 처리 성공");
			
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("연결 실패");
		}
  • 왜 바깥으로 빼는가?

    	- 자원 해제 
    	stmt.close();
        conn.close();
  • 고속버스를 없애고 나서, 고속도로를 없애야 함

finally {
			// JDBC 5단계 : 자원 해제 
			stmt.close();
			conn.close();
		}	
  • 이렇게 하면, close되지 않고 catch로 가게 됨
try {
stmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
  • 그래서 위처럼 나누어서 해주어야 합니다.

  • DriverManager.register~~ 부분을 대체 가능
    - Class.forName("org.h2.Driver");로 가능

    • ClassNotFoundException을 예외 처리해주어야 합니다.
      • 실행을 해야만 문제가 있다는 것을 알 수 있음
    • 하지만, DriverManager.register가 더 안전함
      • 컴파일 단계에서 알려주기 때문

UPDATE 문 사용해보기

Connection conn = null; 
		Statement stmt = null;
		try{
			Class.forName("org.h2.Driver");
			conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
			stmt = conn.createStatement();
			String sql = "UPDATE board SET title = 'JAVA 어려움', content = '오늘도 놀자' WHERE seq = 2";
			int count = stmt.executeUpdate(sql);
			System.out.println(count + "데이터 처리 성공");
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("연결 실패");
		} finally {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
  • 고속버스를 더 빠르게 하자 : Statement ==> PreparedStatement

    • 상속은 확장이 가능하기 때문
    • preparedStatement가 더 빠르다
			String sql = "INSERT INTO board(seq, title, writer, content) " +
					"VALUES((SELECT NVL(MAX(seq),0)+1 FROM board), " + 
					"?, ?, ?)";
			stmt = conn.prepareStatement(sql);
  • SQL의 ?의 개수만큼 값을 포함해서 전송해야 함
String sql = "INSERT INTO board(seq, title, writer, content) " +
					"VALUES((SELECT NVL(MAX(seq),0)+1 FROM board), " + 
					"?, ?, ?)";
stmt = conn.prepareStatement(sql);
			
// SQL을 전송하기 전에 ?(파라미터)를 세팅한다. 
// 주의 : ? 갯수만큼 값을 설정해야 한다. (물론 타입도 일치해야한다.)
stmt.setString(1, "PreparedStatement 적용");
stmt.setString(2, "ㅎㅎㅎ");
stmt.setString(3, "PreparedStatement 테스트.....");
    
int count = stmt.executeUpdate();

  • conn.preparedStatement ~~~

    • 실행 순서는 왼쪽부터 1,2,3...이 됩니다.
  • 왜 preparedStatement가 Statement가 더 빠를까?

    1. SQL을 메모리에 일단 올림
    • UPDATE가 값이 매번 바뀌어서 들어올 것
    • 메모리에 계속 새로운 SQL이 들어오고 ==> 들어올 때마다 문법 체크, 테이블 확인을 반복 체크한다.
    1. preparedStatement는 ?로 되어 있어 ==> 처음에는 확인하지만, 두 번째 부터는 똑같기 때문에, 바로 실행해버림
    • 메모리 상에서 임시로 작업을 하고, 한 번 전송된 SQL이 똑같다고 판단되면 ==> 메모리에 올라간 SQL을 계속 재사용하기 떄문
    • Statement처럼 계속 확인하는 절차가 사라짐!
  • 다 문자 타입의 Column이라 setString 메서드를 사용함

    • 데이터 별로 다 존재함

    • ex) 정수 : setInt, 실수 : setDouble, 문자 : setString, 날짜 : setDate

ResultSet

  • stmt.executeUpdate() : INSERT, UPDATE, DELETE 전용 메서드

  • stmt.executeQuery() : SELECT 전용 메소드

Connection conn = null; 
		PreparedStatement stmt = null;
		ResultSet rs = null;
		
		try{
			Class.forName("org.h2.Driver");
			conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
			String sql = "SELECT * FROM board ORDER BY seq DESC";
			stmt = conn.prepareStatement(sql);
//			stmt.executeUpdate() : INSERT, UPDATE, DELETE 전용 메서드 
//			stmt.executeQuery() : SELECT 전용 메소드 
			rs = stmt.executeQuery();
            rs.next();
			rs = stmt.executeQuery();
			
			// JDBC 5단계 : 검색 결과 처리 
			
			System.out.println("[ 게시글 목록 ]");
			rs.next(); // ResultSet 객체의 커서를 밑으로 한 칸 내려준다. 
			System.out.print(rs.getInt("SEQ"));
			System.out.print(rs.getString("TITLE"));
			System.out.print(rs.getString("WRITER"));
			System.out.print(rs.getString("CONTENT"));
			System.out.print(rs.getDate("REGDATE"));
			System.out.print(rs.getInt("CNT"));
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("연결 실패");
		} finally {
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	

  • ResultSet을 쓰면 자원해제를 할 때,
    - rs.close()를 먼저하고, 나머지를 해제한다.

  • rs.next의 시작은 Before First에서 시작이기 때문에 rs.next를 한 번 실행하고 출력 후, while 루프를 계속해서 진행합니다.

  • 주의! 컬럼 이름은 대소문자를 구분하지 않습니다.

    • 하지만, 가독성을 위해서 대문자로 쓰기로 약속을 했습니다.
  • 또한, 컬럼의 위치를 기반으로 SELECT를 해도 결과가 같지만, 작성한 나만 알 수 있기 때문에

    • 처음 본 개발자한테는 알려주어야 함 ==> 절대 하지 말 것
  • +) 데이터 타입을 항상 신경써야 합니다.

Oracle로 DBMS가 바뀐다면?

  • DBMS가 바뀌었을 때, 먼저 3가지만 바꾸어주면 됩니다.


  • 나머지 코드는 그대로 ==> JDBC의 장점

  • 하지만, 반복되는 정보가 흩어져 있다면?

    • 그래서 패키지에 공통 목적으로 사용할 코드를 만들어보자
    • com.kt.biz.common 패키지 내 JDBCUtil 클래스를 만들자
    		public class JDBCUtil {
    
    		public static Connection getConnection() {
    			Connection conn = null;
    			try {
    				Class.forName("org.h2.Driver");
    				conn = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/test", "sa", "");
    
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			return conn;
    		}
    
    		public static void close(ResultSet rs, PreparedStatement stmt, Connection conn) {
    			try {
    				rs.close();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    		}
    
    		public static void close(PreparedStatement stmt, Connection conn) {
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		}

디자인 패턴

  • DAO

    • 글 목록을 보고 싶으면 위와 같은 정말 많은 코드가 필요합니다.

    • insert 후에, 글 목록을 바로 보고 싶다??

      • 직접 코드를 뒤에 추가하는 것은? 너무 지저분합니다.

      • 가독성이 엄청나게 떨어짐

    • ex) 삭제 후, 목록 보여주기 : 로직 자체는 되게 심플한테, 과정이 너무 복잡해질 수 있어

      • 비즈니스 로직 자체가 도드라지지 않아

        • => 소스코드 분석에 어려움
      • 일단 수정하고 나서, 또 수정하려면?

        • 가독성도 떨어지고, 유지보수도 어려운데?
  • 이러한 문제를 한 방에 해결할 수 있는 것이 DAO 패턴

  • DAO 클래스 : 실질적인 DB 연동을 담당하는 클래스이다.

    • 관련된 SQL을 찾아서 수정도 가능 => DB 연동 로직이 편함

    • 뭔가를 추가한다거나, 수정한다고 할 때 DAO를 찾아보자

profile
꾸준함이 제일 빠른 길이었다
post-custom-banner

0개의 댓글