72일차 JDBC

쿠우·2022년 7월 7일
0

JDBC는 java Database Connectivity
자바 언어로 관계형 데이터베이스에 접속해 SQL문 수행가능

Jdbc를 사용하려면 Driver와 API가 필요하다.

JDBC Driver는 프린트 드라이버와 거의 동일하다
JDBC API를 사용하기위해 JDBC Driver가 필수적이다.

- 연동과정
JDBC프로그램 - JDBC 인터페이스 - JDBC드라이버 - DB

JDBC API = java.sql 패키지
JDBC Driver = java.sql 패키지에 인터페이스를 구현한 몸통부분

▲메이븐에서 pom.xml 부분 JDBC드라이버를 알아서 관리해줌 굿!
->원래는 따로 다운받고 관리해줘야함

▼ JDBC API 사용순서

(0) java.sql.DriverManager (Class) -- 1번의 선행조건
(1) java.sql.Connection (interface) -- 2,3번 생성의 선행조건 / 네트워크상의 연결 그자체를 의미한다. / 자바와 DB의 사이에 연결된 길이라 생각하라
(2) java.sql.Statement (interface) ---> Dynamic SQL (잘 사용하면 안됨!!!--> 성능을 떨어뜨림) / DB쪽에서 처리된 결과를 프로그램쪽에 전달(데이터 전달)
(3) java.sql.PreparedStatement (interface) --> Prepared SQL // Statement와 같은 기능을 수행하지만 ? 기호를 쓸 수 있다는 장점 가독성과 유지보수성이 좋음
(4) java.sql.ResultSet (interace) -->2,3번이 있어야 사용가능
(5) java.sql.SQLException (class) - Checked Exception (쳌드오류를 확인)

  • 위에서, (1) ~ (4) 까지는 자원객체(Resources) 임 =>
    때문에, 다 사용하고 나면, 반드시 자원해제(close) 해줘야됨!
    이때 , 자원객체를 닫는 순서가 정해져 있고, 이를 반드시 지켜야 됨! :
    (4) > (2) 또는 (3) > (1)

메소드와 객체에 대해서 사용 설명은 아래의 코드들에 주석으로 적혀있다.
또한 사용에 있어서 간략화하는 과정을 5단계로 나누고 6번 예제는 응용

▼최대한 모든 객체와 메서드 다 적용해서 API설명해놓음


@Log4j2
public class JDBCExample1 {
    								// 오라클 프로토콜 :@ 서버주소 /서버포트 / DB이름 -> 아래는 지갑과 별칭과 URL로 표시
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
	private static String driverClass = "oracle.jdbc.OracleDriver";
	private static String user = "HR";
	private static String pass = "Oracle12345678";
	
	
	
	
//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	log.trace("main({}) invoked.", Arrays.toString(args) );
    	
