[Java] DB & JDBC

이현경·2025년 11월 15일

관계형 데이터베이스

관계형 데이터베이스는 키와 값들의 관계를 테이블로 표현한 데이터베이스 모델을 말한다. 키는 데이블의 열, 테이블의 행은 하나의 레코드를 표현한다. 여러 테이블 간에 공통된 이름의 열을 포함할 수 있으며, 서로 다른 테이블 간에 관계가 성립할 수도 있다.

  • 기본 용어
    • 릴레이션: 하나의 객체에 관한 데이터를 2차원 테이블 구조로 저장한 것
    • 속성(attribute): 릴레이션의 열, 파일 관리 시스템에서 필드에 해당
    • 투플(tuple): 릴레이션의 행, 파일 관리 시스템에서 레코드에 해당
    • 도메인(domain): 하나의 속성이 가질 수 있는 모든 값의 범위 또는 데이터 타입(문자열, 정수, 실수 등)



기본 SQL 문법

데이터 저장
users 테이블에 새로운 사용자 정보를 저장하는 INSERT문

INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES ('winter', '한겨울', '12345', 25, 'winter@mycompany.com')

값을 ?로 대체한 매개변수화된 INSERT 문

INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES (?, ?, ?, ?, ?)

데이터 수정
boards 테이블에 저장된 게시물 중에서 bno가 1인 게시물의 btitle, bcontent, bfilename, bfiledata를 변경하는 SQL 문

UPDATE boards SET
	btitle='눈사람',
	bcontent='눈으로 만든 사람',
	bfilename='snowman.jpg',
	bfiledata=binaryData
WHERE bno=1

값을 ?로 대체한 매개변수화된 UPDATE 문

UPDATE boards SET
	btitle=?,
	bcontent=?,
	bfilename=?,
	bfiledata=?
WHERE bno=?

데이터 삭제
boards 테이블에서 bwriter가 winter인 모든 게시물을 삭제하는 DELETE 문

DELETE FROM boards WHERE bwriter='winter'

값을 ?로 대체한 매개변수화된 DELETE 문

DELETE FROM boards WHERE bwriter=?

데이터 읽기

SELECTFROM 테이블 WHERE 조건
SELECT * FROM student WHERE stdID=?



JDBC

JDBC는 Java Database Connectivity의 약자로, 자바 애플리케이션이 데이터베이스에 연결하고 SQL문을 실행할 수 있도록 제공되는 표준 API이다. 과거에는 데이터베이스 종류마다 접속 방법이 달라 개발이 번거로웠으나, JAVA는 JDBC라는 통일된 인터페이스를 만들었고, 각 데이터베이스 제조사는 이 규격에 맞는 JDBC 드라이버를 제공한다. 개발자는 JDBC 드라이버만 교체하면, 자바 코드의 변경 없이 다양한 종류의 데이터베이스와 통신가능하다.

참고로 JDBC 라이브러리(java.sql 패키지)는 다음과 같은 구조이다.


JDBC 프로그래밍은 다음 5단계를 따라 수행된다.

1. JDBC 드라이버 로드

사용하려는 데이터베이스의 JDBC 드라이버 클래스를 JVM에 로드한다.

try {
	Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFundException e) {
	e.printStackTrace();
}

JDBC 4.0이후 아래 이미지와 같이 jar 파일이 classpath에 포함되어 있다면 해당 과정은 생략 가능한다.


2. 데이터베이스 연결

DriverManager를 통해 DB 서버 URL, 사용자 계정, 비밀번호를 사용하여 실제 데이터베이스와 연결을 맺는다. 성공하면 Connection 객체가 반환된다.

// DB 서버 주소
String url = "jdbc:mysql://192.168.0.13:3306/java2_3";
String username = "stdUser";
String password = "wkvmtlf2";
Connection conn = null;
try {
	conn = DriverManager.getConnection(url, username, password);
    System.out.println("DB 연결 성공!");
} catch (SQLException e) {
	System.out.println("DB 연결 실패");
    e.printStackTrace();
}

