2011-02-18 14 views
3

일부 바이너리 데이터를 저장하기 위해 최대 절전 모드 @Entity를 java.sql.Blob와 함께 사용하려고합니다. Storing은 어떤 예외도 발생시키지 않습니다 (그러나 실제로 바이트를 저장하는지는 확실하지 않습니다). 그러나 읽기는 않습니다. 여기에 내 테스트는 다음과 같습니다최대 절전 모드에서 java.sql.Blob 스트림을 가져 오기

java.sql.SQLException: could not reset reader 
    at org.hibernate.engine.jdbc.BlobProxy.getStream(BlobProxy.java:86) 
    at org.hibernate.engine.jdbc.BlobProxy.invoke(BlobProxy.java:108) 
    at $Proxy81.getBinaryStream(Unknown Source) 
    ... 

이유 :

@Test 
public void shouldStoreBlob() { 
    InputStream readFile = getClass().getResourceAsStream("myfile"); 

    Blob blob = dao.createBlob(readFile, readFile.available()); 
    Ent ent = new Ent(); 
    ent.setBlob(blob); 

    em.persist(ent); 
    long id = ent.getId(); 

    Ent fromDb = em.find(Ent.class, id); 

    //Exception is thrown from getBinaryStream() 
    byte[] fromDbBytes = IOUtils.toByteArray(fromDb.getBlob().getBinaryStream()); 
} 

그래서 예외를 throw? 여기서 DB 형식의 바이트를 읽지 않아야합니까? 그리고 나는 그것을하기 위해 무엇을 할 수 있습니까?

+0

우리는이 질문에 답하기 전에 어떤 예외가 발생했는지 알아야합니다. – adarshr

+0

예외가 추가되었습니다. – amorfis

답변

-1

IOUtils.toByteArray가 입력 스트림을 닫는 방법이 있습니까?

+0

문제가되지 않습니다. 예외는 getBinaryStream()에서 발생하므로 toByteArray()가 호출되기 전에 예외가 발생합니다. – amorfis

+0

알아. 그러나 getBinaryStream은 스트림을 다시 사용하려고 시도합니다. toByteArray 메서드가이를 닫으면 코드가 처음에는 작동하지만 이후에는 작동하지 않습니다. – adarshr

+0

닫히지는 않았지만 재설정 할 수 없습니다(). 하지만 스트림이 재사용되는 이유는 무엇입니까? 데이터를 DB로 가져 오지 않았습니까? 그런 다음 그것을 읽는 새로운 스트림을 만들어야합니다. – amorfis

0

현재 유일한 해결책은 쓰기 세션을 닫고 새로운 최대 절전 모드 세션을 열어 스트림 된 데이터를 가져 오는 것입니다. 그것은 작동합니다. 그러나 나는 그 차이점을 모른다. 나는 inputStream.close()라고 불렀지 만, 그 정도면 충분하지 않았습니다.

또 다른 방법 : 내가 너무 session.save(attachment) 호출 후 방울의 free() 메소드를 호출하려고하지만 다른 예외가 발생합니다 :

Exception in thread "main" java.lang.AbstractMethodError: org.hibernate.lob.SerializableBlob.free()V 
at my.hibernatetest.HibernateTestBLOB.storeStreamInDatabase(HibernateTestBLOB.java:142) 
at my.hibernatetest.HibernateTestBLOB.main(HibernateTestBLOB.java:60) 

내가 PostgreSQL 8.4 + postgresql-8.4-702.jdbc4.jar, Hibernate 3.3.1.GA

2

을 사용하고 그것은 전혀 분명하지 않다을하는 방법을 여기서 JPA를 사용하고 있지만 JPA를 사용하는 경우 Blob 데이터 유형을 직접 처리 할 필요가 없습니다.

당신은 단지 약간 같은 @Lob의 문제의 실체에 필드를 선언해야합니다

@Lob 
@Basic(fetch = LAZY) 
@Column(name = "image") 
private byte[] image; 

를 그리고, 당신이 당신의 개체를 검색 할 때, 바이트는 필드에 다시 읽고됩니다 그들을 스트림에 넣을 수 있고 원하는대로 할 수 있습니다.

물론 바이트 변환을 수행하려면 엔티티에 getter 및 setter 메소드가 필요합니다. 이 예에서는 다소 같은 것 위에 :

