データベースとの間でバイナリ データをストリーミングする方法を探しています。可能であれば、(データベースにとらわれない方法で) Hibernate で実行したいと思います。私が見つけたすべての解決策には、バイナリ データをバイト [] としてメモリに明示的または暗黙的にロードすることが含まれます。私はそれを避ける必要があります。コードでデータベースから 2GB のビデオ (BLOB 列に格納) をローカル ファイルに書き込めるようにしたい、またはその逆で、256Mb 以下のメモリを使用できるようにしたいとします。それは明らかに達成可能であり、ブードゥー教は含まれていません。しかし、方法が見つかりません。今のところ、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 を使用してデータベースに BLOB を格納するときにストリームをメモリにロードしないようにする方法です。動画をコンテンツ リポジトリに保存してリンクするのではなく、データベースの列に保存する理由についての話題を避けたいと思います。質問に関係のない機種とお考えください。
基礎となるデータベースがストリーミングブロブなどをサポートしていないため、さまざまな方言に何らかの機能があり、Hibernate がすべてをメモリにロードしようとする可能性があるようです。その場合は、ブロブの処理の観点から、異なる方言間のある種の比較表を見たいと思います。
ご助力ありがとうございます!