如何使用冬眠和春天@transactional释放斑点? [英] How to release blobs with hibernate and spring @transactional?

查看:59
本文介绍了如何使用冬眠和春天@transactional释放斑点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a project with Spring and Hibernate. In my implementation, there are multiple models where I have to read from a file and store it as a Blob. For this purpose, I have an abstract class defined with Blob as follows. All the model that need to store blobs extend this class.

@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>命令检查了,那些文件描述符仍然存在.

Now the problem I face is that the FileInputStream I open here is not released. I checked with lsof -p <process_id> command and those file descriptors remain.

我检查了Blob创建的实现路径,该流通过org.hibernate.engine.jdbc.BlobProxy并最终到达具有release方法的org.hibernate.engine.jdbc.BlobProxy.StreamBackedBinaryStream,但不会自动调用它.因此,我相信我们必须手动调用Blob.free()方法来调用发行版.

I checked the implementation path of Blob creation and the stream goes through org.hibernate.engine.jdbc.BlobProxy and ends up in org.hibernate.engine.jdbc.BlobProxy.StreamBackedBinaryStream which has a release method but it is not called automatically. Therefore I believe we have to call the Blob.free() method manually to get the release called.

尝试1:try-with-resources

Attempt 1: try-with-resources

当我用try-with-resources包装流时,得到以下跟踪.我认为这里的问题是,只有在事务提交并且此时try-with-resources已关闭流时,流才是只读的.

I get the following trace when I wrap the stream with try-with-resources. I believe the issue here is that the stream is read only when the transaction commits and by then try-with-resources has closed the stream.

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()方法

Attempt 2: javax.persistence.PostPersist annotated method to call Blob.free() method

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

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

This was successful in most of the cases than the try with resources attempt. Yet in CascadeType.MERGE cases where one model uses another, this attempt also fails.

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. 冬眠或春季通常会自动释放Blob吗?如果是这样,这可能是配置问题吗?

  1. Does hibernate or spring generally release the Blobs automatically? If so could it be a configuration problem?

如果冬眠或春季不能自动释放Blob,在这种情况下如何释放Blob?

If hibernate or spring does not release blob automatically, how can we release the Blob in such scenarios?

让我也分享我的收藏.希望你们中的一些人能帮助我.预先感谢.

Let me share my confugurations as well. Hope some of you could help me. Thanks in advance.

<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"/>

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

P.S: The usage of FileInputStream in the above sample method is to simplify the issue. Actually, in the code, I used an UltraESB-X API to read a stream from a temporary file created by the ESB.

推荐答案

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

Since there were no answers for this, I'll post mine. The solution we could come up with was to write a class which wraps an input stream and closes it automatically once the reading is complete.

/**
 * 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类中的用法如下.

This class was used in the AbstractContentAwareBean class as follows.

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

感谢在代码审查中回答我的问题的人们,包括我在内的人们帮助我编写了SelfClosingInputStream类.

Would like to thank the folks including the people who answered my question at code review for helping me write the SelfClosingInputStream class.

这篇关于如何使用冬眠和春天@transactional释放斑点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