private Image getImage() { 
    Image result = null; 

    if (this.image != null && this.image.length > 0) { 
     result = new ImageIcon(this.image).getImage(); 
    } 

    return result; 
} 

그리고이

private void setImage(Image source) { 

    BufferedImage buffered = new BufferedImage(source.getWidth(null), source.getHeight(null), BufferedImage.TYPE_INT_RGB); 
    Graphics2D g = buffered.createGraphics(); 
    g.drawImage(source, 0, 0, null); 
    g.dispose(); 

    ByteArrayOutputStream stream = new ByteArrayOutputStream(); 
    try { 
     ImageIO.write(buffered, "JPEG", stream); 
     this.image = stream.toByteArray(); 
    } 
    catch (IOException e) { 
     assert (false); // should never happen 
    } 
} 

같은 다소 세터}

+4

작은 데이터의 경우 이는 맞지만 크기가 큰 객체의 경우 스트리밍 API가 필요합니다. 메모리에 완전히로드하면 문제가 발생하기 때문입니다. –

1

당신은 방법 org.hibernate.engine.jdbc에 중단 점을 설정해야합니다을 .BlobProxy # getStream 라인 stream.reset()과 IOException이의 이유로 검사 :

private InputStream getStream() throws SQLException { 
      try { 
        if (needsReset) { 
          stream.reset(); // <---- Set breakpoint here 
        } 
      } 
      catch (IOException ioe) { 
        throw new SQLException("could not reset reader"); 
      } 
      needsReset = true; 
      return stream; 
    } 

이 세션의 최대 절전 모드를 세척하는 Inpustream 내용을 (종료
InputStream content = new AutoCloseInputStream(stream); 
... 
Ent ent = new Ent(); 
... 
Blob blob = Hibernate.getLobCreator(getSession()).createBlob(content, file.getFileSize()) 
ent.setBlob(blob); 
em.persist(ent); 

있지만 내 경우

는 IOException이의 이유는 물방울의 소스로 org.apache.commons.io.input.AutoCloseInputStream의 사용에 있었다 오히려 org.postgresql.jdbc2.AbstractJdbc2Statement # setBlob은 Inpustream을 닫습니다.AutoCloseInputStream 닫을 때 - 방법을 재설정에이 스트림이 또한 예외가 발생합니다 - 그것은 당신의 경우 당신이 FileInputStream에를 사용하는 방법에 리셋()

업데이 트를 IOException를 rases. 테스트 케이스에 문제가 있습니다. 하나의 트랜잭션 내에서 blob을 작성하고 데이터베이스에서 읽습니다. Ent를 만들면 Postgres jdbc 드라이버는 세션을 비우는 동안 InputStream을 닫습니다. Ent (em.find (Ent.class, id))를로드 할 때 이미 닫힌 InputStream을 저장하는 동일한 BlobProxy 객체를 가져옵니다.

이 시도 :

TransactionTemplate tt; 

@Test 
public void shouldStoreBlob() { 
    final long id = tt.execute(new TransactionCallback<long>() 
    { 
     @Override 
     public long doInTransaction(TransactionStatus status) 
     { 
      try 
      { 
       InputStream readFile = getClass().getResourceAsStream("myfile"); 

       Blob blob = dao.createBlob(readFile, readFile.available()); 
       Ent ent = new Ent(); 
       ent.setBlob(blob); 

       em.persist(ent); 
       return ent.getId(); 
      } 
      catch (Exception e) 
      { 
       return 0; 
      } 
     } 
    }); 

    byte[] fromStorage = tt.execute(new TransactionCallback<byte[]>() 
    { 
     @Override 
     public byte[] doInTransaction(TransactionStatus status) 
     { 
      Ent fromDb = em.find(Ent.class, id); 
      try 
      { 
       return IOUtils.toByteArray(fromDb.getBlob().getBinaryStream()); 
      } 
      catch (IOException e) 
      { 
       return new byte[] {}; 
      } 
     } 
    }); 
} 
5

시도 엔티티를 새로 고침 :

em.refresh(fromDb); 

이 스트림이

을 재개됩니다. find (...)가 blob 스트림을 닫고있는 것 같습니다.

+0

고마워!, 나를 위해 일했다. – Carlos

+0

그것은 나를 위해 일했습니다. 감사! – jtonic

관련 문제