diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java index 88bf2273a4..4ad979158c 100644 --- a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/BasicBlobTest.java @@ -4,9 +4,18 @@ */ package org.hibernate.orm.test.envers.integration.blob; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; -import static org.junit.Assert.fail; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import org.hamcrest.Matchers; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.engine.jdbc.proxy.BlobProxy; +import org.hibernate.envers.Audited; +import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; +import org.hibernate.orm.test.envers.Priority; +import org.hibernate.testing.SkipForDialect; +import org.junit.Test; import java.io.BufferedInputStream; import java.io.InputStream; @@ -14,16 +23,9 @@ import java.nio.file.Files; import java.nio.file.Path; import java.sql.Blob; -import org.hamcrest.Matchers; -import org.hibernate.engine.jdbc.proxy.BlobProxy; -import org.hibernate.envers.Audited; -import org.hibernate.orm.test.envers.BaseEnversJPAFunctionalTestCase; -import org.hibernate.orm.test.envers.Priority; -import org.junit.Test; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.fail; /** * @author Chris Cranford @@ -32,19 +34,19 @@ public class BasicBlobTest extends BaseEnversJPAFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { - return new Class[] { Asset.class }; + return new Class[] {Asset.class}; } @Test @Priority(10) - public void initData() { - final Path path = Path.of( getClass().getResource( "./blob.txt" ).getPath() ); + public void testGenerateProxyNoStream() { + final Path path = Path.of( Thread.currentThread().getContextClassLoader() + .getResource( "org/hibernate/orm/test/envers/integration/blob/blob.txt" ).getPath() ); doInJPA( this::entityManagerFactory, entityManager -> { - try { - final Asset asset = new Asset(); - asset.setFileName( "blob.txt" ); + final Asset asset = new Asset(); + asset.setFileName( "blob.txt" ); - final InputStream stream = new BufferedInputStream( Files.newInputStream( path ) ); + try (final InputStream stream = new BufferedInputStream( Files.newInputStream( path ) )) { assertThat( stream.markSupported(), Matchers.is( true ) ); // We use the method readAllBytes instead of passing the raw stream to the proxy @@ -70,6 +72,49 @@ public class BasicBlobTest extends BaseEnversJPAFunctionalTestCase { fail( "Failed to persist the entity" ); } } ); + + } + + @Test + @Priority(10) + @SkipForDialect(value = PostgreSQLDialect.class, + comment = "The driver closes the stream, so it cannot be reused by envers") + @SkipForDialect(value = SQLServerDialect.class, + comment = "The driver closes the stream, so it cannot be reused by envers") + public void testGenerateProxyStream() { + final Path path = Path.of( Thread.currentThread().getContextClassLoader() + .getResource( "org/hibernate/orm/test/envers/integration/blob/blob.txt" ).getPath() ); + + try (final InputStream stream = new BufferedInputStream( Files.newInputStream( path ) )) { + doInJPA( this::entityManagerFactory, entityManager -> { + final Asset asset = new Asset(); + asset.setFileName( "blob.txt" ); + + assertThat( stream.markSupported(), Matchers.is( true ) ); + + // We use the method readAllBytes instead of passing the raw stream to the proxy + // since this is the only guaranteed way that will work across all dialects in a + // deterministic way. Postgres and Sybase will automatically close the stream + // after the blob has been written by the driver, which prevents Envers from + // then writing the contents of the stream to the audit table. + // + // If the driver and dialect are known not to close the input stream after the + // contents have been written by the driver, then it's safe to pass the stream + // here instead and the stream will be automatically marked and reset so that + // Envers can serialize the data after Hibernate has done so. Dialects like + // H2, MySQL, Oracle, SQL Server work this way. + // + // + Blob blob = BlobProxy.generateProxy( stream, 9192L ); + + asset.setData( blob ); + entityManager.persist( asset ); + } ); + } + catch (Exception e) { + e.printStackTrace(); + fail( "Failed to persist the entity" ); + } } @Audited diff --git a/hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt b/hibernate-envers/src/test/resources/org/hibernate/orm/test/envers/integration/blob/blob.txt similarity index 100% rename from hibernate-envers/src/test/java/org/hibernate/orm/test/envers/integration/blob/blob.txt rename to hibernate-envers/src/test/resources/org/hibernate/orm/test/envers/integration/blob/blob.txt