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: 'org.jboss.shrinkwrap:shrinkwrap-impl-base:1.0.0-beta-6',
|
||||
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.jandex )
|
||||
testCompile( libraries.classmate )
|
||||
testCompile( libraries.mockito )
|
||||
testCompile( libraries.validator ) {
|
||||
// for test runtime
|
||||
transitive = true
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
*/
|
||||
package org.hibernate.cache.spi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -44,7 +46,7 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
private final String entityName;
|
||||
private final String tenantId;
|
||||
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.
|
||||
|
@ -73,7 +75,8 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
|
||||
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
|
||||
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];
|
||||
|
||||
result = prime * result + (value != null ? type.getHashCode( value, factory ) : 0);
|
||||
|
@ -82,25 +85,29 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
}
|
||||
|
||||
this.hashCode = result;
|
||||
this.toString = new ValueHolder<String>(
|
||||
new ValueHolder.DeferredInitializer<String>() {
|
||||
@Override
|
||||
public String initialize() {
|
||||
//Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys
|
||||
//the only same way to differentiate the keys is to included the disassembled values in the string.
|
||||
final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" );
|
||||
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
||||
toStringBuilder.append( naturalIdValues[i] );
|
||||
if ( i + 1 < naturalIdValues.length ) {
|
||||
toStringBuilder.append( ", " );
|
||||
}
|
||||
}
|
||||
toStringBuilder.append( "]" );
|
||||
initTransients();
|
||||
}
|
||||
|
||||
return toStringBuilder.toString();
|
||||
}
|
||||
}
|
||||
);
|
||||
private void initTransients() {
|
||||
this.toString = new ValueHolder<String>(
|
||||
new ValueHolder.DeferredInitializer<String>() {
|
||||
@Override
|
||||
public String initialize() {
|
||||
//Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys
|
||||
//the only same way to differentiate the keys is to included the disassembled values in the string.
|
||||
final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" );
|
||||
for ( int i = 0; i < naturalIdValues.length; i++ ) {
|
||||
toStringBuilder.append( naturalIdValues[i] );
|
||||
if ( i + 1 < naturalIdValues.length ) {
|
||||
toStringBuilder.append( ", " );
|
||||
}
|
||||
}
|
||||
toStringBuilder.append( "]" );
|
||||
|
||||
return toStringBuilder.toString();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings( {"UnusedDeclaration"})
|
||||
|
@ -144,4 +151,10 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
&& EqualsHelper.equals( tenantId, other.tenantId )
|
||||
&& 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