Java 如何使用Hibernate将数据流传输到数据库BLOB(内存中没有存储字节[])

Java 如何使用Hibernate将数据流传输到数据库BLOB(内存中没有存储字节[]),java,database,hibernate,stream,blob,Java,Database,Hibernate,Stream,Blob,我正在寻找一种将二进制数据传输到数据库或从数据库传输出去的方法。如果可能的话,我希望使用Hibernate(以数据库不可知的方式)来完成。 我发现的所有解决方案都涉及显式或隐式地将二进制数据作为字节[]加载到内存中。我需要避免它。假设我希望我的代码能够从数据库(存储在BLOB列中)将2GB视频写入本地文件,或者反过来,使用不超过256Mb的内存。这显然是可以实现的,而且不涉及巫毒。但我找不到办法,因为现在我正试图避免调试Hibernate 让我们看看示例代码(请记住-Jmx=256Mb) 实体类

我正在寻找一种将二进制数据传输到数据库或从数据库传输出去的方法。如果可能的话,我希望使用Hibernate(以数据库不可知的方式)来完成。 我发现的所有解决方案都涉及显式或隐式地将二进制数据作为字节[]加载到内存中。我需要避免它。假设我希望我的代码能够从数据库(存储在BLOB列中)将2GB视频写入本地文件,或者反过来,使用不超过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();
运行该代码段时,我会遇到内存异常。查看堆栈跟踪可以看出Hibernate试图将流加载到内存中并从内存中取出什么(它应该这样做)。下面是堆栈跟踪:

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可能会尝试在内存中加载所有内容,因为底层数据库不支持流Blob或类似的东西。如果是这样的话,我想看看不同方言在处理斑点方面的比较表


非常感谢你的帮助

您正在POJO
SimpleBean
中存储
Blob
。这意味着,如果blob大于堆空间,则无论何时处理此对象或访问
数据
字段,都会得到
OutOfMemoryError
,因为整个内容都加载到内存中

我认为在hibernate中没有办法使用流来设置或获取数据库字段,HQL只在SELECT语句中插入

您可能需要做的是从
SimpleBean
对象中删除
data
字段,以便在加载或保存时不会将其存储在内存中。但是当需要保存blob时,可以使用hibernate的
save()
创建行,然后使用a和
setBinaryStream()
方法。当需要访问流时,可以使用hibernate的
load()
方法获取
SimpleBean
对象,执行jdbc选择以获取
ResultSet
,然后使用
getBinaryStream()
方法读取blob。
setBinaryStream()的文档说明:

数据将根据需要从流中读取,直到到达文件末尾


因此,数据不会完全存储在内存中。

对于那些寻找相同内容的人

糟糕的是,对于PostgreSQL(可能还有很多其他代码),代码的工作方式与预期的一样(不尝试复制到内存的流)。Hibernate的内部工作取决于所选的方言。我首先使用的方法覆盖了流的直接使用,而支持字节[]支持的二进制流

性能方面也没有问题,因为对于PostgreSQL,它只加载OID(数字),对于其他方言(包括byte[]实现),它可能延迟加载数据。只是运行了一些肮脏的测试,在10000个实体负载中没有明显的差异,包括和不包括二进制数据字段


不过,在数据库中存储数据似乎比将数据作为外部文件保存在磁盘上要慢。但是,在备份、处理特定文件系统的限制或并发更新等时,这会让您省去很多麻烦。但这是一个离题的话题。

使用Hibernate的lobHelper的解决方案应该可以工作,但您可能需要确保强制使用流。 设置属性hibernate.jdbc.use\u streams\u for\u binary=true 这是一个系统级属性,因此必须在启动时设置它(我在测试期间在命令行上定义了它:

java -Dhibernate.jdbc.use_streams_for_binary=true blobTest
您可以在代码中证明它已更改:

Object prop = props.get("hibernate.jdbc.use_streams_for_binary");
System.out.println("hibernate.jdbc.use_streams_for_binary" + "/" + prop);

感谢您的回复!是的,我正在寻找PreparedStatement.setBinaryStream行为。但如果可能的话,我希望找到一种从基础数据库中提取的方法。一些RDBMS具有列的BLOB类型,PostgreSQL单独存储BLOB并通过OID引用它们,等等。节省一些时间,不要试图避免调试。)这是否仅适用于任何数据库或Oracle?