Java 如何使用Hibernate在Oracle中持久化大blob(>;100MB)
我正在努力找到一种方法,使用BLOB列在Oracle数据库中插入大图像(>100MB,主要是TIFF格式) 我在网络上甚至StackOverflow中进行了彻底的搜索,但没有找到这个问题的答案。Java 如何使用Hibernate在Oracle中持久化大blob(>;100MB),java,oracle,hibernate,spring,blob,Java,Oracle,Hibernate,Spring,Blob,我正在努力找到一种方法,使用BLOB列在Oracle数据库中插入大图像(>100MB,主要是TIFF格式) 我在网络上甚至StackOverflow中进行了彻底的搜索,但没有找到这个问题的答案。 首先是问题……然后是关于相关代码(java类/配置)的一小段,最后是第三段,其中我展示了我为测试映像持久性而编写的junit测试(我在junit测试执行期间收到错误) 编辑:我在问题的末尾添加了一个部分,在这里我描述了一些使用JConsole的测试和分析 问题 我收到一个java.lang.OutOfM
首先是问题……然后是关于相关代码(java类/配置)的一小段,最后是第三段,其中我展示了我为测试映像持久性而编写的junit测试(我在junit测试执行期间收到错误) 编辑:我在问题的末尾添加了一个部分,在这里我描述了一些使用JConsole的测试和分析 问题 我收到一个
java.lang.OutOfMemoryError:java堆空间
错误,使用hibernate并试图保存非常大的图像/文档:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
at org.hibernate.type.descriptor.java.DataHelper.extractBytes(DataHelper.java:190)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:123)
at org.hibernate.type.descriptor.java.BlobTypeDescriptor.unwrap(BlobTypeDescriptor.java:47)
at org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:101)
at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:91)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:283)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:278)
at org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:89)
at org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2184)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2430)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2874)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:79)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:184)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
at it.paoloyx.blobcrud.manager.DocumentManagerTest.testInsertDocumentVersion(DocumentManagerTest.java:929)
代码(域对象、存储库类、配置)
下面是我正在使用的一系列技术(从数据库到业务逻辑层)。我使用JDK6
- Oracle Database 10g Enterprise Edition 10.2.0.4.0版-产品
- ojdbc6.jar(适用于11.2.0.3版本)
- Hibernate 4.0.1最终版
- 弹簧3.1.GA释放
DocumentVersion
有许多DocumentData
,每一个都可以代表同一DocumentVersion
的不同二进制内容
文档版本的相关摘录
类:
@Entity
@Table(name = "DOCUMENT_VERSION")
public class DocumentVersion implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private Set<DocumentData> otherDocumentContents = new HashSet<DocumentData>(0);
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "DOV_ID", nullable = false)
public Long getId() {
return id;
}
@OneToMany
@Cascade({ CascadeType.SAVE_UPDATE })
@JoinColumn(name = "DOD_DOCUMENT_VERSION")
public Set<DocumentData> getOtherDocumentContents() {
return otherDocumentContents;
}
@Entity
@Table(name = "DOCUMENT_DATA")
public class DocumentData {
private Long id;
/**
* The binary content (java.sql.Blob)
*/
private Blob binaryContent;
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
@Column(name = "DOD_ID", nullable = false)
public Long getId() {
return id;
}
@Lob
@Column(name = "DOD_CONTENT")
public Blob getBinaryContent() {
return binaryContent;
}
以下是我的Spring和Hibernate配置的主要参数:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="it.paoloyx.blobcrud.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.jdbc.batch_size">0</prop>
<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
</props>
</property>
</bean>
<bean class="org.springframework.orm.hibernate4.HibernateTransactionManager"
id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
我有一个服务类,它委托给一个存储库类:
@Transactional
public class DocumentManagerImpl implements DocumentManager {
DocumentVersionDao documentVersionDao;
public void setDocumentVersionDao(DocumentVersionDao documentVersionDao) {
this.documentVersionDao = documentVersionDao;
}
现在是存储库类的相关摘录:
public class DocumentVersionDaoHibernate implements DocumentVersionDao {
@Autowired
private SessionFactory sessionFactory;
@Override
public DocumentVersion saveOrUpdate(DocumentVersion record) {
this.sessionFactory.getCurrentSession().saveOrUpdate(record);
return record;
}
导致错误的JUnit测试
如果我运行下面的单元测试,就会出现上述错误(java.lang.OutOfMemoryError:java堆空间
):
相同的代码适用于PostreSQL 9安装。正在将图像写入数据库中。
在调试代码时,我发现PostgreSQL jdbc驱动程序使用缓冲输出流在数据库中写入数据……而Oracle OJDBC驱动程序试图一次分配所有表示映像的字节[]
从错误堆栈:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
错误是否由此行为引起?
有人能给我一些关于这个问题的见解吗
谢谢大家
使用JConsole进行内存测试
多亏了针对我的问题所收到的建议,我尝试使用两种不同的jdbc驱动程序(一种用于PostgreSQL,另一种用于Oracle)进行一些简单的测试,以显示代码的内存使用情况。
测试设置:
java.lang.OutOfMemoryError:java heap space
)
有没有人能给我更多的见解?
非常感谢您的帮助:)就我个人而言,我使用Hibernate在Oracle BLOB列中存储高达200MB的文件,因此我可以保证它能正常工作。所以 您应该尝试更新版本的Oracle JDBC驱动程序。这种使用字节数组而不是流的行为似乎随着时间的推移发生了一些变化。并且驱动程序是向后兼容的。我不确定,这是否能解决你的问题,但它对我有效。 此外,您应该切换到org.hibernate.dialent.oracle10galent,这将取消对oracle.jdbc.driver包的使用,而代之以oracle.jdbc,这可能也会有所帮助。您是否尝试在会话工厂中定义oracle及其版本 以下是一个例子:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="oracleDocDataSource"/>
<property name="annotatedClasses">
<list>
...
</list>
</property>
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.OracleLobHandler">
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor"/>
</property>
</bean>
</property>
</bean>
...
更新
我刚刚意识到这篇演讲是关于hibernate 4的。这不是最好的解决方案,但您可以允许Java使用-Xmx参数使用更多内存 编辑: 你应该试着更深入地分析这个问题,试着使用它。它可以帮助您查看内存负载 即使使用Postgres,您也可能没有达到堆大小限制,但不会超过它,因为加载的驱动程序占用的内存要少一点
在默认设置下,heam大小限制约为物理内存的一半。试试你能把多大的blob保存到postgres中。在尝试使用“blob”类型进行映射时,我遇到了与你相同的问题。以下是我在hibernate网站上发表的一篇文章的链接: 冬眠3.6.9
Oracle驱动程序11.2.0.2.0
Oracle数据库11.2.0.2.0 为了解决这个问题,我使用了具有Blob自定义用户类型的代码,返回类型为java.sql.Blob 以下是此用户类型的关键方法实现:
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
Blob blob = rs.getBlob(names[0]);
if (blob == null)
return null;
return blob;
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, sqlTypes()[0]);
}
else {
InputStream in = null;
OutputStream out = null;
// oracle.sql.BLOB
BLOB tempBlob = BLOB.createTemporary(st.getConnection(), true, BLOB.DURATION_SESSION);
tempBlob.open(BLOB.MODE_READWRITE);
out = tempBlob.getBinaryOutputStream();
Blob valueAsBlob = (Blob) value;
in = valueAsBlob.getBinaryStream();
StreamUtil.toOutput(in, out);
out.flush();
StreamUtil.close(out);
tempBlob.close();
st.setBlob(index, tempBlob);
StreamUtil.close(in);
}
}
当我在Oracle和Hibernate上遇到同样的问题时,我发现了这个问题。问题出在Hibernate blob中
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.toByteArray(ByteArrayOutputStream.java:133)
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="oracleDocDataSource"/>
<property name="annotatedClasses">
<list>
...
</list>
</property>
<property name="lobHandler">
<bean class="org.springframework.jdbc.support.lob.OracleLobHandler">
<property name="nativeJdbcExtractor">
<bean class="org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor"/>
</property>
</bean>
</property>
</bean>
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
Blob blob = rs.getBlob(names[0]);
if (blob == null)
return null;
return blob;
}
public void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
if (value == null) {
st.setNull(index, sqlTypes()[0]);
}
else {
InputStream in = null;
OutputStream out = null;
// oracle.sql.BLOB
BLOB tempBlob = BLOB.createTemporary(st.getConnection(), true, BLOB.DURATION_SESSION);
tempBlob.open(BLOB.MODE_READWRITE);
out = tempBlob.getBinaryOutputStream();
Blob valueAsBlob = (Blob) value;
in = valueAsBlob.getBinaryStream();
StreamUtil.toOutput(in, out);
out.flush();
StreamUtil.close(out);
tempBlob.close();
st.setBlob(index, tempBlob);
StreamUtil.close(in);
}
}
public class Oracle10DialectWithoutInputStreamToInsertBlob extends Oracle10gDialect {
public boolean useInputStreamToInsertBlob() {
return false;
}
}