JDBC(Java DataBase Connect) 프로그래밍

웅평·2024년 1월 7일
0

JDBC란?

JDBC(Java DataBase Connectivity)는 자바에서 표준화된 방법으로 데이터베이스에 접근할 수 있도록 하는 프로그래밍 API이다. java.sql 패키지로 제공되는 JDBC API는 DBMS 종류에 상관없이 사용 가능하도록 대부분 몸체가 없는 인터페이스로 선언되어 있으며, 각 DBMS 회사에서는 이 인터페이스들을 구현한 클래스 파일들을 JDBC 드라이버로 묶어서 제공하고 있다. 자바 어플리케이션과 실제 데이터베이스가 연동하기 위해서는 각 DBMS 종류에 맞는 드라이버를 다운받아 사용해야 한다. JDBC 프로그래밍으로 연동하는 과정을 알아보자.

JDBC 환경설정

JDBC를 사용하기 위해 데이터베이스 서비스가 실행중인지 확인하고, 사용중인 DBMS 종류에 맞는 드라이버를 다운받아 클래스 패스에 설정하는 것이 필요하다

https://www.oracle.com/kr/database/technologies/xe-downloads.html

위 경로에서 ojdbc8.jar를 사용

이클립스 JDBC 설정

Build Path - Configure Build Path - Libraries - Add External JARs - 드라이버 선택 - Apply and Close

  • 오라클XE를 설치한 경로를 기억하여 드라이버 선택

JDBC API 사용 흐름

  • JDBC 드라이버 로딩 : 사용하고자 하는 JDBC 드라이버를 로딩한다. JDBC 드라이버는 DriverManager 클래스를 통해 로딩된다.
  • Connection 객체 생성 : JDBC 드라이버가 정상적으로 로딩되면 DriverManager를 통해 데이터베이스와 연결되는 세션(Session)인 Connection 객체를 생성한다.
  • Statement 객체 생성 : Statement 객체는 작성된 SQL 쿼리문을 실행하기 위한 객체로 정적 SQL 쿼리 문자열을 입력으로 가진다.
  • Query 실행 : 생성된 Statement 객체를 이용하여 입력한 SQL 쿼리를 실행한다.
  • ResultSet 객체로부터 데이터 조회 : 실행된 SQL 쿼리문에 대한 결과 데이터 셋이다.
  • ResultSet, Statement, Connection 객체들의 Close : JDBC API를 통해 사용된 객체들은 생성된 객체들을 사용한 순서의 역순으로 Close 한다.

커넥션 풀(Connection Pool)

JDBC API를 사용하여 데이터베이스와 연결하기 위해 Connection 객체를 생성하는 작업은 비용이 많이 드는 작업 중 하나이다.

커넥션 객체를 생성하는 과정

애플리케이션에서 DB 드라이버를 통해 커넥션을 조회한다.
DB 드라이버는 DB와 TCP/IP 커넥션을 연결한다. (3 way handshake와 같은 네트워크 연결 동작 발생)
DB 드라이버는 TCP/IP 커넥션이 연결되면 아이디와 패스워드, 기타 부가 정보를 DB에 전달한다.
DB는 아이디, 패스워드를 통해 내부 인증을 거친 후 내부에 DB를 생성한다.
DB는 커넥션 생성이 완료되었다는 응답을 보낸다.
DB 드라이버는 커넥션 객체를 생성해서 클라이언트에 반환한다.

이처럼 커넥션을 새로 만드는 것은 비용이 많이 들며, 비효율적이다.

이러한 문제를 해결하기 위해 애플리케이션 로딩 시점에 Connection 객체를 미리 생성하고, 애플리케이션에서 데이터베이스에 연결이 필요할 경우 미리 준비된 Connection 객체를 사용하여 애플리케이션의 성능을 향상하는 커넥션 풀(Connection Pool)이 등장하게 된다.

Connection 객체를 미리 생성하여 보관하고 애플리케이션이 필요할 때 꺼내서 사용할 수 있도록 관리해 주는 것이 Connection Pool이다.

