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
끝.