    	// Step.2 : JDBC를 위한 지역변수 선언
    	Connection conn =null; 
    	Statement stmt = null;  
    	ResultSet rs = null;
    	
    	
    	try {
    	
	    	// Step.3 : JDBC Driver Class를 등록 (registration)= 드라이버 로딩이라고함
	    	Class.forName(driverClass); // 정적 메소드여서 정적 멤버여야함
	    	
	    	// Step.4 : 연결하기
	    	conn = DriverManager.getConnection(jdbcUrl, user, pass);
	    	
	    	log.info("\t  + conn : " ,  conn);
	    	
	    	// Step 5: Connection 객체로부터, Statement 객체를 생성할 수 있다! 
	    	stmt = conn.createStatement();
	    	
	    	// Step6: 실제 수행시킬 , SQL 문장 생성
	    	String sql = "SELECT current_date FROM dual";
	    	
	    	
	    	
	    	//Step 7: Statement 실행 (SQL문장을 전송)
	    	rs = stmt.executeQuery(sql); //실행 메소드를 통해 실행 if DQL(SELECT문장만 해당)
	    	
	    	
	    	//executeQuery()  -> sql ReusltSet 결과값을 가진 객체인 ReusltSet를 반환
	    	
	    	//-ReusltSet객체의 메소드 
	    	//afterLast() -> 맨 뒤 빈행으로 
	    	//beforeFirst() -> 맨 앞 빈행으로
	    	//next() -> 다음 행
	    	//getString().... 등등  - 값 가져오기
	    	
	    	//executeUpdate() -> 반환하는 int값을 표시 즉, 변경된 레코드 개수
	    	
	    	
	    	
	    	//Step 8: ResultSet을 이용해서, 각 레코드별로, 각 컬럼의 값 추출
	    	if(rs.next()) {
	    		//현재 레코드의 구성 칼럼의 값을 추출
	    		//rs.get  < 추출컬럼타입에 대응되는 자바타입> ("컬럼명");
	    		Timestamp now = rs.getTimestamp("current_date");
	        	log.info("\t  + now : " ,  now);
	    	} // while
    	
	    	
    	} catch(SQLException | ClassNotFoundException e ) {
    		e.printStackTrace();
    	
    	
    	
    	} finally {
    		
	    	//Step 9 : 자원객체를 모두 해제(순서가 중요)
	    	// 순서: ResultSet > Statement > Connection
	    	try { if(rs != null && !rs.isClosed())rs.close(); 
	    	// 2번 호출하면 안되니까 닫혔는지도 확인한다.
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
    	
	    	try { if(stmt != null && !stmt.isClosed() )stmt.close();
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
	    	
	    	try { if(conn != null && !conn.isClosed())conn.close();
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
	    	
    	}// try- catch- finally
		
    	
    	
    }//main
}// end class

▼ Class.forName() 제거

Class.forName 메서드는 런타임 시점에 해당 경로의 클래스를 동적으로 로드한다. JDK 6 이후엔 Class.forName() 구문을 적지 않아도 된다. java.sql.Driver 인터페이스의 벤더별 구현 클래스를 모두 로드한다. 그래서 Class.forName으로 로드할 필요가 없다.



@Log4j2
public class JDBCExample2 {
    	//driverClass /ForName 을 사용하지 않는다 
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
//	private static String driverClass = "oracle.jdbc.OracleDriver";
	private static String user = "HR";
	private static String pass = "Oracle12345678";

//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	log.trace("main({}) invoked.", Arrays.toString(args) );
    	
    	// Step.2 : JDBC를 위한 지역변수 선언
    	Connection conn =null;
    	Statement stmt = null;
    	ResultSet rs = null;
    	
    	
    	try {
    	
	    	// Step.3 : JDBC Driver Class를 등록 (registration)
//	    	Class.forName(driverClass); // 정적 메소드여서 정적 멤버여야함
	    	
	    	// Step.4 : 연결하기
	    	conn = DriverManager.getConnection(jdbcUrl, user, pass);
	    	
	    	log.info("\t  + conn : {} " ,  conn);
	    	
	    	// Step 5: Connection 객체로부터, Statement 객체를 생성할 수 있다!
	    	stmt = conn.createStatement();
	    	
	    	// Step6: 실제 수행시킬 , SQL 문장 생성
	    	String sql = "SELECT current_date \r\n" +  // 여러줄을 넣게되면 개행 문자 넣어주기
	    				 "FROM dual";
	    	
	    	//Step 7: Statement 실행 (SQL문장을 전송)
	    	rs = stmt.executeQuery(sql); //실행 메소드를 통해 실행 if DQL(SELECT문장만 해당)
	    	
	    	//Step 8: ResultSet을 이용해서, 각 레코드별로, 각 컬럼의 값 추출
	    	if(rs.next()) {
	    		//현재 레코드의 구성 칼럼의 값을 추출
	    		//rs.get  < 추출컬럼타입에 대응되는 자바타입> ("컬럼명");
	    		Timestamp now = rs.getTimestamp("current_date");
	        	log.info("\t  + now : {} " ,  now);
	    	} // while
    	
    	} catch(SQLException e ) {
    		e.printStackTrace();
    	} finally {
    		
	    	//Step 9 : 자원객체를 모두 해제(순서가 중요)
	    	// 순서: ResultSet > Statement > Connection
	    	try { 
	    		//if(rs != null && !rs.isClosed())
	    		//assert rs != null && !rs.isClosed(); // 언어상의 문법 참이면 그냥 지나간다 아니면 에러발생 
	    		Objects.requireNonNull(rs); // 인자값이 없으면 null포인트 에러를 보낸다
	    		rs.close(); 
	    	// 2번 호출하면 안되니까 닫혔는지도 확인한다.
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
    	
	    	try { if(stmt != null && !stmt.isClosed() )stmt.close();
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
	    	
	    	try { if(conn != null && !conn.isClosed())conn.close();
	    	} catch(SQLException e) {
	    		;;
	    	}// try-catch
	    	
    	}// try- catch- finally
		
    	
    	
    }//main
}// end class


▼ try with resources 블럭으로 자원체계 단순화



@Log4j2
public class JDBCExample3 {
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
	private static String user = "HR";
	private static String pass = "Oracle12345678";

//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	
    	try {
    	
    		Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
	    	Statement stmt = conn.createStatement();
	    	
	    	String sql = "SELECT current_date \r\n" + "FROM dual";
	    	ResultSet rs = stmt.executeQuery(sql); 
	    	
	    	//자동으로 닫힐 자원 객체를 나열  (생성의 반대의 순서로 닫아야함)
	    	// 닫는순서 ( <- )
	    	try(conn; stmt; rs){
	    		
	    		if(rs.next()) {
	    			Timestamp now = rs.getTimestamp("current_date");
	    			log.info("\t  + now : {} " ,  now);
	    		} // if

	    	} // try with resources
    	} catch(SQLException e ) {
    		e.printStackTrace();
    	} // try- catch
		
    	
    	
    }//main
}// end class



▼lombok의 Cleanup 어노테이션 사용으로 더 단순화


@Log4j2
public class JDBCExample4 {
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
	private static String user = "HR";
	private static String pass = "Oracle12345678";
	
//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	
    	try {
//    		@Cleanup("close") <-("자원객체를 닫는 메소드의 이름지정 - 기본은 close")
    		// 순서는 생성 반대로 닫힘
    		@Cleanup Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
    		@Cleanup Statement stmt = conn.createStatement();
	    	
	    	String sql = "SELECT current_date \r\n" + "FROM dual";
	    	@Cleanup ResultSet rs = stmt.executeQuery(sql); 
	    		
	    		if(rs.next()) {
	    			Timestamp now = rs.getTimestamp("current_date");
	    			log.info("\t  + now : {} " ,  now);
	    		} // if

    	} catch(SQLException e ) {
    		e.printStackTrace();
    	} // try- catch
		
    }//main
}// end class

▼ PreparedStatement의 ? 이용해 가독성 유지보수성 향상



@Log4j2
public class JDBCExample5 {
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
	private static String user = "HR";
	private static String pass = "Oracle12345678";
	
//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	
    	try {
    		@Cleanup Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
    		// 1. Dynamic SQL(동적인 SQL) => Database에 상당한 부담을 줌 어쩔 수 없는 경우에만 씀
//    		@Cleanup Statement stmt = conn.createStatement(); // Dynamic SQL에 대한 문장생성
	    	
    		 
    		// 2. prepared SQL(정적인 SQL) => DB에 최적화 된 처리가능하게 함// 물음표의 값에 따라 삭제되는 부분이 변함 
	    	String sql = "DELETE FROM tbl_emp WHERE employee_id = ? ";   // ?: Bind Variable 이라고 부름
	    	PreparedStatement pstmt = conn.prepareStatement(sql);
	    	
	    	
	    	// 3. Bind Variable에 값 설정 (Binding)
	    	pstmt.setInt(1,167); 
	    	
	    	
	    	int affectedLines = pstmt.executeUpdate();
	    	
	    	
	    	log.info("\t 삭제된 라인수를 반환: {}" , affectedLines);
	    	
    	} catch(SQLException e ) {
    		e.printStackTrace();
    	} // try- catch
		
    }//main
}// end class



