Wie zum streamen von Daten in die Datenbank BLOB mit Hibernate (keine in-memory-Speicherung in byte[])

Ich bin auf der Suche nach einem Weg, um Strom von binären Daten zu/von der Datenbank. Wenn möglich, möchte ich die Arbeit mit Hibernate (Datenbank-unabhängige Art und Weise).
Alle Lösungen die ich gefunden habe, beinhalten explizite oder implizite laden von binären Daten in den Speicher als byte[]. Ich brauche zu vermeiden. Sagen wir, ich will mein code zum schreiben in eine lokale Datei eine 2-GB-video aus einer Datenbank (gespeichert in der BLOB-Spalte), oder die andere Weise herum, mit nicht mehr als 256Mb Speicher. Es ist klar erreichbar und beinhaltet kein voodoo. Aber ich kann nicht einen Weg finden, für die ich jetzt versuche zu vermeiden, debugging Hibernate.

Betrachten wir Beispiel-code (halten im Verstand -Jmx=256Mb).

Entity-Klasse:

public class SimpleBean {
    private Long id;
    private Blob data;
    //... skipping getters, setters and constructors.
}

Hibernate mapping fragment:

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

Test-code-fragment:

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

Beim laufen das snippet bekomme ich OutOfMemory-exception. Auf der Suche im stack-trace zeigt, was mit Hibernate zu laden versucht den stream in den Speicher und bekommt OutOfMemory (wie es sollte). Hier die stack-trace:

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)

Verwendet Hibernate 4.1.5.SP1. Die genaue Frage ist: wie vermeiden Sie das laden-stream in den Speicher, wenn das speichern eines blob in der Datenbank mit Hilfe von Hibernate, mit direct-streaming statt. Ich möchte vermeiden, aus Themen darüber, warum man speichert video in der Spalte der Datenbank, anstatt es zu speichern in einigen content-repository und verknüpfen. Bitte, halten Sie es für ein Modell, was für die Fragestellung irrelevant.

Es scheint, dass es möglicherweise irgendeine Art von Fähigkeiten auf verschiedene Dialekte und Überwintern könnten versuchen, zu laden, alles im Speicher, weil die zugrunde liegende Datenbank nicht unterstützt streaming-blobs oder so ähnlich. Wenn es der Fall ist - ich würde gerne sehen, eine Art vergleichende Tabelle zwischen verschiedenen Dialekte im Aspekt der Umgang mit blobs.

Danken Ihnen sehr für Ihre Hilfe!

InformationsquelleAutor IceGlow | 2012-08-12
Schreibe einen Kommentar