Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-17466 Exception on query: Could not convert 'java.util.Locale' to 'java.util.Locale' using 'org.hibernate.type.descriptor.java.LocaleJavaType' to wrap #7575

Merged
merged 2 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ public <X> X unwrap(Locale value, Class<X> type, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( Locale.class.isAssignableFrom( type ) ) {
return (X) value;
}
if ( String.class.isAssignableFrom( type ) ) {
return (X) value.toString();
}
Expand All @@ -101,6 +104,9 @@ public <X> Locale wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof Locale ) {
return (Locale) value;
}
if (value instanceof CharSequence) {
return fromString( (CharSequence) value );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,24 @@
import java.io.Serializable;
import java.sql.Blob;
import java.sql.Clob;
import java.util.TimeZone;

import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;

import org.junit.Before;
import org.junit.Test;

import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.orm.junit.JiraKey;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

/**
Expand All @@ -37,6 +46,40 @@ public Data(T originalValue, T copyOfOriginalValue, T differentValue) {

private final JavaType<T> typeDescriptor;

protected final WrapperOptions wrapperOptions = new WrapperOptions() {
@Override
public SharedSessionContractImplementor getSession() {
return null;
}

@Override
public SessionFactoryImplementor getSessionFactory() {
return null;
}

public boolean useStreamForLobBinding() {
return false;
}

@Override
public int getPreferredSqlTypeCodeForBoolean() {
return 0;
}

public LobCreator getLobCreator() {
return NonContextualLobCreator.INSTANCE;
}

public JdbcType remapSqlTypeDescriptor(JdbcType sqlTypeDescriptor) {
return sqlTypeDescriptor;
}

@Override
public TimeZone getJdbcTimeZone() {
return null;
}
};

public AbstractDescriptorTest(JavaType<T> typeDescriptor) {
this.typeDescriptor = typeDescriptor;
}
Expand All @@ -56,9 +99,15 @@ protected JavaType<T> typeDescriptor() {

protected abstract boolean shouldBeMutable();

protected boolean isIdentityDifferentFromEquality() {
return true;
}

@Test
public void testEquality() {
assertFalse( testData.originalValue == testData.copyOfOriginalValue );
if ( isIdentityDifferentFromEquality() ) {
assertFalse( testData.originalValue == testData.copyOfOriginalValue );
}
assertTrue( typeDescriptor.areEqual( testData.originalValue, testData.originalValue ) );
assertTrue( typeDescriptor.areEqual( testData.originalValue, testData.copyOfOriginalValue ) );
assertFalse( typeDescriptor.areEqual( testData.originalValue, testData.differentValue ) );
Expand All @@ -72,6 +121,23 @@ public void testExternalization() {
assertTrue( typeDescriptor.areEqual( testData.originalValue, consumed ) );
}

/**
* Check that wrapping/unwrapping a value that already has the expected type
* does not fail and returns a value that is considered equal.
*/
@Test
@JiraKey("HHH-17466")
public void testPassThrough() {
assertTrue( typeDescriptor.areEqual(
testData.originalValue,
typeDescriptor.wrap( testData.originalValue, wrapperOptions )
) );
assertTrue( typeDescriptor.areEqual(
testData.originalValue,
typeDescriptor.unwrap( testData.originalValue, typeDescriptor.getJavaTypeClass(), wrapperOptions )
) );
}

@Test
public void testMutabilityPlan() {
assertTrue( shouldBeMutable() == typeDescriptor.getMutabilityPlan().isMutable() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
package org.hibernate.orm.test.mapping.type.java;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
Expand All @@ -23,6 +24,8 @@
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.junit.Test;

import org.assertj.core.api.Assertions;

/**
* @author Steve Ebersole
*/
Expand Down Expand Up @@ -55,6 +58,16 @@ public void testEquality() {
assertFalse( BlobJavaType.INSTANCE.areEqual( original, different ) );
}

@Override
public void testPassThrough() {
// blobs of the same internal value are not really comparable
// we'll just check that operations don't fail, not that the output is equal to the input
assertThatCode( () -> BlobJavaType.INSTANCE.wrap( original, wrapperOptions ) )
.doesNotThrowAnyException();
assertThatCode( () -> BlobJavaType.INSTANCE.unwrap( original, Blob.class, wrapperOptions ) )
.doesNotThrowAnyException();
}

@Test
@Override
public void testExternalization() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import java.util.Locale;

import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.type.descriptor.java.LocaleJavaType;

import org.junit.Test;

/**
Expand All @@ -21,7 +21,29 @@
* @author Christian Beikov
* @author Steve Ebersole
*/
public class LocaleJavaTypeDescriptorTest extends BaseUnitTestCase {
public class LocaleJavaTypeDescriptorTest extends AbstractDescriptorTest<Locale> {
final Locale original = toLocale( "de", "DE", null );
final Locale copy = toLocale( "de", "DE", null );
final Locale different = toLocale( "de", null, null );

public LocaleJavaTypeDescriptorTest() {
super( LocaleJavaType.INSTANCE );
}

@Override
protected Data<Locale> getTestData() {
return new Data<>( original, copy, different );
}

@Override
protected boolean shouldBeMutable() {
return false;
}

@Override
protected boolean isIdentityDifferentFromEquality() {
return false;
}

@Test
public void testConversionFromString() {
Expand All @@ -36,7 +58,7 @@ public void testConversionFromString() {
assertEquals( Locale.ROOT, LocaleJavaType.INSTANCE.fromString( "" ) );
}

public Locale toLocale(String lang, String region, String variant) {
private static Locale toLocale(String lang, String region, String variant) {
final Locale.Builder builder = new Locale.Builder();
if ( StringHelper.isNotEmpty( lang ) ) {
builder.setLanguage( lang );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* 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.orm.test.mapping.type.java;

import static org.assertj.core.api.Assertions.assertThat;

import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.mapping.internal.BasicValuedCollectionPart;
import org.hibernate.persister.entity.EntityPersister;

import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;

import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.Table;

@DomainModel(annotatedClasses = LocaleMappingTests.LocaleMappingTestEntity.class)
@SessionFactory
@JiraKey("HHH-17466")
public class LocaleMappingTests {

@Test
public void basicAssertions(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final EntityPersister entityDescriptor = sessionFactory.getMappingMetamodel().getEntityDescriptor(
LocaleMappingTestEntity.class );

{
final BasicAttributeMapping localeAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping(
"locale" );
assertThat( localeAttribute.getJdbcMapping().getJdbcType().getJdbcTypeCode() ).isEqualTo( Types.VARCHAR );
assertThat( localeAttribute.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo(
Locale.class );
}

{
final PluralAttributeMapping yearsAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping(
"locales" );
final BasicValuedCollectionPart elementDescriptor = (BasicValuedCollectionPart) yearsAttribute.getElementDescriptor();
assertThat( elementDescriptor.getJdbcMapping().getJdbcType().getJdbcTypeCode() ).isEqualTo( Types.VARCHAR );
assertThat( elementDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo(
Locale.class );
}

{
final PluralAttributeMapping countByYearAttribute = (PluralAttributeMapping) entityDescriptor.findAttributeMapping(
"countByLocale" );
final BasicValuedCollectionPart keyDescriptor = (BasicValuedCollectionPart) countByYearAttribute.getIndexDescriptor();
assertThat( keyDescriptor.getJdbcMapping().getJdbcType().getJdbcTypeCode() ).isEqualTo( Types.VARCHAR );
assertThat( keyDescriptor.getJdbcMapping().getJavaTypeDescriptor().getJavaTypeClass() ).isEqualTo(
Locale.class );
}
}

@Test
public void testUsage(SessionFactoryScope scope) {
final LocaleMappingTestEntity entity = new LocaleMappingTestEntity( 1, Locale.ENGLISH, "Hello" );
final LocaleMappingTestEntity entity2 = new LocaleMappingTestEntity( 2, Locale.FRENCH, "Salut" );

scope.inTransaction( (session) -> {
session.save( entity );
session.save( entity2 );
} );

try {
scope.inTransaction( (session) -> assertThat( session.createQuery(
"from LocaleMappingTestEntity where locale = ?1",
LocaleMappingTestEntity.class
)
.setParameter( 1, Locale.FRENCH )
.list() )
.extracting( LocaleMappingTestEntity::getId )
.containsExactly( 2 ) );
}
finally {
scope.inTransaction( session -> session.delete( entity ) );
scope.inTransaction( session -> session.delete( entity2 ) );
}
}

@Entity(name = "LocaleMappingTestEntity")
@Table(name = "locale_map_test_entity")
public static class LocaleMappingTestEntity {
private Integer id;
private Locale locale;
private String name;

private Set<Locale> locales = new HashSet<>();
private Map<Locale, Integer> countByLocale = new HashMap<>();

public LocaleMappingTestEntity() {
}

public LocaleMappingTestEntity(Integer id, Locale locale, String name) {
this.id = id;
this.locale = locale;
this.name = name;
}

@Id
public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Locale getLocale() {
return locale;
}

public void setLocale(Locale locale) {
this.locale = locale;
}

@ElementCollection
@CollectionTable(
name = "entity_locale",
joinColumns = @JoinColumn(name = "entity_id")
)
@Column(name = "locales")
public Set<Locale> getLocales() {
return locales;
}

public void setLocales(Set<Locale> locales) {
this.locales = locales;
}

@ElementCollection
@CollectionTable(name = "count_by_locale", joinColumns = @JoinColumn(name = "entity_id"))
@MapKeyColumn(name = "locl")
@Column(name = "cnt")
public Map<Locale, Integer> getCountByLocale() {
return countByLocale;
}

public void setCountByLocale(Map<Locale, Integer> countByLocale) {
this.countByLocale = countByLocale;
}
}
}