首页 > 解决方案 > 如何使用 hibernate 和 spring @transactional 释放 blob?

问题描述

我有一个 Spring 和 Hibernate 项目。在我的实现中,有多个模型我必须从文件中读取并将其存储为 Blob。为此,我有一个用 Blob 定义的抽象类,如下所示。所有需要存储 blob 的模型都扩展了这个类。

@MappedSuperclass
public abstract class AbstractContentAwareBean {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    protected Long id;

    public AbstractContentAwareBean() {}

    @Lob
    @Column(name = "content", columnDefinition = "LONGBLOB")
    protected Blob content;

    public void setContent(Blob content) {
        this.content = content;
    }

    public void setContent(Session currentSession, InputStream inputStream, long size) {
        Blob blob = Hibernate.getLobCreator(currentSession).createBlob(inputStream, size);
        setContent(blob);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @JsonIgnore
    public Blob getContent() {
        return content;
    }
}

这是一个示例用法。

@Transactional
public void sampleMethod() throws Exception {
    try {
        // I have entity manager auto-wired. 
        Session session = entityManager.unwrap(Session.class);

        File file = new File("/tmp/filepath.txt");
        MySampleModel sm = new MySampleModel();
        sm.setContent(session, new FileInputStream(file), file.length());
        // I have service auto wired. persistSampleModel simply calls mySampleRepository.save(sm)
        mySampleModelService.persistSampleModel(sm);

    } catch (Exception e) {
        // log and throw
        throw new Exception("Error occurred", e);
    }
}

现在我面临的问题是我这里打开的FileInputStream没有释放。我用lsof -p <process_id>命令检查了这些文件描述符。

我检查了 Blob 创建的实现路径,流通过org.hibernate.engine.jdbc.BlobProxy并最终在org.hibernate.engine.jdbc.BlobProxy.StreamBackedBinaryStream其中有一个release方法,但它不会自动调用。因此,我相信我们必须Blob.free()手动调用该方法才能调用该版本。

尝试1:try-with-resources

当我用try-with-resources. 我相信这里的问题是只有在事务提交时才读取流,然后try-with-resources关闭流。

Caused by: java.io.IOException: Stream Closed
    at java.io.FileInputStream.readBytes(Native Method) ~[?:1.8.0_151]
    at java.io.FileInputStream.read(FileInputStream.java:255) ~[?:1.8.0_151]
    at com.mysql.cj.jdbc.PreparedStatement.readblock(PreparedStatement.java:2612) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.streamToBytes(PreparedStatement.java:4302) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.fillSendPacket(PreparedStatement.java:2144) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:2013) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.executeUpdateInternal(PreparedStatement.java:1970) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.executeLargeUpdate(PreparedStatement.java:4984) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at com.mysql.cj.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1955) ~[mysql-connector-java-6.0.5.jar:6.0.5]
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:205) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:57) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:42) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2909) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3480) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:626) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:280) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:261) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:306) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:318) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:275) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:804) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:771) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener$1.cascade(JpaPersistEventListener.java:80) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:458) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:383) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:414) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:252) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:182) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:113) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:780) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:765) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at com.sun.proxy.$Proxy76.persist(Unknown Source) ~[?:?]
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:508) ~[spring-data-jpa-1.11.6.RELEASE.jar:?]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504) ~[spring-data-commons-1.13.6.RELEASE.jar:?]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489) ~[spring-data-commons-1.13.6.RELEASE.jar:?]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.6.RELEASE.jar:?]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56) ~[spring-data-commons-1.13.6.RELEASE.jar:?]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
    ... 48 more

尝试2:javax.persistence.PostPersist注解方法调用Blob.free()方法

@PostPersist
public void postPersist(){
    try {
        this.content.free();
    } catch (SQLException ignore) {}
}

在大多数情况下,这比尝试使用资源尝试成功。然而,CascadeType.MERGE在一个模型使用另一个模型的情况下,这种尝试也会失败。