커넥션 풀 동작 구조

  • 애플리케이션을 시작하는 시점에 커넥션 풀은 필요한 만큼 커넥션을 미리 생성하여 보관한다.
  • 서비스의 특징과 스펙에 따라 생성되는 Connection 객체의 개수는 다르지만 일반적으로 기본값으로 10개를 생성한다.
  • 커넥션 풀에 들어있는 Connection 객체는 TCP/IP로 DB와 연결되어 있는 상태이기 때문에 즉시 SQL을 DB에 전달할 수 있다.
  • 즉, DB 드라이버를 통해 새로운 커넥션을 획득하는 것이 아닌 이미 생성되어 있는 커넥션을 참조하여 사용하게 된다.
  • 커넥션 풀에 있는 커넥션을 요청하면 커넥션 풀은 자신이 가지고 있는 커넥션 객체 중 하나를 반환한다.

따라서, DB 드라이버를 통해 커넥션을 조회, 연결, 인증, SQL을 실행하는 시간 등 커넥션 객체를 생성하기 위한 과정을 생략할 수 있게 된다.

JDBC 프로그래밍 단계

1. 필요한 변수 선언 및 초기화

우선 데이터베이스 연동을 위한 네 가지 정보를 문자열에 저장한다. 어떤 드라이버를 쓸 것이고, 어떤 DBMS를, 어느 계정으로 사용할 것인지에 대한 정보를 변수에 저장하는 작업이다. 드라이버 내에 OracleDriver 클래스가 핵심이 되는 클래스 파일이다. 접속할 DBMS의 주소는 현재 로컬 컴퓨터에 DBMS를 설치했기 때문에 localhost라고 지정하고, 오라클의 기본 포트번호인 1521번과, ojdbc8.jar xe 버전의 SID인 xe를 지정합니다. 마지막으로 접속하고자 하는 계정명과 비밀번호를 저장한다.

String driver = "oracle.jdbc.driver.OracleDriver";
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String userid = "scott";
String passwd = "tiger";

데이터베이스 연동을 위해 필요한 객체도 선언합니다. 이후 반납해야 하는 자원들이므로 블록 바깥에서 선언해야 합니다.

Connection conn = null;
Statement stmt = null; //또는 PreparedStatement
ResultSet rs = null;
  • java.sql.Connection - 연결
  • java.sql.Statement - SQL을 담은 내용
  • java.sql.ResultSet - SQL 요청 응답

2. JDBC 드라이버 로딩

JDBC 드라이버 파일을 사용하기 위해 OracleDriver 클래스를 메모리에 올려야 한다. 다음과 같은 방법으로 클래스 파일을 메모리에 로딩한다.

Class.forName(driver);

이때 forName() 메소드가 ClassNotFoundException을 던지고 있으므로 try~catch문으로 잡아야 한다. 드라이버 파일을 클래스 패스에 설정하지 않았거나, 드라이버 이름을 잘못 지정한 경우 드라이버 로딩이 실패하여 예외가 발생하게 된다.

3. DB 접속 - Connection

드라이버가 성공적으로 로딩되었다면 DB 서버와 연결해야 한다. DriverManager 클래스의 getConnection() 메소드를 호출하며 접속할 DBMS의 주소와 계정 정보를 인자로 전달한다. 이 정보를 이용해서 DB 연결에 성공하면 Connection 객체를 반환한다. DB를 사용할 준비가 완료된 것이다.

conn = DriverManager.getConnection(url, userid, passwd);

이때 getConnection() 메소드가 SQLException을 던지고 있으므로 마찬가지로 try~catch문으로 잡아야 한다. 접속 정보를 잘못 지정한 경우 DB 접속에 실패하여 예외가 발생하게 된다. 이후 발생하는 예외들 역시 대부분 SQLException이므로 같은 catch블록으로 처리할 수 있게 된다.

4. SQL 문 작성

자바에서 DB에 요청할 SQL문을 문자열로 저장한다. 주의할 점은 SQL문 마지막에 세미콜론을 입력하지 않는다는 것이다. dept 테이블의 모든 데이터를 검색하기 위한 SQL문이다.

String sql = "select * from dept";

5. SQL 문 실행 후 결과값 얻기

자바에서 DB로 SQL문을 전송하고, DB에서 SQL문을 실행한 결과를 다시 자바로 받아오기 위해 Statement, PreparedStatement, 또는 CallableStatement 인터페이스를 사용한다. Connection 객체의 createStatement() 메소드를 이용해 Statement 객체를 생성하고, prepareStatement() 메소드를 이용해 PreparedStatement 객체를 생성하고, prepareCall() 메소드를 이용해 CallableStatement 객체를 생성한다.

stmt = con.createStatement();

PreparedStatement

PreparedStatement pstmt = conn.prepareStatement(sql);

PreparedStatement를 사용할 경우 sql문은
String sql = "select * from dept where no=?"
? 가 있어야하고 ?안에 값을 지정해줘야한다

pstmt.setString(1, 값);

자료형에따라 setString 또는 setInt등 될 수 있고
1번째 인덱스부터 시작이다

executeQuery() 메소드를 사용해 SELECT문을 실행하고 실행한 결과를 ResultSet 객체로 반환받아 변수에 저장한다. 테이블 형태의 SELECT한 결과를 자바의 객체로 표현한 것이 ResultSet 객체입니다.

rs = stmt.executeQuery(sql);

SELECT 문의 제외한 다른 SQL문

int re  = stmt.executeUpdate(sql);
int re = pstmt.executeUpdate()

executeUpdate()메소드를 사용해 DELETE, UPDATE, INSERT문을 실행하고 INT형을 반환한다
이 떄 ResultSet rs 는 필요가 없다

PreparedStatement와 Statement 차이점

PreparedStatement는 ?에 값을 직접 정해야줘한다
PreparedStatement를 생성할 때 sql문 먼저 넣는 차이점이 있다

6. 데이터 출력

ResultSet 객체는 포인터를 이용해서 원하는 데이터를 얻을 수 있다. 포인터를 이용해서 레코드를 선택한 후, 포인터가 가리키는 레코드의 컬럼을 지정해서 데이터를 얻는다. next() 메소드는 다음 레코드가 있는지 여부를 boolean 값으로 반환하고 다음 레코드로 포인터를 이동시킨다. 컬럼의 데이터 타입에 따른 getXXX("컬럼명") 메소드로 데이터를 얻을 수 있으며, 컬럼명 대신 컬럼의 위치값(1부터 시작) 사용도 가능합니다. 컬럼에 alias를 지정한 경우에는 컬럼명 사용은 불가능하다.

while (rs.next()) {
	int deptno = rs.getInt("deptno");
	String dname = rs.getString("dname");
	String loc = rs.getString("loc");
	System.out.println(deptno+"  "+dname+"  "+loc);
}

7. 자원 반납

파일 및 데이터베이스는 자바에서 사용하는 외부 자원이기 때문에 사용 후 반드시 자원을 해제해야 한다. 사용한 역순으로 자원을 해제하며, 예외가 발생하든 발생하지 않든 항상 자원을 반납해야 한다

f (rs != null) rs.close();
if (stmt != null) stmt.close();
if (con != null) con.close();

어떤 작업이 실패하거나 변수를 초기화하지 않아 해당 변수가 null인 경우가 있을 수 있습니다. 이때 null값을 가지고 close() 메소드를 호출하게 되면 NullPointerException이 발생하기 때문에 null이 아닌 경우에만 close() 메소드를 호출하도록 조건식을 지정해 주어야 한다. 또한, close() 메소드도 SQLException을 던지고 있기 때문에 finally 블록에서도 다시 try~catch 문으로 잡아주어야 한다.

전체코드 예시

class ListStudentFrame extends JFrame{
	JTextArea jta;
	public ListStudentFrame() {
		setTitle("학생목록");
		jta = new JTextArea();
		JScrollPane jsp = new JScrollPane(jta);
		add(jsp);
		
		listStudent();
		
		setSize(400, 300);
		setVisible(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	public void listStudent() {
		String sql = "select * from student";
		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");
			String url = "jdbc:oracle:thin:@localhost:1521:XE";
			String username="c##madang";
			String password = "madang";
			Connection conn = DriverManager.getConnection(url, username, password);
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(sql);
			while(rs.next()) {
				String name = rs.getString(1);
				int kor = rs.getInt(2);
				int eng = rs.getInt(3);
				int math = rs.getInt(4);
				int tot = rs.getInt(5);
				int avg = rs.getInt(6);
				String row = name+","+kor+","+eng+","+math+","+tot+","+avg+"\n";
				jta.append(row);
			}
			rs.close();
			stmt.close();
			conn.close();
			
		}catch (Exception e) {
			System.out.println("예외발생:"+e.getMessage());
		}
	}
}

public class ListStudentGUI {
	public static void main(String[] args) {
		new ListStudentFrame();
	}
}

참고
https://ittrue.tistory.com/250
https://amy-it.tistory.com/61

0개의 댓글