Получить поток из java.sql.Blob в Hibernate - PullRequest
3 голосов
/ 18 февраля 2011

Я пытаюсь использовать 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)
    ...

Почему? Разве он не должен читать байты из БД здесь? И что я могу сделать, чтобы это работало?

Ответы [ 5 ]

5 голосов
/ 27 мая 2014

Попробуйте обновить сущность:

em.refresh(fromDb);

Поток будет вновь открыт. Я подозреваю, что find (...) закрывает поток больших двоичных объектов.

2 голосов
/ 08 июня 2011

Не совсем понятно, как вы используете JPA здесь, но, конечно, вам не нужно иметь дело непосредственно с типом данных Blob, если вы используете JPA.

Вам просто нужно объявить поле врассматриваемая сущность @Lob выглядит примерно так:

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

Затем, когда вы извлекаете вашу сущность, байты будут снова считываться в поле, и вы сможете поместить их в поток и выполнитьвсе, что вы хотите с ними.

Конечно, вам понадобятся методы получения и установки в вашей сущности для выполнения преобразования байтов.В приведенном выше примере это будет выглядеть примерно так:

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 голос
/ 11 декабря 2012

Вам необходимо установить точку останова для метода 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 в качестве источника для BLOB-объекта:

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

При сбрасывании спящего режима Session закрываетсяInpustream content (точнее org.postgresql.jdbc2.AbstractJdbc2Statement # setBlob закрывает Inpustream в моем случае).И когда AutoCloseInputStream закрывается - он вызывает IOException в методе reset ()

update В вашем случае вы используете FileInputStream - этот поток также вызывает исключение сброс метод.В тестовом случае есть проблема.Вы создаете BLOB-объекты и считываете их из базы данных внутри одной транзакции.Когда вы создаете Ent, драйвер jdbc Postgres закрывает 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[] {};
            }
        }
    });
}
0 голосов
/ 07 июня 2011

Мое текущее и единственное решение - закрыть сеанс записи и открыть новый сеанс Hibernate для возврата потоковых данных.Оно работает.Однако я не знаю, в чем разница.Я позвонил inputStream.close(), но этого было недостаточно.

Другой способ: я пытался вызвать free() метод blob после вызова 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

0 голосов
/ 18 февраля 2011

Метод IOUtils.toByteArray закрывает входной поток?

...