3. SQL문 실행

Connection 객체로부터 SQL문을 실행할 수 있는 Statement 또는 PreparedStatement 객체를 생성한다. Statement 객체는 정적인 SQL문을 실행할 때 사용하긴 하지만 성능과 보안 문제로 preparedStatement을 권장한다. SQL을 미리 컴파일하여 반복 실행 시 성능이 좋고, ?를 이용한 값 바인딩으로 SQL 인젝션 공격을 원천적으로 방어할 수 있다.

// PreparedStatement 사용 예시
String insertSql = "INSERT INTO student (no, dept, stdID, name) VALUES (?,?,?,?)";
// ?에 값 바인딩
PreparedStatement pstmt = conn.prepareStatement(insertSql);
pstmt.setInt(1, 1);
pstmt.setString(2, "컴퓨터공학과");
pstmt.setString(3, "1234567");
pstmt.setString(4, "신짱구");
int result = pstmt.executeUpdate();

여기서 executeUpdate()는 INSERT, UPDATE, DELETE 등 데이터 변경 SQL에 사용하며, 영향받은 행의 수를 반환한다. 뒤에 나올 executeQuery()는 SELECT 등 데이터 조회 SQL에 사용하며, 결과는 ResultSet 객체로 반환하다.


4. 결과 처리

SELECT문을 실행한 후 반환되는 ResultSet 객체는 조회된 데이터의 집합으로, next() 메서드를 호출하여 한 행씩 이동하며 데이터를 읽어오면 된다.

// student 테이블을 PreparedStatement로 조회
String selectSql = "SELECT * FROM student order by no asc";
PreparedStatement pstmt = conn.PreparedStatement(selectSql);

ResultSet rs = pstmt.executeQuery(selectSql); // SELECT 실행
while (rs.next()) { // 조회된 데이터가 있다면
	int no = rs.getInt("no");
	String dept = rs.getString("dept");
	String stdID = rs.getString("stdID");
	String name = rs.getString("name");

	System.out.printf(" No: %2d, 학과: %10s 학번: %10s 이름: %10s\n", no, dept, stdID, name);
}

5. 자원 해제

사용한 객체를 사용한 역순으로 닫아 시스템의 자원 누수를 막는다. 이때, try-with-resources(Java 7 이상)를 사용하면 자원을 자동 해제해다.

String sql = "SELECT id, name FROM user";
try (Connection conn = DriverManager.getConnection(url, user, password);
	PreparedStatement pstmt = conn.prepareStatement(sql);
    ResultSet rs = pstmt.executeQuery()) {
    while(rs.next()) {
        System.out.println("아이디: "+rs.getString("id")+", 이름: "+rs.getString("name"));
    }
} catch(SQLException e) {
	e.printStackTrace();
}

try() 괄호 안에 선언된 리소스는 자동으로 close()가 호출된다. 또한 예외가 발생하더라도 finally 없이 자원 정리가 가능하여 코드가 깔끔하고 오류 처리도 쉽다.



JAVA&DB 연결 코드 리팩토링

수업시간에 실습한 main 코드를 구조를 나누어 리팩토링 해보았다.

DatabaseConnector.java

Student.java

StudentDao.java

StudentTest.java

초기 db 상태

코드 실행 후 결과



후기

아직 sql 문법이 익숙하지도 않아서 ?의 순서에 따라서 어떤게 수정되고 뭐가 수정될 대상인지 구별해주는게 좀 헷갈렸다. 그리고 아직 dao, dto 이런 개념이 생소하니 구조 분리에 적용하는게 생각보다 어려웠다. 그리고 처음에 실행시키는데 내 콘솔과 db의 상태정보가 달라서 왜 다른지 찾느라 좀 진을 뺐다. 알고보니 수정된 최신값을 안 가져오고 그냥 출력문을 돌려버려서였다... 그래도 실습해보니 재미있었다^^

profile
커피 한 잔의 여유를 아는 품격있는 여자

0개의 댓글