diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java index aaab94fc3f..096a0fd97c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java @@ -622,6 +622,8 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont private boolean autoQuoteKeywords; + private String schemaCharset; + // private PersistentAttributeMemberResolver persistentAttributeMemberResolver = // StandardPersistentAttributeMemberResolver.INSTANCE; @@ -765,6 +767,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont ); this.reflectionManager = generateDefaultReflectionManager(); + + this.schemaCharset = configService.getSetting( + AvailableSettings.HBM2DDL_CHARSET_NAME, + String.class, + null + ); } private ArrayList resolveInitialSourceProcessOrdering(ConfigurationService configService) { @@ -949,6 +957,10 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont : Collections.emptyList(); } + public String getSchemaCharset() { + return schemaCharset; + } + void addAttributeConverterInfo(AttributeConverterInfo info) { if ( this.attributeConverterInfoMap == null ) { this.attributeConverterInfoMap = new HashMap<>(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ImplicitNamingStrategyJpaCompliantImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ImplicitNamingStrategyJpaCompliantImpl.java index ebf1d66243..5418de9433 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ImplicitNamingStrategyJpaCompliantImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ImplicitNamingStrategyJpaCompliantImpl.java @@ -199,8 +199,9 @@ public class ImplicitNamingStrategyJpaCompliantImpl implements ImplicitNamingStr @Override public Identifier determineForeignKeyName(ImplicitForeignKeyNameSource source) { Identifier userProvidedIdentifier = source.getUserProvidedIdentifier(); + source.getBuildingContext().getBuildingOptions().getSchemaCharset(); return userProvidedIdentifier != null ? userProvidedIdentifier : toIdentifier( - NamingHelper.INSTANCE.generateHashedFkName( + NamingHelper.withCharset( source.getBuildingContext().getBuildingOptions().getSchemaCharset() ).generateHashedFkName( "FK", source.getTableName(), source.getReferencedTableName(), @@ -214,7 +215,7 @@ public class ImplicitNamingStrategyJpaCompliantImpl implements ImplicitNamingStr public Identifier determineUniqueKeyName(ImplicitUniqueKeyNameSource source) { Identifier userProvidedIdentifier = source.getUserProvidedIdentifier(); return userProvidedIdentifier != null ? userProvidedIdentifier : toIdentifier( - NamingHelper.INSTANCE.generateHashedConstraintName( + NamingHelper.withCharset( source.getBuildingContext().getBuildingOptions().getSchemaCharset() ).generateHashedConstraintName( "UK", source.getTableName(), source.getColumnNames() @@ -227,7 +228,7 @@ public class ImplicitNamingStrategyJpaCompliantImpl implements ImplicitNamingStr public Identifier determineIndexName(ImplicitIndexNameSource source) { Identifier userProvidedIdentifier = source.getUserProvidedIdentifier(); return userProvidedIdentifier != null ? userProvidedIdentifier : toIdentifier( - NamingHelper.INSTANCE.generateHashedConstraintName( + NamingHelper.withCharset( source.getBuildingContext().getBuildingOptions().getSchemaCharset() ).generateHashedConstraintName( "IDX", source.getTableName(), source.getColumnNames() diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java index 9edf9ce657..4b83b2c6ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/NamingHelper.java @@ -6,7 +6,9 @@ */ package org.hibernate.boot.model.naming; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; +import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -24,6 +26,20 @@ public class NamingHelper { */ public static final NamingHelper INSTANCE = new NamingHelper(); + public static NamingHelper withCharset(String charset) { + return new NamingHelper(charset); + } + + private final String charset; + + public NamingHelper() { + this(null); + } + + private NamingHelper(String charset) { + this.charset = charset; + } + /** * If a foreign-key is not explicitly named, this is called to generate * a unique hash using the table and column names. @@ -146,7 +162,7 @@ public class NamingHelper { try { MessageDigest md = MessageDigest.getInstance( "MD5" ); md.reset(); - md.update( s.getBytes() ); + md.update( charset != null ? s.getBytes( charset ) : s.getBytes() ); byte[] digest = md.digest(); BigInteger bigInt = new BigInteger( 1, digest ); // By converting to base 35 (full alphanumeric), we guarantee @@ -154,7 +170,7 @@ public class NamingHelper { // character identifier restriction enforced by a few dialects. return bigInt.toString( 35 ); } - catch ( NoSuchAlgorithmException e ) { + catch ( NoSuchAlgorithmException|UnsupportedEncodingException e ) { throw new HibernateException( "Unable to generate a hashed name!", e ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java index 399ee167ac..c60866510d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java @@ -224,6 +224,10 @@ public interface MetadataBuildingOptions { List getAttributeConverters(); + default String getSchemaCharset() { + return null; + } + // /** // * Obtain the selected strategy for resolving members identifying persistent attributes // * diff --git a/hibernate-core/src/test/java/org/hibernate/boot/model/naming/NamingHelperTest.java b/hibernate-core/src/test/java/org/hibernate/boot/model/naming/NamingHelperTest.java index c62d72c354..e03594f6c6 100644 --- a/hibernate-core/src/test/java/org/hibernate/boot/model/naming/NamingHelperTest.java +++ b/hibernate-core/src/test/java/org/hibernate/boot/model/naming/NamingHelperTest.java @@ -43,6 +43,26 @@ public class NamingHelperTest extends BaseUnitTestCase { assertEquals( "FKdgopp1hqnm8c1o6sfbb3tbeh", fkNameUtf8 ); } + @Test + @TestForIssue(jiraKey = "HHH-12357") + public void generateHashedFkNameUSingUtf8() { + Identifier booksDe = new Identifier( "Bücher", false ); + Identifier authorsDe = new Identifier( "Autoren", false ); + Identifier authorId = new Identifier( "autor_id", false ); + + defaultCharset.set( StandardCharsets.ISO_8859_1 ); + + String fkNameLatin1 = NamingHelper.withCharset( "UTF8" ).generateHashedFkName( "FK", booksDe, authorsDe, authorId ); + + assertEquals( "FKdgopp1hqnm8c1o6sfbb3tbeh", fkNameLatin1 ); + + defaultCharset.set( StandardCharsets.UTF_8 ); + + String fkNameUtf8 = NamingHelper.withCharset( "UTF8" ).generateHashedFkName( "FK", booksDe, authorsDe, authorId ); + + assertEquals( "FKdgopp1hqnm8c1o6sfbb3tbeh", fkNameUtf8 ); + } + public static class DefaultCharset extends ExternalResource { private Charset prev; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/AbstractCharsetNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/AbstractCharsetNamingStrategyTest.java new file mode 100644 index 0000000000..8e0c02e1d3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/AbstractCharsetNamingStrategyTest.java @@ -0,0 +1,120 @@ +/* + * 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 . + */ + +// $Id$ +package org.hibernate.test.annotations.namingstrategy.charset; + +import java.util.HashMap; +import java.util.Map; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Index; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Environment; +import org.hibernate.mapping.UniqueKey; +import org.hibernate.service.ServiceRegistry; + +import org.hibernate.testing.ServiceRegistryBuilder; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.test.annotations.namingstrategy.LongIdentifierNamingStrategy; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +@TestForIssue( jiraKey = "HHH-12357" ) +public abstract class AbstractCharsetNamingStrategyTest extends BaseUnitTestCase { + + private ServiceRegistry serviceRegistry; + + @Before + public void setUp() { + Map properties = new HashMap( Environment.getProperties() ); + properties.put( AvailableSettings.HBM2DDL_CHARSET_NAME, charsetName() ); + serviceRegistry = ServiceRegistryBuilder.buildServiceRegistry( properties ); + } + + protected abstract String charsetName(); + + @After + public void tearDown() { + if ( serviceRegistry != null ) { + ServiceRegistryBuilder.destroy( serviceRegistry ); + } + } + @Test + public void testWithCustomNamingStrategy() throws Exception { + Metadata metadata = new MetadataSources( serviceRegistry ) + .addAnnotatedClass(Address.class) + .addAnnotatedClass(Person.class) + .getMetadataBuilder() + .applyImplicitNamingStrategy( new LongIdentifierNamingStrategy() ) + .build(); + + UniqueKey uniqueKey = metadata.getEntityBinding( Address.class.getName()).getTable().getUniqueKeyIterator().next(); + assertEquals( expectedUniqueKeyName(), uniqueKey.getName() ); + + org.hibernate.mapping.ForeignKey foreignKey = + (org.hibernate.mapping.ForeignKey) metadata.getEntityBinding( Address.class.getName()).getTable().getForeignKeyIterator().next(); + assertEquals( expectedForeignKeyName(), foreignKey.getName() ); + + org.hibernate.mapping.Index index = metadata.getEntityBinding( Address.class.getName()).getTable().getIndexIterator().next(); + assertEquals( expectedIndexName(), index.getName() ); + } + + protected abstract String expectedUniqueKeyName(); + + protected abstract String expectedForeignKeyName(); + + protected abstract String expectedIndexName(); + + @Entity(name = "Address") + @Table(uniqueConstraints = @UniqueConstraint( + columnNames = { + "city", "stradă" + }), + indexes = @Index( columnList = "city, stradă") + ) + public class Address { + + @Id + private Long id; + + private String city; + + private String stradă; + + @ManyToOne + private Person personă; + } + + @Entity(name = "Person") + public class Person { + + @Id + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Iso88591CharsetNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Iso88591CharsetNamingStrategyTest.java new file mode 100644 index 0000000000..bc16dbd668 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Iso88591CharsetNamingStrategyTest.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +// $Id$ +package org.hibernate.test.annotations.namingstrategy.charset; + +/** + * @author Vlad Mihalcea + */ +public class Iso88591CharsetNamingStrategyTest extends AbstractCharsetNamingStrategyTest { + + @Override + protected String charsetName() { + return "ISO-8859-1"; + } + + @Override + protected String expectedUniqueKeyName() { + return "UKq2jxex2hrvg4139p85npyj71g"; + } + + @Override + protected String expectedForeignKeyName() { + return "FKdeqq4y6cesc2yfgi97u2hp61g"; + } + + @Override + protected String expectedIndexName() { + return "IDXq2jxex2hrvg4139p85npyj71g"; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Utf8CharsetNamingStrategyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Utf8CharsetNamingStrategyTest.java new file mode 100644 index 0000000000..669ff5cf06 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/namingstrategy/charset/Utf8CharsetNamingStrategyTest.java @@ -0,0 +1,37 @@ +/* + * 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 . + */ + +// $Id$ +package org.hibernate.test.annotations.namingstrategy.charset; + +import org.hibernate.testing.TestForIssue; + +/** + * @author Vlad Mihalcea + */ +public class Utf8CharsetNamingStrategyTest extends AbstractCharsetNamingStrategyTest { + + @Override + protected String charsetName() { + return "UTF8"; + } + + @Override + protected String expectedUniqueKeyName() { + return "UKpm66tdjkgtsca5x2uwux487t5"; + } + + @Override + protected String expectedForeignKeyName() { + return "FKgvrnki5fwp3qo0hfp1bu1jj0q"; + } + + @Override + protected String expectedIndexName() { + return "IDXpm66tdjkgtsca5x2uwux487t5"; + } +}