2011-02-18 63 views
3

我想用java.sql.Blob中的hibernate @Entity来存储一些二进制数据。存储不会抛出任何异常(但是,我不确定它是否真的存储了字节),但是读取确实存在。下面是我的测试:在休眠中从java.sql.Blob获取流

@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()); 
} 

所以它抛出一个异常:

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) 
    ... 

为什么?它不应该在这里读取字节表单DB吗?我能为它做些什么?

+0

我们需要知道在我们尝试回答这个问题之前抛出的异常是什么。 – adarshr 2011-02-18 15:23:49

+0

增加了例外。 – amorfis 2011-02-18 15:29:29

回答

-1

IOUtils.toByteArray关闭输入流的方法是什么?

+0

无关紧要,getBinaryStream()引发异常,因此在调用toByteArray()之前都会引发异常。 – amorfis 2011-02-18 15:35:46

+0

我知道。但getBinaryStream试图重新使用流。如果toByteArray方法关闭它,您的代码可能会第一次运行,但不会在后续时间运行。 – adarshr 2011-02-18 15:36:52

0

我目前唯一的解决方案是关闭写会话并打开新的Hibernate会话以获取流数据。有用。但我不知道有什么区别。我打电话给inputStream.close(),但这还不够。

另一种方式: 席力图召斑点free()方法之后session.save(attachment)调用过,但它抛出另一个异常:

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 
    } 
} 

}

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; 
    } 

在我的情况IOException异常的原因是org.apache.commons.io.input.AutoCloseInputStream的使用作为斑点来源:

InputStream content = new AutoCloseInputStream(stream); 
... 
Ent ent = new Ent(); 
... 
Blob blob = Hibernate.getLobCreator(getSession()).createBlob(content, file.getFileSize()) 
ent.setBlob(blob); 
em.persist(ent); 

在刷新会话休眠关闭Inpustream 内容(或者更确切地说org.postgresql.jdbc2.AbstractJdbc2Statement#setBlob在我的情况下关闭Inpustream)。当AutoCloseInputStream被关闭 - 这rases一个IOException异常的方法复位()

更新 在你的情况,你使用一个FileInputStream - 此流也会引发异常的复位方法。 测试用例存在问题。你创建blob并从一个事务中的数据库中读取它。在创建Ent时,Postgres jdbc驱动程序在清除会话时关闭InputStream。当你加载Ent(em.find(Ent.class,id)) - 你得到相同的BlobProxy对象,存储已经关闭的InputStream。

试试这个:

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流。