Blobs, InputStream and Envers: Could not reset reader

Hello everyone,

I have a problem with Hibernate, Blobs, and Envers. I have an entity that has a java.sql.Blob mapped like this in a audited entity:

@Column(name = "ASSET_DATA")
@Lob
@Basic(fetch = FetchType.LAZY)
private Blob data;

When I now try to update the entity with a file uploaded using RestEasy using this code:

@POST
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @AuthorizationRequired
    @Transactional(Transactional.TxType.REQUIRED)
    public String uploadFile(
        @PathParam("assetIdentifier")
        final String assetIdentifier
        final MultipartFormDataInput input
) {

    Asset asset = assetRepo.getAsset(assetIdentifier);

    final Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
    final List<InputPart> inputParts = uploadForm.get("fileData");
    for (final InputPart inputPart : inputParts) {
        try {
            Blob blob = BlobProxy.generateProxy(inputPart.getBody(InputStream.class, null), -1);
            asset.setData(blob);
           
            assetRepo.save(asset); // calls EntitiyManager#persist(asset);
        } catch(IOExpection ex) {
             // Exception handling omitted for this topic.
        }    
    }
   
}

I then get the following exception (only the last two levels of stack trace shown here, if you need more please let me know):

Caused by: org.hibernate.exception.GenericJDBCException: could not insert: [org.example.assets.Asset_AUD]
        at org.hibernate@5.3.20.Final//org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
        at org.hibernate@5.3.20.Final//org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3210)
        at org.hibernate@5.3.20.Final//org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3706)
        at org.hibernate@5.3.20.Final//org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:90)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
        at org.hibernate@5.3.20.Final//org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
        at org.hibernate@5.3.20.Final//org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
        at org.hibernate@5.3.20.Final//org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1472)
        ... 134 more
Caused by: java.sql.SQLException: could not reset reader
        at org.hibernate@5.3.20.Final//org.hibernate.engine.jdbc.BlobProxy.resetIfNeeded(BlobProxy.java:75)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.jdbc.BlobProxy.getUnderlyingStream(BlobProxy.java:64)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.jdbc.BlobProxy.getStream(BlobProxy.java:60)
        at org.hibernate@5.3.20.Final//org.hibernate.engine.jdbc.BlobProxy.invoke(BlobProxy.java:101)
        at org.hibernate@5.3.20.Final//com.sun.proxy.$Proxy309.getBinaryStream(Unknown Source)
        at deployment.postgresql.jar//org.postgresql.jdbc.PgPreparedStatement.setBlob(PgPreparedStatement.java:1151)
        at org.jboss.ironjacamar.jdbcadapters@1.4.25.Final//org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.setBlob(WrappedPreparedStatement.java:1157)
        at org.hibernate@5.3.20.Final//org.hibernate.type.descriptor.sql.BlobTypeDescriptor$4$1.doBind(BlobTypeDescriptor.java:132)
        at org.hibernate@5.3.20.Final//org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:74)
        at org.hibernate@5.3.20.Final//org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:276)
        at org.hibernate@5.3.20.Final//org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:271)
        at org.hibernate@5.3.20.Final//org.hibernate.type.AbstractSingleColumnStandardBasicType.nullSafeSet(AbstractSingleColumnStandardBasicType.java:39)
        at org.hibernate@5.3.20.Final//org.hibernate.persister.entity.AbstractEntityPersister.dehydrate(AbstractEntityPersister.java:2886)
        at org.hibernate@5.3.20.Final//org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3183)
        ... 141 more

It looks like the InputStream provided by InputPart does not support resetting, but Hibernate needs to reset the stream because it has to insert the data twice…

Any idea how to solve this? We would like to have the Blob versioned, so any help is appreciated.

Best

Jens

It’s probably best to ensure you materialize the file in a temp file on the file system first to avoid a slow user connection block your transaction. Streaming within a transaction is never a good idea. So one way is to just create the temp file, write the stream into it and when that is finished, attach a blob that is based on a file input stream which is then resetable.

Doing that already. The error message is the same, but on further investigation, I found out that in that case the FileInputStream is closed by the BlobProxy before Hibernate tries to insert the data in the audit table.

If you think this is a bug in Hibernate Envers due to the blob being closed too early, please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(hibernate-test-case-templates/JPAUnitTestCase.java at main · hibernate/hibernate-test-case-templates · GitHub) that reproduces the issue.

Just created a bug: [HHH-14724] Metamodel generates invalid model classes for converters and user types - Hibernate JIRA

A link to the test case is in the issue.

Thanks for looking into that.

Sorry, wrong link. Correct link to the Jira issue: [HHH-14725] Using a InputStream with BlobProxy and Envers results in java.sql.SQLException: could not reset reader - Hibernate JIRA