HHH-7468 Recreate toString value holder after deserialization
This commit is contained in:
parent
b76e7c4987
commit
e813d329bc
|
@ -102,7 +102,8 @@ libraries = [
|
||||||
shrinkwrap_api: 'org.jboss.shrinkwrap:shrinkwrap-api:1.0.0-beta-6',
|
shrinkwrap_api: 'org.jboss.shrinkwrap:shrinkwrap-api:1.0.0-beta-6',
|
||||||
shrinkwrap: 'org.jboss.shrinkwrap:shrinkwrap-impl-base:1.0.0-beta-6',
|
shrinkwrap: 'org.jboss.shrinkwrap:shrinkwrap-impl-base:1.0.0-beta-6',
|
||||||
validator: 'org.hibernate:hibernate-validator:4.2.0.Final',
|
validator: 'org.hibernate:hibernate-validator:4.2.0.Final',
|
||||||
h2: "com.h2database:h2:${h2Version}"
|
h2: "com.h2database:h2:${h2Version}",
|
||||||
|
mockito: 'org.mockito:mockito-core:1.9.0'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ dependencies {
|
||||||
testCompile( libraries.validation )
|
testCompile( libraries.validation )
|
||||||
testCompile( libraries.jandex )
|
testCompile( libraries.jandex )
|
||||||
testCompile( libraries.classmate )
|
testCompile( libraries.classmate )
|
||||||
|
testCompile( libraries.mockito )
|
||||||
testCompile( libraries.validator ) {
|
testCompile( libraries.validator ) {
|
||||||
// for test runtime
|
// for test runtime
|
||||||
transitive = true
|
transitive = true
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.cache.spi;
|
package org.hibernate.cache.spi;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
private final String entityName;
|
private final String entityName;
|
||||||
private final String tenantId;
|
private final String tenantId;
|
||||||
private final int hashCode;
|
private final int hashCode;
|
||||||
private final transient ValueHolder<String> toString;
|
private transient ValueHolder<String> toString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new key for a caching natural identifier resolutions into the second level cache.
|
* Construct a new key for a caching natural identifier resolutions into the second level cache.
|
||||||
|
@ -73,7 +75,8 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
|
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
|
||||||
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
|
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
|
||||||
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
||||||
final Type type = propertyTypes[naturalIdPropertyIndexes[i]];
|
final int naturalIdPropertyIndex = naturalIdPropertyIndexes[i];
|
||||||
|
final Type type = propertyTypes[naturalIdPropertyIndex];
|
||||||
final Object value = naturalIdValues[i];
|
final Object value = naturalIdValues[i];
|
||||||
|
|
||||||
result = prime * result + (value != null ? type.getHashCode( value, factory ) : 0);
|
result = prime * result + (value != null ? type.getHashCode( value, factory ) : 0);
|
||||||
|
@ -82,25 +85,29 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hashCode = result;
|
this.hashCode = result;
|
||||||
this.toString = new ValueHolder<String>(
|
initTransients();
|
||||||
new ValueHolder.DeferredInitializer<String>() {
|
}
|
||||||
@Override
|
|
||||||
public String initialize() {
|
private void initTransients() {
|
||||||
//Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys
|
this.toString = new ValueHolder<String>(
|
||||||
//the only same way to differentiate the keys is to included the disassembled values in the string.
|
new ValueHolder.DeferredInitializer<String>() {
|
||||||
final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" );
|
@Override
|
||||||
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
public String initialize() {
|
||||||
toStringBuilder.append( naturalIdValues[i] );
|
//Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys
|
||||||
if ( i + 1 < naturalIdValues.length ) {
|
//the only same way to differentiate the keys is to included the disassembled values in the string.
|
||||||
toStringBuilder.append( ", " );
|
final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" );
|
||||||
}
|
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
||||||
}
|
toStringBuilder.append( naturalIdValues[i] );
|
||||||
toStringBuilder.append( "]" );
|
if ( i + 1 < naturalIdValues.length ) {
|
||||||
|
toStringBuilder.append( ", " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toStringBuilder.append( "]" );
|
||||||
|
|
||||||
return toStringBuilder.toString();
|
return toStringBuilder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings( {"UnusedDeclaration"})
|
@SuppressWarnings( {"UnusedDeclaration"})
|
||||||
|
@ -144,4 +151,10 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
&& EqualsHelper.equals( tenantId, other.tenantId )
|
&& EqualsHelper.equals( tenantId, other.tenantId )
|
||||||
&& Arrays.deepEquals( this.naturalIdValues, other.naturalIdValues );
|
&& Arrays.deepEquals( this.naturalIdValues, other.naturalIdValues );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void readObject(ObjectInputStream ois)
|
||||||
|
throws ClassNotFoundException, IOException {
|
||||||
|
ois.defaultReadObject();
|
||||||
|
initTransients();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
73
hibernate-core/src/test/java/org/hibernate/cache/spi/NaturalIdCacheKeyTest.java
vendored
Normal file
73
hibernate-core/src/test/java/org/hibernate/cache/spi/NaturalIdCacheKeyTest.java
vendored
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
package org.hibernate.cache.spi;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.mockito.Matchers.anyObject;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.type.Type;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
public class NaturalIdCacheKeyTest {
|
||||||
|
@Test
|
||||||
|
public void testSerializationRoundTrip() throws Exception {
|
||||||
|
final EntityPersister entityPersister = mock(EntityPersister.class);
|
||||||
|
final SessionImplementor sessionImplementor = mock(SessionImplementor.class);
|
||||||
|
final SessionFactoryImplementor sessionFactoryImplementor = mock(SessionFactoryImplementor.class);
|
||||||
|
final Type mockType = mock(Type.class);
|
||||||
|
|
||||||
|
when (entityPersister.getRootEntityName()).thenReturn("EntityName");
|
||||||
|
|
||||||
|
when(sessionImplementor.getFactory()).thenReturn(sessionFactoryImplementor);
|
||||||
|
|
||||||
|
when(entityPersister.getNaturalIdentifierProperties()).thenReturn(new int[] {0, 1, 2});
|
||||||
|
when(entityPersister.getPropertyTypes()).thenReturn(new Type[] {
|
||||||
|
mockType,
|
||||||
|
mockType,
|
||||||
|
mockType
|
||||||
|
});
|
||||||
|
|
||||||
|
when(mockType.getHashCode(anyObject(), eq(sessionFactoryImplementor))).thenAnswer(new Answer<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
return invocation.getArguments()[0].hashCode();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
when(mockType.disassemble(anyObject(), eq(sessionImplementor), eq(null))).thenAnswer(new Answer<Object>() {
|
||||||
|
@Override
|
||||||
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
return invocation.getArguments()[0];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final NaturalIdCacheKey key = new NaturalIdCacheKey(new Object[] {"a", "b", "c"}, entityPersister, sessionImplementor);
|
||||||
|
|
||||||
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
final ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||||
|
oos.writeObject(key);
|
||||||
|
|
||||||
|
final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
final NaturalIdCacheKey keyClone = (NaturalIdCacheKey)ois.readObject();
|
||||||
|
|
||||||
|
assertEquals(key, keyClone);
|
||||||
|
assertEquals(key.hashCode(), keyClone.hashCode());
|
||||||
|
assertEquals(key.toString(), keyClone.toString());
|
||||||
|
assertEquals(key.getEntityName(), keyClone.getEntityName());
|
||||||
|
assertArrayEquals(key.getNaturalIdValues(), keyClone.getNaturalIdValues());
|
||||||
|
assertEquals(key.getTenantId(), keyClone.getTenantId());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue