Я ищу способ потоковой передачи двоичных данных в/из базы данных. Если возможно, я бы хотел, чтобы это было сделано с помощью Hibernate (независимо от базы данных ). Все решения, которые я нашел, включают явную или неявную загрузку двоичных данных в память как byte[]. Мне нужно избежать этого. Допустим, я хочу, чтобы мой код мог записать в локальный файл 2 ГБ видео из базы данных (, хранящейся в столбце BLOB ), или наоборот, используя не более 256 МБ памяти. Это вполне достижимо и не требует вуду. Но не могу найти способ, пока пытаюсь избежать отладки Hibernate.
Давайте посмотрим на пример кода (, имея в виду -Jmx=256Mb ).
Класс сущности:
public class SimpleBean {
private Long id;
private Blob data;
//... skipping getters, setters and constructors.
}
Фрагмент отображения Hibernate:
<class name="SimpleBean" table="SIMPLE_BEANS">
<id name="id" column="SIMPLE_BEAN_ID">
<generator class="increment" />
</id>
<property name="data" type="blob" column="DATA" />
</class>
Фрагмент тестового кода:
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties())
.buildServiceRegistry();
SessionFactory sessionFactory = cfg.buildSessionFactory(serviceRegistry);
Session session = sessionFactory.openSession();
session.beginTransaction();
File dataFile = new File("movie_1gb.avi");
long dataSize = dataFile.length();
InputStream dataStream = new FileInputStream(dataFile);
LobHelper lobHelper = session.getLobHelper();
Blob dataBlob = lobHelper.createBlob(dataStream, dataSize);
session.save( new SimpleBean(data) );
session.getTransaction().commit(); // Throws java.lang.OutOfMemoryError
session.close();
blobStream.close();
sessionFactory.close();
При запуске этого фрагмента я получаю исключение OutOfMemory. Глядя на трассировку стека, видно, что Hibernate пытается загрузить поток в память и получает OutOfMemory (, как и должно ). Вот трассировка стека:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2271)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:113)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:140)
at org.hibernate.type.descriptor.java.DataHelper.extractBytes(DataHelper.java:183)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:121)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:45)
at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:105)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:92)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:305)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:300)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:57)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2603)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2857)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3301)
at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:88)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:354)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:275)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:326)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:52)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1214)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:403)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
at ru.swemel.msgcenter.domain.SimpleBeanTest.testBasicUsage(SimpleBeanTest.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
Использовал Hibernate 4.1.5.SP1. Точный вопрос :заключается в том, как избежать загрузки потока в память при сохранении большого двоичного объекта в базе данных с использованием Hibernate, используя вместо этого прямую потоковую передачу. Я хотел бы избежать нестандартных тем о том, почему видео хранится в столбце базы данных, а не хранится в каком-либо репозитории контента и связывается. Пожалуйста, считайте это моделью, которая не имеет отношения к вопросу.
Кажется, что на разных диалектах могут быть какие-то возможности, и Hibernate может попытаться загрузить все в память, потому что базовая база данных не поддерживает потоковую передачу больших двоичных объектов или что-то в этом роде.Если это так, -я хотел бы увидеть какую-то сравнительную таблицу между различными диалектами с точки зрения обращения с каплями.
Большое спасибо за Вашу помощь!