使用JPA / Hibernate保持Lob

时间:2013-11-01 11:12:06

标签: spring oracle hibernate jpa heap-memory

尝试在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)。

1 个答案:

答案 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中做同样的事情?