관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

JDBC 프로그래밍 7 - 단원 마무리 및 도전 문제 풀기 1 : 데이터베이스로 사진 저장하고 추출하기 본문

프로그래밍 언어/자바

JDBC 프로그래밍 7 - 단원 마무리 및 도전 문제 풀기 1 : 데이터베이스로 사진 저장하고 추출하기

huenuri 2024. 12. 14. 13:25

다음으로는 단원 마무리를 하며 도전 문제를 하나 풀어보려고 한다.


 
 
 
 

단원 요약하기


 
 
 
 

데이터베이스로 사진 저장 및 추출하기

 


 
 
 
 

문제 풀기

 

1. 테이블 생성

images라는 테이블을 MySQL에서 생성하려면 아래 SQL 명령어를 실행하면 된다.

 

SQL 코드 설명

  • ID : 자동 증가(AUTO_INCREMENT)로 설정하여, 이미지 삽입 시 고유한 값이 자동으로 지정된다. 별도로 관리할 필요 없다.
  • FILENAME : 이미지 파일 이름을 저장하는 열
  • FILE : 실제 이미지 데이터를 저장하는 MEDIUMBLOB 타입

 
 
 

2. 데이터베이스 확인

테이블이 제대로 생성되었는지 확인하려면 아래 명령어를 실행한다.


 
 
 
 

3. 데이터 삽입 테스트

테이블이 잘 생성되었으면, 이미지를 저장하는 Java 코드를 작성하기 전에 직접 SQL을 사용해 데이터 삽입 테스트를 해볼 수 있다.

 

SQL 코드 설명

  • LOAD_FILE('/path/to/your/image.jpg') : 파일 경로에 있는 이미지를 읽어와 MEDIUMBLOB 열에 저장
    • 예: '/Users/username/Desktop/test_image.jpg' 또는 'C:/images/test_image.jpg'

테스트 코드로 데이터가 잘 삽입되는 것을 확인했으니 이제 진짜 설정할 코드를 작성해 보겠다.


 
 
 

4. 이미지 삽입 테스트

이미지를 저장하려면 MySQL에서 아래와 같은 설정이 필요하다.

 
 

 
다운 받은 사진을 이처럼 폴더에 저장하고 이 코드를 실행한다.
 
 

 
이 설정이 없으면 LOAD_FILE 함수가 작동하지 않을 수 있다.


 
 
 
 

5. 자바 코드 만들고 실행하기

 

 
코드 실행 결과 몇 가지 오류가 있으므로 한번 수정해 보겠다.


 
 
 
 

6. 코드 수정하기

 

1. MySQL 드라이버 경고

Class.forName("com.mysql.jdbc.Driver");
  • 이 경고는 지금까지 많이 수정하며 아는 내용이므로 넘어가겠다.

 

2. ResultSet 사용 경고

ResultSet 객체를 사용하는 코드에서 경고가 발생할 수 있다.

Blob b = viewRS.getBlob("FILE");
img = new ImageIcon(b.getBytes(1, (int) b.length()));

 

  • getBytes()는 대량의 바이너리 데이터를 다룰 때 비효율적일 수 있다는 경고를 표시한다.
  • 경고를 무시해도 실행에는 문제가 없지만, 대량의 데이터를 처리해야 한다면 효율적인 방식으로 데이터를 처리하는 것이 좋다. 여기서는 getBytes()를 사용해도 괜찮다.

 
 

3. numberOfRecord 초기화 관련

numberOfRecord = srs.getInt(1);
  • numberOfRecord를 사용할 때 초기화되지 않았다는 경고가 발생할 수 있다.

 
해결 방법

  • 초기 값 설정하기
private int numberOfRecord = 0;

 
또는, 생성자에서 기본값을 명시적으로 설정

numberOfRecord = (srs != null && srs.next()) ? srs.getInt(1) : 0;

 
 
 

4. 자원 해제 관련 경고

자원(Connection, PreparedStatement, ResultSet)을 닫지 않았을 때 경고가 발생할 수 있다.

  • 모든 자원에 대해 finally 블록 또는 try-with-resources를 사용해 명시적으로 닫아야 한다
try (ResultSet srs = stmt.executeQuery("select count(*) from images")) {
    if (srs.next()) {
        numberOfRecord = srs.getInt(1);
    }
} catch (SQLException e) {
    e.printStackTrace();
}

 
 
 

5. Connection과 Statement 경고

conn과 stmt 필드가 클래스 수준에서 선언되었지만, 사용 후 명시적으로 닫지 않아서 경고가 발생할 수 있다.

  • 클래스 필드가 아닌, 지역 변수로 선언하는 것이 더 안전하다.
  • try-with-resources를 사용해 자동으로 자원을 닫도록 처리
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/photodb", "root", "1234");
     Statement stmt = conn.createStatement()) {
    ResultSet srs = stmt.executeQuery("select count(*) from images");
    if (srs.next()) {
        numberOfRecord = srs.getInt(1);
    }
} catch (SQLException e) {
    e.printStackTrace();
}

 
 

6. 파일 스트림 경고

FileInputStream을 사용하면서 자원을 닫지 않았을 때 발생할 수 있다.
 
해결 방법

  • try-with-resources를 사용해 스트림을 자동으로 닫도록 처리
try (FileInputStream fin = new FileInputStream(file)) {
    PreparedStatement pre = conn.prepareStatement("INSERT INTO images (ID, FILENAME, FILE) VALUES (?, ?, ?)");
    pre.setInt(1, numberOfRecord++);
    pre.setString(2, file.getName());
    pre.setBinaryStream(3, fin, (int) file.length());
    pre.executeUpdate();
    pre.close();
} catch (Exception e) {
    handleError(e.getMessage());
}

 
 
 
 

학습을 마치고

문제가 아직 해결되지 않았지만 끝까지 코드를 수정하며 반드시 해결해 보기로 했다. 이 문제를 2시간도 훨씬 넘게 푼 것 같다. 도전 문제가 너무나도 어렵다.