▼ rs.next() ,rs.get~~() 사용을 통한 데이터뽑기 응용


@Log4j2
public class JDBCExample6 {
	private static String jdbcUrl = "jdbc:oracle:thin:@db20220510181319_high?TNS_ADMIN=C:/opt/OracleCloudWallet/ATP";
	private static String user = "HR";
	private static String pass = "Oracle12345678";
	
//	public static void main( String[] args ) throws ClassNotFoundException, SQLException{
	public static void main( String[] args ) {
    	
    	try {
    		@Cleanup Connection conn = DriverManager.getConnection(jdbcUrl, user, pass);
	    	
    		 
    		// 1. 문제에 맞는 쿼리 설정
	    	String sql = "SELECT * FROM employees WHERE salary >= ? AND department_id >= ? ";   // ?: Bind Variable 이라고 부름
	    	PreparedStatement pstmt = conn.prepareStatement(sql);
	    	
	    	// 2. Bind Variable에 값 설정 (Binding)
	    	pstmt.setDouble(1,7000); // 첫번째 물음표에 7000대입 
	    	pstmt.setInt(2,50); // 두번째 물음표에 50 대입
	    	
	    	
	    	// DQL 조건에서 얻어낸 SELECT 결과값 저장
	    	@Cleanup ResultSet rs =pstmt.executeQuery();
	    	
            //결과에서 행을 다음으로 넘기며 해당 속성값을 출력한다.
	    	while(rs.next()) {
	    		Integer EMPLOYEE_ID 	= rs.getInt("EMPLOYEE_ID");
	    		String FIRST_NAME   	= rs.getString("FIRST_NAME");
	    		String LAST_NAME 		= rs.getString("LAST_NAME");
	    		String EMAIL 			= rs.getString("EMAIL");
	    		String JOB_ID 			= rs.getString("JOB_ID");
	    		String PHONE_NUMBER 	= rs.getString("PHONE_NUMBER");
	    		Double SALARY 			= rs.getDouble("SALARY");
	    		Date HIRE_DATE 			= rs.getDate("HIRE_DATE");
	    		Double COMMISSION_PCT 	= rs.getDouble("COMMISSION_PCT");
	    		Integer MANAGER_ID 		= rs.getInt("MANAGER_ID");
	    		Integer DEPARTMENT_ID 	= rs.getInt("DEPARTMENT_ID");
	    	
	    		String employee = 
	    			String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",EMPLOYEE_ID,FIRST_NAME,LAST_NAME, EMAIL, 
	    					JOB_ID, PHONE_NUMBER, SALARY ,HIRE_DATE ,COMMISSION_PCT, MANAGER_ID, DEPARTMENT_ID );
	    		
	    		
	    	log.info("Employee: {}" , employee);
    		}// while
    	} catch(SQLException e ) {
    		e.printStackTrace();
    	} // try- catch
		
    }//main
}// end class

끝.

profile
일단 흐자

0개의 댓글