Caused by: org.hibernate.exception.GenericJDBCException: unable to merge BLOB data
        at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.dialect.Dialect$3.mergeBlob(Dialect.java:597) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.BlobType.getReplacement(BlobType.java:39) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.BlobType.getReplacement(BlobType.java:20) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.AbstractStandardBasicType.replace(AbstractStandardBasicType.java:353) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.TypeHelper.replace(TypeHelper.java:180) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:394) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:203) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:903) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:873) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:458) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:383) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:461) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:202) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:881) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:867) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at com.sun.proxy.$Proxy160.merge(Unknown Source) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at com.sun.proxy.$Proxy160.merge(Unknown Source) ~[?:?]
        at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:511) ~[spring-data-jpa-1.11.5.RELEASE.jar:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        ... 137 more
Caused by: javax.sql.rowset.serial.SerialException: Error: You cannot call a method on a SerialBlob instance once free() has been called.
        at javax.sql.rowset.serial.SerialBlob.isValid(SerialBlob.java:592) ~[?:1.8.0_151]
        at javax.sql.rowset.serial.SerialBlob.getBinaryStream(SerialBlob.java:220) ~[?:1.8.0_151]
        at org.hibernate.dialect.Dialect$3.mergeBlob(Dialect.java:594) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.BlobType.getReplacement(BlobType.java:39) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.BlobType.getReplacement(BlobType.java:20) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.AbstractStandardBasicType.replace(AbstractStandardBasicType.java:353) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.type.TypeHelper.replace(TypeHelper.java:180) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:394) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:203) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:903) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:873) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:458) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:383) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:193) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:126) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:461) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:202) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:881) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:867) ~[hibernate-core-5.2.10.Final.jar:5.2.10.Final]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at com.sun.proxy.$Proxy160.merge(Unknown Source) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298) ~[spring-orm-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at com.sun.proxy.$Proxy160.merge(Unknown Source) ~[?:?]
        at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:511) ~[spring-data-jpa-1.11.5.RELEASE.jar:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_151]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_151]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_151]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_151]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56) ~[spring-data-commons-1.13.5.RELEASE.jar:?]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.10.RELEASE.jar:4.3.10.RELEASE]
        ... 137 more

问题:

  1. hibernate 或 spring 通常会自动释放 Blob 吗?如果是这样可能是配置问题吗?

  2. 如果hibernate或者spring没有自动释放blob,这种场景下怎么释放blob呢?

让我也分享一下我的配置。希望你们中的一些人可以帮助我。提前致谢。

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
      p:packagesToScan="org.sample.model" p:dataSource-ref="dataSource">

    <property name="jpaProperties">
        <props>
            <prop key="hibernate.hbm2ddl.auto">${spring.jpa.hibernate.ddl-auto}</prop>
            <prop key="hibernate.implicit_naming_strategy">${spring.jpa.hibernate.naming.implicit-strategy}</prop>
            <prop key="hibernate.physical_naming_strategy">${spring.jpa.hibernate.naming.physical-strategy}</prop>
        </props>
    </property>

    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="generateDdl" value="false"/>
            <property name="showSql" value="${spring.jpa.show-sql}"/>
            <property name="databasePlatform" value="${spring.jpa.database-platform}"/>
        </bean>
    </property>
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

<jpa:repositories base-package="org.sample.repository"/>

<context:component-scan base-package="org.sample.repository"/>

PS:FileInputStream上述示例方法中的使用是为了简化问题。实际上,在代码中,我使用UltraESB-X API从 ESB 创建的临时文件中读取流。

标签: javaspringhibernateblobinputstream

解决方案


由于没有答案,我会发布我的。我们可以想出的解决方案是编写一个类来包装输入流并在读取完成后自动关闭它。

/**
 * Purpose of this class is to wrap an input stream and close it automatically once the reading is complete.
 *
 * @author Rajind Ruparathna
 */
public class SelfClosingInputStream extends FilterInputStream {

    private static final int EOF = -1;

    private boolean isClosed = false;
    private Long length;
    private Long count = 0L;

    public SelfClosingInputStream(InputStream is, Long length) {
        super(is);
        this.length = length;
    }

    public SelfClosingInputStream(InputStream is) {
        super(is);
        length = -1L;
    }

    @Override
    public int read() throws IOException {
        try {
            if (!isClosed)  {
                int val = super.read();
                count = count + 1;
                if ((length != -1L && count.equals(length)) || val == EOF) {
                    close();
                    isClosed = true;
                }
                return val;
            } else {
                return EOF;
            }
        } catch (IOException e) {
            close();
            isClosed = true;
            throw e;
        }
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        try {
            if (!isClosed)  {
                int val = super.read(b, off, len);
                count = count + val;
                if ((length != -1L && count.equals(length)) || val == EOF) {
                    close();
                    isClosed = true;
                }
                return val;
            } else {
                return EOF;
            }
        } catch (IOException e) {
            close();
            isClosed = true;
            throw e;
        }
    }
}

该类在类中使用AbstractContentAwareBean如下。

public void setContent(Session currentSession, InputStream inputStream, long size) {
    Blob blob = Hibernate.getLobCreator(currentSession).createBlob(new SelfClosingInputStream(inputStream, size), size);
    setContent(blob);
}

想感谢那些帮助我编写课程的人,包括在代码审查中回答我的问题SelfClosingInputStream的人。


推荐阅读