尝试在Oracle 11g数据库中保存大文件时遇到问题,我使用Spring和Hibernate 3.6.9作为JPA实现。
持久性使用Spring并配置如下:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
<property name="generateDdl" value="false" />
</bean>
</property>
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.connection.SetBigStringTryClob">true</prop>
<prop key="hibernate.jdbc.batch_size">0</prop>
<prop key="hibernate.jdbc.use_streams_for_binary">true</prop>
</props>
</property>
</bean>
我想要保留的实体包含一个定义如下的列:
@Column(name = "FILE_OBJECT")
@Lob
private byte[] fileObject;
...
我加入了这个stackTrace:
java.lang.OutOfMemoryError: Java heap space
at java.lang.reflect.Array.newArray(Native Method)
at java.lang.reflect.Array.newInstance(Array.java:52)
at org.hibernate.type.descriptor.java.ArrayMutabilityPlan.deepCopyNotNull(ArrayMutabilityPlan.java:44)
at org.hibernate.type.descriptor.java.MutableMutabilityPlan.deepCopy(MutableMutabilityPlan.java:58)
at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:314)
at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:310)
at org.hibernate.type.TypeHelper.deepCopy(TypeHelper.java:68)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:302)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:143)
at org.hibernate.ejb.event.EJB3MergeEventListener.saveWithGeneratedId(EJB3MergeEventListener.java:62)
at org.hibernate.event.def.DefaultMergeEventListener.saveTransientEntity(DefaultMergeEventListener.java:415)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:341)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:303)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:258)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:877)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:859)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:279)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:450)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:336)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:303)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:258)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:877)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:859)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:279)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
我google了很多,但我发现没有任何帮助,我仍然会得到同样的错误,如果有人可以帮忙吗? 一些链接提到使用另一个Oracle驱动程序,但正如我在堆栈跟踪中看到的,问题出在Hibernate代码中,我不认为问题与DB有关(我使用最新的驱动程序ojdbc6)。
答案 0 :(得分:0)
最后,我认为我找到了一个解决方案,定义了一个链接到字节数组的自定义类型。 一些解释:
首先,我创建了CustomMaterializedBlobType,Hibernate使用它来建立一个byte和Blob数组之间的链接,这里,它的主要作用是提供一个不是数组真实副本的MutabilityPlan(自定义)(它是我的问题):
import org.hibernate.type.AlternativeLobTypes.BlobTypes;
import org.hibernate.type.MaterializedBlobType;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
public class CustomMaterializedBlobType extends MaterializedBlobType{
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomMaterializedBlobType() {
super();
}
public CustomMaterializedBlobType(SqlTypeDescriptor sqlTypeDescriptor, BlobTypes<byte[], MaterializedBlobType> blobTypes) {
super(sqlTypeDescriptor, blobTypes);
}
@SuppressWarnings("unchecked")
@Override
protected MutabilityPlan<byte[]> getMutabilityPlan() {
return CustomArrayMutabilityPlan.INSTANCE;
}
}
现在,MutabilityPlan除了返回相同的数组而不是创建一个新数组之外别无其他: import org.hibernate.type.descriptor.java.ArrayMutabilityPlan;
public class CustomArrayMutabilityPlan<T> extends ArrayMutabilityPlan<T>{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final CustomArrayMutabilityPlan INSTANCE = new CustomArrayMutabilityPlan();
@SuppressWarnings({ "unchecked", "SuspiciousSystemArraycopy" })
public T deepCopyNotNull(T value) {
if ( ! value.getClass().isArray() ) {
// ugh! cannot find a way to properly define the type signature here to
throw new IllegalArgumentException( "Value was not an array [" + value.getClass().getName() + "]" );
}
return value;
}
}
我需要将这个custimized类型链接到Lob,它是在我的实体中完成的:
@Lob
@Type(type = "CustomMaterializedBlobType")
@Column(name = "FILE_OBJECT")
private byte[] fileObject;
这不是一个非常好的解决方案,因为这样,我使我的应用程序依赖于Hibernate而不是JPA(类型注释来自Hibernate)。如果有人知道如何在完整的JPA中做同样的事情?