행복한 아빠

Spring과 Hibernate에서 Blob Object 처리하기 본문

잡다한기록

Spring과 Hibernate에서 Blob Object 처리하기

행복한아빠 2009. 12. 2. 09:51

Hibernate를 이용하면 Java 객체의 내용을 DB로 자연스럽게 매핑해줍니다. 이런 ORM을 이용하면 DB 관련 작업에 놀라운 생산성을 가져다 줍니다. 가끔 대량의 문자열이나 파일을 DB에 넣기 위해 CLOB이나 BLOB을 이용하는데 이런 LOB(Large Object) 객체도 쉽게 DB에 넣을 수 있습니다. (예전에 lob 처리가 간단치 않았습니다.)
단지 필드를 byte[] 타입으로 저장하고 Lob이라고 지정하면 끝이 납니다. 아래처럼 말이죠.


...
    @Lob
    @Column(name="PHOTO")
    private byte[] photo[];
....

뭐가 문제일까?

파일내용을 DB로 넣기 위해 객체에서 위 방법대로 byte 배열로 모두 가지고 있으면 운영 시 상당한 문제를 발생시킬 수 있습니다. 예를 들어 몇십 메가바이트의 파일을 업로드하기 위해 저 방법을 사용한다면 몇십 메가바이트의 파일 내용이 메모리에 그대로 올라가게 됩니다. (사실 몇십메가바이트 이상 쓰게 됩니다)
JVM은 한정된 메모리에서 동작하므로 이러한 방법은 애플리케이션 성능에 막대한 영향을 미칠 수 있습니다. 동시 사용자가 많을 경우 OutOfMemoryError 가 발생하는 상황을 만날수도 있습니다.
JVM은 가비지 컬렉션을 어떻게 하느냐 얼마나 하느냐에 따라 성능을 좌우하기도 합니다. 메모리 함부로 쓰지 마세요~

Hibernate에서 이러한 LOB 처리를 메모리에 올리는 방법이 아니라 스트림으로 처리하는 방법을 잘 정리한 블로그가 있어 정리해봅니다. 파일이나 입력스트림을 읽는 즉시 DB의 LOB에 스트림으로 바로 넣으니 메모리 사용이 없고 중간과정 없으니 속도도 훨씬 빠릅니다.

원문: http://turgaykivrak.wordpress.com/2008/06/06/blob-objects-with-spring-and-hibernate/


Spring과 Hibernate로 Blob 처리하기

당신은 스트림 객체들을 가지고 있습니다. 즉 파일들이죠. 그리고 이것을 hibernate와 spring을 이용하여 database에 넣고 싶습니다.

무엇보다도 먼저 파일내용(byte)만 저장하는 객체를 따로 만들라고 권장하고 싶습니다.

public class MyFileContent extends Entity {

  private InputStream content;

  MyFileContent () {
  }

  public MyFileContent(InputStream content) {
   this.content = content;
  }

  public InputStream getContent() {
    return content;
  }

  private void setContent(InputStream content) {
    this.content = content;
   }
}


여기에서 상속받은 Entity 는 단지 id를 가지고 있는 base 클래스입니다. 뭐 id의 getter, setter도 있겠지요.

아래에 blob에 대한 hibernate 사용자 타입이 있습니다. (hibernate가 blob 처리를 아래 클래스로 하게 할 겁니다.)

import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.HibernateException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.orm.hibernate3.support.AbstractLobType;

public class BlobUserType extends AbstractLobType {

 public int[] sqlTypes() {
   return new int[] { Types.BLOB };
 }

 public Class returnedClass() {
   return InputStream.class;
 }

 protected Object nullSafeGetInternal(ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
  throws SQLException, HibernateException {
   return lobHandler.getBlobAsBinaryStream(rs, names[0]);
 }

 protected void nullSafeSetInternal(PreparedStatement ps, int index, Object value, LobCreator lobCreator)
  throws SQLException, HibernateException {
   if (value != null) {
    lobCreator.setBlobAsBinaryStream(ps, index, (InputStream) value, -1);
   } else {
    lobCreator.setBlobAsBytes(ps, index, null);
   }
 }


MyFileContent 클래스의 hibernate 매핑입니다.


HibernateContext.xml의 hiberation 설정입니다.


Oracle과 Websphere를 사용한다면 defaultLobHandler 대신 아래것을 사용해야 합니다. Oracle의 blob 객체는 좀 다르기 때문이죠.


마지막으로 사용해 봅시다.

InputStream inputStream = ...;
MyFileContent fileContent = new StoredFile(inputStream);
myRepository.save(fileContent );


테스트 하는 건 잊지 마세요 :)

--

Hibernate를 직접 사용하지 않고 JPA를 사용할 경우는 좀 다릅니다. "JPA 에서 Blob 처리"를 참조하세요~


0 Comments
댓글쓰기 폼