mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-18 00:55:16 +00:00
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 @@ private long getLength() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getStream() throws SQLException {
|
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 {
|
try {
|
||||||
if ( needsReset ) {
|
if ( needsReset ) {
|
||||||
stream.reset();
|
binaryStream.getInputStream().reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch ( IOException ioe) {
|
catch ( IOException ioe) {
|
||||||
throw new SQLException("could not reset reader");
|
throw new SQLException("could not reset reader");
|
||||||
}
|
}
|
||||||
needsReset = true;
|
needsReset = true;
|
||||||
return stream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException if any methods other than
|
* @throws UnsupportedOperationException if any methods other than
|
||||||
|
<<<<<<< HEAD
|
||||||
* {@link Blob#length}, {@link Blob#getBinaryStream}, {@link Blob#getBytes}, {@link Blob#free},
|
* {@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.
|
* or toString/equals/hashCode are invoked.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ -105,7 +117,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
|
|||||||
return Long.valueOf( getLength() );
|
return Long.valueOf( getLength() );
|
||||||
}
|
}
|
||||||
if ( "getUnderlyingStream".equals( methodName ) ) {
|
if ( "getUnderlyingStream".equals( methodName ) ) {
|
||||||
return binaryStream;
|
return getUnderlyingStream(); // Reset stream if needed.
|
||||||
}
|
}
|
||||||
if ( "getBinaryStream".equals( methodName ) ) {
|
if ( "getBinaryStream".equals( methodName ) ) {
|
||||||
if ( argCount == 0 ) {
|
if ( argCount == 0 ) {
|
||||||
|
@ -76,13 +76,16 @@ protected long getLength() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected InputStream getAsciiStream() throws SQLException {
|
protected InputStream getAsciiStream() throws SQLException {
|
||||||
resetIfNeeded();
|
return new ReaderInputStream( getCharacterStream() );
|
||||||
return new ReaderInputStream( characterStream.asReader() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Reader getCharacterStream() throws SQLException {
|
protected Reader getCharacterStream() throws SQLException {
|
||||||
|
return getUnderlyingStream().asReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CharacterStream getUnderlyingStream() throws SQLException {
|
||||||
resetIfNeeded();
|
resetIfNeeded();
|
||||||
return characterStream.asReader();
|
return characterStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getSubString(long start, int length) {
|
protected String getSubString(long start, int length) {
|
||||||
@ -95,8 +98,10 @@ protected String getSubString(long start, int length) {
|
|||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException if any methods other than {@link Clob#length()},
|
* @throws UnsupportedOperationException if any methods other than {@link Clob#length},
|
||||||
* {@link Clob#getAsciiStream()}, or {@link Clob#getCharacterStream()} are invoked.
|
* {@link Clob#getAsciiStream}, {@link Clob#getCharacterStream},
|
||||||
|
* {@link ClobImplementer#getUnderlyingStream}, {@link Clob#getSubString},
|
||||||
|
* {@link Clob#free}, or toString/equals/hashCode are invoked.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "UnnecessaryBoxing" })
|
@SuppressWarnings({ "UnnecessaryBoxing" })
|
||||||
@ -108,7 +113,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
|
|||||||
return Long.valueOf( getLength() );
|
return Long.valueOf( getLength() );
|
||||||
}
|
}
|
||||||
if ( "getUnderlyingStream".equals( methodName ) ) {
|
if ( "getUnderlyingStream".equals( methodName ) ) {
|
||||||
return characterStream;
|
return getUnderlyingStream(); // Reset stream if needed.
|
||||||
}
|
}
|
||||||
if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) {
|
if ( "getAsciiStream".equals( methodName ) && argCount == 0 ) {
|
||||||
return getAsciiStream();
|
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
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.type.descriptor.java;
|
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.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
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
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@ -84,4 +88,14 @@ public void testExternalization() {
|
|||||||
fail( "SQLException accessing blob : " + e.getMessage() );
|
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 @@ public boolean isMatch(Dialect dialect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class UsesInputStreamToInsertBlob implements DialectCheck {
|
||||||
|
public boolean isMatch(Dialect dialect) {
|
||||||
|
return dialect.useInputStreamToInsertBlob();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SupportsIdentityColumns implements DialectCheck {
|
public static class SupportsIdentityColumns implements DialectCheck {
|
||||||
public boolean isMatch(Dialect dialect) {
|
public boolean isMatch(Dialect dialect) {
|
||||||
return dialect.supportsIdentityColumns();
|
return dialect.supportsIdentityColumns();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user