HHH-11098 - Attributeconverter converttoEntityAttribute and convertToDatabaseColumn code is called within a query.list() call -> slow
This commit is contained in:
parent
4bf4e3b81a
commit
81aba17771
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.test.converter;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Cacheable;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Converter;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.Immutable;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
|
||||
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
|
||||
import org.hibernate.type.descriptor.java.MutabilityPlan;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ExplicitJavaTypeDescriptorTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected void configureBootstrapServiceRegistryBuilder(BootstrapServiceRegistryBuilder bsrb) {
|
||||
super.configureBootstrapServiceRegistryBuilder( bsrb );
|
||||
|
||||
// Let's tell Hibernate to treat MutableState2 as immutable
|
||||
JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(
|
||||
new JavaTypeDescriptorRegistry.FallbackJavaTypeDescriptor( MutableState2.class ) {
|
||||
@Override
|
||||
public MutabilityPlan getMutabilityPlan() {
|
||||
return ImmutableMutabilityPlan.INSTANCE;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
|
||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, "true" );
|
||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources metadataSources) {
|
||||
super.applyMetadataSources( metadataSources );
|
||||
|
||||
metadataSources.addAnnotatedClass( TheEntity.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-11098" )
|
||||
public void testIt() {
|
||||
// set up test data
|
||||
Session session = openSession();
|
||||
session.beginTransaction();
|
||||
session.persist( new TheEntity(1) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
// assertions based on the persist call
|
||||
assertThat( mutableToDomainCallCount, is(1) ); // 1 instead of 0 because of the deep copy call
|
||||
assertThat( mutableToDatabaseCallCount, is(2) ); // 2 instead of 1 because of the deep copy call
|
||||
|
||||
assertThat( immutableToDomainCallCount, is(0) ); // logical
|
||||
assertThat( immutableToDatabaseCallCount, is(1) ); // logical
|
||||
|
||||
assertThat( immutableMutableToDomainCallCount, is(0) ); // was 1 (like mutable) before the JavaTypeDescriptor registration
|
||||
assertThat( immutableMutableToDatabaseCallCount, is(1) ); // was 2 (like mutable) before the JavaTypeDescriptor registration
|
||||
|
||||
// clean up test data
|
||||
session = openSession();
|
||||
session.beginTransaction();
|
||||
session.delete( session.byId( TheEntity.class ).getReference( 1 ) );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
|
||||
@Entity( name = "TheEntity")
|
||||
@Table( name = "T_ENTITY" )
|
||||
@Cacheable
|
||||
public static class TheEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Convert( converter = MutableConverterImpl.class )
|
||||
private MutableState mutableState;
|
||||
|
||||
@Convert( converter = ImmutableConverterImpl.class )
|
||||
private ImmutableState immutableState;
|
||||
|
||||
@Convert( converter = ImmutableMutable2ConverterImpl.class )
|
||||
private MutableState2 immutableMutableState;
|
||||
|
||||
public TheEntity() {
|
||||
}
|
||||
|
||||
public TheEntity(Integer id) {
|
||||
this.id = id;
|
||||
|
||||
this.mutableState = new MutableState( id.toString() );
|
||||
this.immutableState = new ImmutableState( id.toString() );
|
||||
this.immutableMutableState = new MutableState2( id.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void clearCounts() {
|
||||
// in case we add additional tests
|
||||
sessionFactory().getStatistics().clear();
|
||||
|
||||
mutableToDatabaseCallCount = 0;
|
||||
mutableToDomainCallCount = 0;
|
||||
|
||||
immutableToDatabaseCallCount = 0;
|
||||
immutableToDomainCallCount = 0;
|
||||
|
||||
immutableMutableToDatabaseCallCount = 0;
|
||||
immutableMutableToDomainCallCount = 0;
|
||||
}
|
||||
|
||||
private static int mutableToDatabaseCallCount;
|
||||
private static int mutableToDomainCallCount;
|
||||
|
||||
private static int immutableToDatabaseCallCount;
|
||||
private static int immutableToDomainCallCount;
|
||||
|
||||
private static int immutableMutableToDatabaseCallCount;
|
||||
private static int immutableMutableToDomainCallCount;
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Purely mutable state
|
||||
|
||||
public static class MutableState {
|
||||
private String state;
|
||||
|
||||
public MutableState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
// mutable
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableState that = (MutableState) o;
|
||||
|
||||
return getState() != null ? getState().equals( that.getState() ) : that.getState() == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getState() != null ? getState().hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static class MutableConverterImpl implements AttributeConverter<MutableState,String> {
|
||||
@Override
|
||||
public String convertToDatabaseColumn(MutableState attribute) {
|
||||
mutableToDatabaseCallCount++;
|
||||
return attribute == null ? null : attribute.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableState convertToEntityAttribute(String dbData) {
|
||||
mutableToDomainCallCount++;
|
||||
return new MutableState( dbData );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Purely immutable state
|
||||
|
||||
@Immutable
|
||||
public static class ImmutableState {
|
||||
private final String state;
|
||||
|
||||
public ImmutableState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImmutableState that = (ImmutableState) o;
|
||||
|
||||
return getState().equals( that.getState() );
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getState().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static class ImmutableConverterImpl implements AttributeConverter<ImmutableState,String> {
|
||||
@Override
|
||||
public String convertToDatabaseColumn(ImmutableState attribute) {
|
||||
immutableToDatabaseCallCount++;
|
||||
return attribute == null ? null : attribute.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableState convertToEntityAttribute(String dbData) {
|
||||
immutableToDomainCallCount++;
|
||||
return new ImmutableState( dbData );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Mutable state we treat as immutable
|
||||
|
||||
public static class MutableState2 {
|
||||
private String state;
|
||||
|
||||
public MutableState2(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
// mutable
|
||||
public void setState(String state) {
|
||||
// just a safety net - the idea is that the user is promising to not mutate the internal state
|
||||
throw new UnsupportedOperationException( "illegal attempt to mutate state" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableState2 that = (MutableState2) o;
|
||||
|
||||
return getState() != null ? getState().equals( that.getState() ) : that.getState() == null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getState() != null ? getState().hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Converter
|
||||
public static class ImmutableMutable2ConverterImpl implements AttributeConverter<MutableState2,String> {
|
||||
@Override
|
||||
public String convertToDatabaseColumn(MutableState2 attribute) {
|
||||
immutableMutableToDatabaseCallCount++;
|
||||
return attribute == null ? null : attribute.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableState2 convertToEntityAttribute(String dbData) {
|
||||
immutableMutableToDomainCallCount++;
|
||||
return new MutableState2( dbData );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue