HHH-8193 - Reset input stream before binding
Conflicts: hibernate-core/src/main/java/org/hibernate/engine/jdbc/BlobProxy.java hibernate-core/src/test/java/org/hibernate/test/type/descriptor/java/BlobDescriptorTest.java
This commit is contained in:
parent
2ecde32811
commit
6c2937e3c8
|
@ -75,24 +75,36 @@ public class BlobProxy implements InvocationHandler {
|
|||
}
|
||||
|
||||
private InputStream getStream() throws SQLException {
|
||||
final InputStream stream = binaryStream.getInputStream();
|
||||
return getUnderlyingStream().getInputStream();
|
||||
}
|
||||
|
||||
private BinaryStream getUnderlyingStream() throws SQLException {
|
||||
resetIfNeeded();
|
||||
return binaryStream;
|
||||
}
|
||||
|
||||
private void resetIfNeeded() throws SQLException {
|
||||
try {
|
||||
if ( needsReset ) {
|
||||
stream.reset();
|
||||
binaryStream.getInputStream().reset();
|
||||
}
|
||||
}
|
||||
catch ( IOException ioe) {
|
||||
throw new SQLException("could not reset reader");
|
||||
}
|
||||
needsReset = true;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws UnsupportedOperationException if any methods other than
|
||||
<<<<<<< HEAD
|
||||
* {@link Blob#length}, {@link Blob#getBinaryStream}, {@link Blob#getBytes}, {@link Blob#free},
|
||||
=======
|
||||
* {@link Blob#length}, {@link BlobImplementer#getUnderlyingStream},
|
||||
* {@link Blob#getBinaryStream}, {@link Blob#getBytes}, {@link Blob#free},
|
||||
>>>>>>> d7dae6c... HHH-8193 - Reset input stream before binding
|
||||
* or toString/equals/hashCode are invoked.
|
||||
*/
|
||||
@Override
|
||||
|
@ -105,7 +117,7 @@ public class BlobProxy implements InvocationHandler {
|
|||
return Long.valueOf( getLength() );
|
||||
}
|
||||
if ( "getUnderlyingStream".equals( methodName ) ) {
|
||||
return binaryStream;
|
||||
return getUnderlyingStream(); // Reset stream if needed.
|
||||
}
|
||||
if ( "getBinaryStream".equals( methodName ) ) {
|
||||
if ( argCount == 0 ) {
|
||||
|
|
|
@ -76,13 +76,16 @@ public class ClobProxy implements InvocationHandler {
|
|||
}
|
||||
|
||||
protected InputStream getAsciiStream() throws SQLException {
|
||||
resetIfNeeded();
|
||||
return new ReaderInputStream( characterStream.asReader() );
|
||||
return new ReaderInputStream( getCharacterStream() );
|
||||
}
|
||||
|
||||
protected Reader getCharacterStream() throws SQLException {
|
||||
return getUnderlyingStream().asReader();
|
||||
}
|
||||
|
||||
protected CharacterStream getUnderlyingStream() throws SQLException {
|
||||
resetIfNeeded();
|
||||
return characterStream.asReader();
|
||||
return characterStream;
|
||||
}
|
||||
|
||||
protected String getSubString(long start, int length) {
|
||||
|
@ -95,8 +98,10 @@ public class ClobProxy implements InvocationHandler {
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @throws UnsupportedOperationException if any methods other than {@link Clob#length()},
|
||||
* {@link Clob#getAsciiStream()}, or {@link Clob#getCharacterStream()} are invoked.
|
||||
* @throws UnsupportedOperationException if any methods other than {@link Clob#length},
|
||||
* {@link Clob#getAsciiStream}, {@link Clob#getCharacterStream},
|
||||
* {@link ClobImplementer#getUnderlyingStream}, {@link Clob#getSubString},
|
||||
* {@link Clob#free}, or toString/equals/hashCode are invoked.
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "UnnecessaryBoxing" })
|
||||
|
@ -108,7 +113,7 @@ public class ClobProxy implements InvocationHandler {
|
|||
return Long.valueOf( getLength() );
|
||||
}
|
||||
if ( "getUnderlyingStream".equals( methodName ) ) {
|
||||
return characterStream;
|
||||
return getUnderlyingStream(); // Reset stream if needed.
|
||||
}
|
||||
if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) {
|
||||
return getAsciiStream();
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
|
||||
* third-party contributors as indicated by either @author tags or express
|
||||
* copyright attribution statements applied by the authors. All
|
||||
* third-party contributions are distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.annotations.lob.locator;
|
||||
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@Entity
|
||||
public class LobHolder {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private Clob clobLocator;
|
||||
|
||||
private Blob blobLocator;
|
||||
|
||||
private Integer counter;
|
||||
|
||||
public LobHolder() {
|
||||
}
|
||||
|
||||
public LobHolder(Blob blobLocator, Clob clobLocator, Integer counter) {
|
||||
this.blobLocator = blobLocator;
|
||||
this.clobLocator = clobLocator;
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Clob getClobLocator() {
|
||||
return clobLocator;
|
||||
}
|
||||
|
||||
public void setClobLocator(Clob clobLocator) {
|
||||
this.clobLocator = clobLocator;
|
||||
}
|
||||
|
||||
public Blob getBlobLocator() {
|
||||
return blobLocator;
|
||||
}
|
||||
|
||||
public void setBlobLocator(Blob blobLocator) {
|
||||
this.blobLocator = blobLocator;
|
||||
}
|
||||
|
||||
public Integer getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
public void setCounter(Integer counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2009 by Red Hat Inc and/or its affiliates or by
|
||||
* third-party contributors as indicated by either @author tags or express
|
||||
* copyright attribution statements applied by the authors. All
|
||||
* third-party contributions are distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.annotations.lob.locator;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.testing.DialectChecks;
|
||||
import org.hibernate.testing.RequiresDialectFeature;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.hibernate.type.descriptor.java.DataHelper;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
public class LobLocatorTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { LobHolder.class };
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific JDBC drivers (e.g. SQL Server) may not automatically rewind bound input stream
|
||||
* during statement execution. Such behavior results in error message similar to:
|
||||
* {@literal The stream value is not the specified length. The specified length was 4, the actual length is 0.}
|
||||
*/
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8193")
|
||||
@RequiresDialectFeature(DialectChecks.UsesInputStreamToInsertBlob.class)
|
||||
public void testStreamResetBeforeParameterBinding() throws SQLException {
|
||||
final Session session = openSession();
|
||||
|
||||
session.getTransaction().begin();
|
||||
LobHolder entity = new LobHolder(
|
||||
session.getLobHelper().createBlob( "blob".getBytes() ),
|
||||
session.getLobHelper().createClob( "clob" ), 0
|
||||
);
|
||||
session.persist( entity );
|
||||
session.getTransaction().commit();
|
||||
|
||||
final Integer updatesLimit = 3;
|
||||
|
||||
for ( int i = 1; i <= updatesLimit; ++i ) {
|
||||
session.getTransaction().begin();
|
||||
entity = (LobHolder) session.get( LobHolder.class, entity.getId() );
|
||||
entity.setCounter( i );
|
||||
entity = (LobHolder) session.merge( entity );
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
|
||||
session.getTransaction().begin();
|
||||
entity = (LobHolder) session.get( LobHolder.class, entity.getId() );
|
||||
entity.setBlobLocator( session.getLobHelper().createBlob( "updated blob".getBytes() ) );
|
||||
entity.setClobLocator( session.getLobHelper().createClob( "updated clob" ) );
|
||||
entity = (LobHolder) session.merge( entity );
|
||||
session.getTransaction().commit();
|
||||
|
||||
session.clear();
|
||||
|
||||
session.getTransaction().begin();
|
||||
checkState( "updated blob".getBytes(), "updated clob", updatesLimit, (LobHolder) session.get( LobHolder.class, entity.getId() ) );
|
||||
session.getTransaction().commit();
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
private void checkState(byte[] blob, String clob, Integer counter, LobHolder entity) throws SQLException {
|
||||
Assert.assertEquals( counter, entity.getCounter() );
|
||||
Assert.assertArrayEquals( blob, DataHelper.extractBytes( entity.getBlobLocator().getBinaryStream() ) );
|
||||
Assert.assertEquals( clob, DataHelper.extractString( entity.getClobLocator() ) );
|
||||
}
|
||||
}
|
|
@ -22,20 +22,24 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.test.type.descriptor.java;
|
||||
import java.sql.Blob;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.engine.jdbc.BlobProxy;
|
||||
import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.DataHelper;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.Blob;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.engine.jdbc.BlobImplementer;
|
||||
import org.hibernate.engine.jdbc.BlobProxy;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
|
||||
import org.hibernate.type.descriptor.java.DataHelper;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -84,4 +88,14 @@ public class BlobDescriptorTest extends AbstractDescriptorTest<Blob> {
|
|||
fail( "SQLException accessing blob : " + e.getMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-8193" )
|
||||
public void testStreamResetOnAccess() throws IOException {
|
||||
byte[] bytes = new byte[] { 1, 2, 3, 4 };
|
||||
BlobImplementer blob = (BlobImplementer) BlobProxy.generateProxy( bytes );
|
||||
int value = blob.getUnderlyingStream().getInputStream().read();
|
||||
// Call to BlobImplementer#getUnderlyingStream() should mark input stream for reset.
|
||||
assertEquals( bytes.length, blob.getUnderlyingStream().getInputStream().available() );
|
||||
}
|
||||
}
|
|
@ -44,6 +44,12 @@ abstract public class DialectChecks {
|
|||
}
|
||||
}
|
||||
|
||||
public static class UsesInputStreamToInsertBlob implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.useInputStreamToInsertBlob();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SupportsIdentityColumns implements DialectCheck {
|
||||
public boolean isMatch(Dialect dialect) {
|
||||
return dialect.supportsIdentityColumns();
|
||||
|
|
Loading…
Reference in New Issue