HHH-12555 Fix merging of lazy loaded blobs/clobs/nclobs

It's better to avoid pushing UNFETCHED_PROPERTY to the types as it
requires all the types to take it into account.

TypeHelper looks like the only sensible caller that needs change.
This commit is contained in:
Guillaume Smet 2018-05-28 12:35:37 +02:00
parent c79ce44f84
commit 1af878166f
6 changed files with 91 additions and 26 deletions

View File

@ -71,11 +71,7 @@ public abstract class AbstractStandardBasicType<T>
}
protected T getReplacement(T original, T target, SharedSessionContractImplementor session) {
if ( original == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
return target;
}
else if ( !isMutable() ||
( target != LazyPropertyInitializer.UNFETCHED_PROPERTY && isEqual( original, target ) ) ) {
if ( !isMutable() || ( target != null && isEqual( original, target ) ) ) {
return original;
}
else {
@ -351,6 +347,10 @@ public abstract class AbstractStandardBasicType<T>
@Override
@SuppressWarnings({ "unchecked" })
public final Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache) {
if ( original == null && target == null ) {
return null;
}
return getReplacement( (T) original, (T) target, session );
}

View File

@ -7,8 +7,12 @@
package org.hibernate.type;
import java.sql.Blob;
import java.sql.SQLException;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
/**
@ -36,7 +40,23 @@ public class BlobType extends AbstractSingleColumnStandardBasicType<Blob> {
@Override
protected Blob getReplacement(Blob original, Blob target, SharedSessionContractImplementor session) {
if ( target == null ) {
return copyOriginalBlob( (Blob) original, session );
}
return session.getJdbcServices().getJdbcEnvironment().getDialect().getLobMergeStrategy().mergeBlob( original, target, session );
}
private Blob copyOriginalBlob(Blob original, SharedSessionContractImplementor session) {
try {
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator(
session );
return original == null
? lobCreator.createBlob( ArrayHelper.EMPTY_BYTE_ARRAY )
: lobCreator.createBlob( original.getBinaryStream(), original.length() );
}
catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert( e, "unable to merge BLOB data" );
}
}
}

View File

@ -7,7 +7,10 @@
package org.hibernate.type;
import java.sql.Clob;
import java.sql.SQLException;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.ClobTypeDescriptor;
@ -36,7 +39,21 @@ public class ClobType extends AbstractSingleColumnStandardBasicType<Clob> {
@Override
protected Clob getReplacement(Clob original, Clob target, SharedSessionContractImplementor session) {
return session.getJdbcServices().getJdbcEnvironment().getDialect().getLobMergeStrategy().mergeClob( original, target, session );
if ( target == null ) {
return copyOriginalClob( (Clob) original, session );
}
return session.getJdbcServices().getJdbcEnvironment().getDialect().getLobMergeStrategy().mergeClob( (Clob) original, (Clob) target, session );
}
private Clob copyOriginalClob(Clob original, SharedSessionContractImplementor session) {
try {
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session );
return original == null
? lobCreator.createClob( "" )
: lobCreator.createClob( original.getCharacterStream(), original.length() );
}
catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert( e, "unable to merge CLOB data" );
}
}
}

View File

@ -7,12 +7,15 @@
package org.hibernate.type;
import java.sql.NClob;
import java.sql.SQLException;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.java.NClobTypeDescriptor;
/**
* A type that maps between {@link java.sql.Types#CLOB CLOB} and {@link java.sql.Clob}
* A type that maps between {@link java.sql.Types#NCLOB NCLOB} and {@link java.sql.NClob}
*
* @author Gavin King
* @author Steve Ebersole
@ -36,7 +39,21 @@ public class NClobType extends AbstractSingleColumnStandardBasicType<NClob> {
@Override
protected NClob getReplacement(NClob original, NClob target, SharedSessionContractImplementor session) {
if ( target == null ) {
return copyOriginalNClob( original, session );
}
return session.getJdbcServices().getJdbcEnvironment().getDialect().getLobMergeStrategy().mergeNClob( original, target, session );
}
private NClob copyOriginalNClob(NClob original, SharedSessionContractImplementor session) {
try {
final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session );
return original == null
? lobCreator.createNClob( "" )
: lobCreator.createNClob( original.getCharacterStream(), original.length() );
}
catch (SQLException e) {
throw session.getJdbcServices().getSqlExceptionHelper().convert( e, "unable to merge NCLOB data" );
}
}
}

View File

@ -159,6 +159,9 @@ public class TypeHelper {
if ( original[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY || original[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
copied[i] = target[i];
}
else if ( target[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
copied[i] = types[i].replace( original[i], null, session, owner, copyCache );
}
else {
copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache );
}
@ -193,6 +196,9 @@ public class TypeHelper {
|| original[i] == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
copied[i] = target[i];
}
else if ( target[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
copied[i] = types[i].replace( original[i], null, session, owner, copyCache, foreignKeyDirection );
}
else {
copied[i] = types[i].replace( original[i], target[i], session, owner, copyCache, foreignKeyDirection );
}

View File

@ -6,6 +6,23 @@
*/
package org.hibernate.test.type;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Currency;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.internal.SessionImpl;
import org.hibernate.internal.util.SerializationHelper;
@ -42,26 +59,11 @@ import org.hibernate.type.TimeType;
import org.hibernate.type.TimeZoneType;
import org.hibernate.type.TimestampType;
import org.hibernate.type.TrueFalseType;
import org.hibernate.type.Type;
import org.hibernate.type.TypeHelper;
import org.hibernate.type.YesNoType;
import org.junit.Test;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Currency;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
*/
@ -344,8 +346,11 @@ public class TypeTest extends BaseUnitTestCase {
assertTrue( original == type.replace( original, copy, null, null, null ) );
// following tests assert that types work with properties not yet loaded in bytecode enhanced entities
assertSame( copy, type.replace( LazyPropertyInitializer.UNFETCHED_PROPERTY, copy, null, null, null ) );
assertNotEquals( LazyPropertyInitializer.UNFETCHED_PROPERTY, type.replace( original, LazyPropertyInitializer.UNFETCHED_PROPERTY, null, null, null ) );
Type[] types = new Type[]{ type };
assertSame( copy, TypeHelper.replace( new Object[]{ LazyPropertyInitializer.UNFETCHED_PROPERTY },
new Object[]{ copy }, types, null, null, null )[0] );
assertNotEquals( LazyPropertyInitializer.UNFETCHED_PROPERTY, TypeHelper.replace( new Object[]{ original },
new Object[]{ LazyPropertyInitializer.UNFETCHED_PROPERTY }, types, null, null, null )[0] );
assertTrue( type.isSame( original, copy ) );
assertTrue( type.isEqual( original, copy ) );