From 36576046b8df859a672c90182522048dd18e1e74 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Wed, 1 Oct 2014 23:59:05 -0700 Subject: [PATCH] HHH-9388 HHH-7079 : Default collection table and foreign key names are incorrect in some cases; deprecate NamingStrategy --- .../cfg/AnnotationConfiguration.java | 7 + .../java/org/hibernate/cfg/Configuration.java | 78 +++++- .../java/org/hibernate/cfg/Ejb3Column.java | 25 +- .../org/hibernate/cfg/Ejb3JoinColumn.java | 49 ++-- .../java/org/hibernate/cfg/HbmBinder.java | 54 ++-- .../main/java/org/hibernate/cfg/Mappings.java | 32 ++- .../org/hibernate/cfg/NamingStrategy.java | 2 + .../hibernate/cfg/ObjectNameNormalizer.java | 40 ++- .../cfg/annotations/CollectionBinder.java | 6 +- .../cfg/annotations/EntityBinder.java | 41 ++- .../cfg/annotations/TableBinder.java | 96 +++++-- .../AbstractLegacyNamingStrategyDelegate.java | 79 ++++++ .../AbstractNamingStrategyDelegate.java | 66 +++++ .../DefaultNamingStrategyDelegator.java | 48 ++++ .../cfg/naming/HbmNamingStrategyDelegate.java | 123 +++++++++ .../cfg/naming/JpaNamingStrategyDelegate.java | 183 +++++++++++++ .../LegacyHbmNamingStrategyDelegate.java | 125 +++++++++ .../naming/LegacyNamingStrategyDelegate.java | 35 +++ .../naming/LegacyNamingStrategyDelegator.java | 67 +++++ .../LegacyStandardNamingStrategyDelegate.java | 132 ++++++++++ .../cfg/naming/NamingStrategyDelegate.java | 102 ++++++++ .../cfg/naming/NamingStrategyDelegator.java | 44 ++++ .../internal/IdentifierGeneratorResolver.java | 6 + .../SequenceHiLoGeneratorNoIncrementTest.java | 6 + .../id/SequenceHiLoGeneratorTest.java | 7 + .../enhanced/SequenceStyleConfigUnitTest.java | 6 + .../CollectionElementTest.java | 9 +- .../CustomNamingCollectionElementTest.java | 244 ++++++++++++++++++ .../LegacyNamingCollectionElementTest.java | 88 +++++++ .../test/annotations/manytomany/Group.java | 3 + .../annotations/manytomany/GroupWithSet.java | 3 + .../LegacyManyToManyDefaultsTest.java | 84 ++++++ .../defaults/ManyToManyDefaultsTest.java | 5 +- .../org/hibernate/ejb/AvailableSettings.java | 5 + .../org/hibernate/ejb/Ejb3Configuration.java | 21 ++ .../test/MyLegacyNamingStrategyDelegator.java | 54 ++++ .../ejb/test/MyNamingStrategyDelegator.java | 50 ++++ ...ingStrategyDelegatorConfigurationTest.java | 114 ++++++++ 38 files changed, 2053 insertions(+), 86 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractLegacyNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/DefaultNamingStrategyDelegator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/HbmNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/JpaNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyHbmNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegator.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyStandardNamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegate.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegator.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CustomNamingCollectionElementTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/LegacyNamingCollectionElementTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/LegacyManyToManyDefaultsTest.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyLegacyNamingStrategyDelegator.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyNamingStrategyDelegator.java create mode 100644 hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/ejb3configuration/NamingStrategyDelegatorConfigurationTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java index 3f4e6a34aa..dd6e7e4013 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java @@ -33,6 +33,7 @@ import org.dom4j.Document; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.MappingException; +import org.hibernate.cfg.naming.NamingStrategyDelegator; /** * Similar to the {@link Configuration} object but handles EJB3 and Hibernate @@ -239,6 +240,12 @@ public class AnnotationConfiguration extends Configuration { return this; } + @Override + public AnnotationConfiguration setNamingStrategyDelegator(NamingStrategyDelegator namingStrategyDelegator) { + super.setNamingStrategyDelegator( namingStrategyDelegator ); + return this; + } + @Deprecated protected class ExtendedMappingsImpl extends MappingsImpl { } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index 29547d052f..2a4f195aa4 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -76,6 +76,9 @@ import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; +import org.hibernate.cfg.naming.DefaultNamingStrategyDelegator; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; @@ -237,7 +240,7 @@ public class Configuration implements Serializable { private EntityNotFoundDelegate entityNotFoundDelegate; protected transient XMLHelper xmlHelper; - protected NamingStrategy namingStrategy; + private NamingStrategyDelegator namingStrategyDelegator; private SessionFactoryObserver sessionFactoryObserver; protected final SettingsFactory settingsFactory; @@ -332,7 +335,7 @@ public class Configuration implements Serializable { mappedByResolver = new HashMap(); propertyRefResolver = new HashMap(); caches = new ArrayList(); - namingStrategy = EJB3NamingStrategy.INSTANCE; + namingStrategyDelegator = DefaultNamingStrategyDelegator.INSTANCE; setEntityResolver( new EJB3DTDEntityResolver() ); anyMetaDefs = new HashMap(); propertiesAnnotatedWithMapsId = new HashMap>(); @@ -2375,18 +2378,57 @@ public class Configuration implements Serializable { } public NamingStrategy getNamingStrategy() { - return namingStrategy; + if ( LegacyNamingStrategyDelegator.class.isInstance( namingStrategyDelegator ) ) { + return ( (LegacyNamingStrategyDelegator) namingStrategyDelegator ).getNamingStrategy(); + } + return null; + } + + public NamingStrategyDelegator getNamingStrategyDelegator() { + return namingStrategyDelegator; } /** - * Set a custom naming strategy + * Set the current naming strategy. An instance of {@link org.hibernate.cfg.naming.LegacyNamingStrategyDelegator} + * will be constructed with the specified naming strategy. * - * @param namingStrategy the NamingStrategy to set + * @param namingStrategy the {@link NamingStrategy} to set; must be non-null. * - * @return this for method chaining + * @return this for method chaining. + * + * @see org.hibernate.cfg.naming.LegacyNamingStrategyDelegator#LegacyNamingStrategyDelegator(NamingStrategy) */ public Configuration setNamingStrategy(NamingStrategy namingStrategy) { - this.namingStrategy = namingStrategy; + if ( namingStrategy == null ) { + throw new MappingException( "namingStrategy must be non-null" ); + } + setNamingStrategyDelegator( new LegacyNamingStrategyDelegator( namingStrategy ) ); + return this; + } + + /** + * Set a current naming strategy delegator. + * + * @param namingStrategyDelegator the {@link org.hibernate.cfg.naming.NamingStrategyDelegator} to set; + * must be non-null; if {@code namingStrategyDelegator} is an instance + * of {@link LegacyNamingStrategyDelegator}, then + * {@link LegacyNamingStrategyDelegator#getNamingStrategy()} must be non-null. + * @return this for method chaining. + * + * @throws org.hibernate.MappingException if {@code namingStrategyDelegator} is null. + * @throws org.hibernate.MappingException if {@code namingStrategyDelegator} is an instance + * of {@link LegacyNamingStrategyDelegator} and {@link LegacyNamingStrategyDelegator#getNamingStrategy()} + * is null. + */ + public Configuration setNamingStrategyDelegator(NamingStrategyDelegator namingStrategyDelegator) { + if ( namingStrategyDelegator == null ) { + throw new MappingException( "namingStrategyDelegator and namingStrategyDelegator.getNamingStrategy() must be non-null" ); + } + if ( LegacyNamingStrategyDelegator.class.isInstance( namingStrategyDelegator ) && + LegacyNamingStrategyDelegator.class.cast( namingStrategyDelegator ).getNamingStrategy() == null ) { + throw new MappingException( "namingStrategyDelegator.getNamingStrategy() must be non-null." ); + } + this.namingStrategyDelegator = namingStrategyDelegator; return this; } @@ -2630,11 +2672,19 @@ public class Configuration implements Serializable { public NamingStrategy getNamingStrategy() { - return namingStrategy; + return Configuration.this.getNamingStrategy(); } public void setNamingStrategy(NamingStrategy namingStrategy) { - Configuration.this.namingStrategy = namingStrategy; + Configuration.this.setNamingStrategy( namingStrategy ); + } + + public NamingStrategyDelegator getNamingStrategyDelegator() { + return Configuration.this.getNamingStrategyDelegator(); + } + + public void setNamingStrategyDelegator(NamingStrategyDelegator namingStrategyDelegator) { + Configuration.this.setNamingStrategyDelegator( namingStrategyDelegator ); } public TypeResolver getTypeResolver() { @@ -3408,7 +3458,15 @@ public class Configuration implements Serializable { } public NamingStrategy getNamingStrategy() { - return namingStrategy; + if ( LegacyNamingStrategyDelegator.class.isInstance( namingStrategyDelegator ) ) { + ( (LegacyNamingStrategyDelegator) namingStrategyDelegator ).getNamingStrategy(); + } + return null; + } + + @Override + protected NamingStrategyDelegator getNamingStrategyDelegator() { + return namingStrategyDelegator; } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java index c6a28562d1..d5cf240ed0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3Column.java @@ -34,6 +34,7 @@ import org.hibernate.annotations.ColumnTransformers; import org.hibernate.annotations.Index; import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.cfg.annotations.Nullability; +import org.hibernate.cfg.naming.NamingStrategyDelegate; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.Column; @@ -249,7 +250,7 @@ public class Ejb3Column { if ( propertyName != null ) { mappingColumn.setName( mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( - mappings.getNamingStrategy().propertyToColumnName( propertyName ) + getNamingStrategyDelegate().determineAttributeColumnName( propertyName ) ) ); } @@ -257,7 +258,7 @@ public class Ejb3Column { } else { columnName = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnName ); - columnName = mappings.getNamingStrategy().columnName( columnName ); + columnName = getNamingStrategyDelegate().toPhysicalColumnName( columnName ); columnName = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnName ); mappingColumn.setName( columnName ); } @@ -324,11 +325,18 @@ public class Ejb3Column { } protected void addColumnBinding(SimpleValue value) { - String logicalColumnName = mappings.getNamingStrategy() - .logicalColumnName( this.logicalColumnName, propertyName ); + String logicalColumnName = getNamingStrategyDelegate().logicalColumnName( this.logicalColumnName, propertyName ); mappings.addColumnBinding( logicalColumnName, getMappingColumn(), value.getTable() ); } + protected NamingStrategyDelegate getNamingStrategyDelegate() { + return getNamingStrategyDelegate( mappings ); + } + + protected static NamingStrategyDelegate getNamingStrategyDelegate(Mappings mappings) { + return mappings.getNamingStrategyDelegator().getNamingStrategyDelegate( false ); + } + /** * Find appropriate table of the column. * It can come from a secondary table or from the main table of the persistent class @@ -446,9 +454,12 @@ public class Ejb3Column { final String sqlType = col.columnDefinition().equals( "" ) ? null : nameNormalizer.normalizeIdentifierQuoting( col.columnDefinition() ); - final String tableName = ! StringHelper.isEmpty(col.table()) - ? nameNormalizer.normalizeIdentifierQuoting( mappings.getNamingStrategy().tableName( col.table() ) ) - : ""; + final String tableName = + ! StringHelper.isEmpty(col.table()) + ? nameNormalizer.normalizeIdentifierQuoting( getNamingStrategyDelegate( mappings ).toPhysicalTableName( + col.table() + ) ) + : ""; final String columnName = nameNormalizer.normalizeIdentifierQuoting( col.name() ); Ejb3Column column = new Ejb3Column(); column.setImplicit( false ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java index a200086af2..38daf2017e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Ejb3JoinColumn.java @@ -63,6 +63,7 @@ public class Ejb3JoinColumn extends Ejb3Column { //table name on the mapped by side if any private String mappedByTableName; private String mappedByEntityName; + private String mappedByJpaEntityName; private boolean JPA2ElementCollection; public void setJPA2ElementCollection(boolean JPA2ElementCollection) { @@ -309,7 +310,10 @@ public class Ejb3JoinColumn extends Ejb3Column { setReferencedColumn( annJoin.referencedColumnName() ); final String tableName = !BinderHelper.isEmptyAnnotationValue( annJoin.table() ) - ? nameNormalizer.normalizeIdentifierQuoting( getMappings().getNamingStrategy().tableName( annJoin.table() ) ) : ""; + ? nameNormalizer.normalizeIdentifierQuoting( getNamingStrategyDelegate().toPhysicalTableName( + annJoin.table() + ) ) + : ""; setSecondaryTableName( tableName ); } } @@ -450,14 +454,24 @@ public class Ejb3JoinColumn extends Ejb3Column { if ( mappedBySide ) { String unquotedMappedbyTable = StringHelper.unquote( mappedByTableName ); - final String ownerObjectName = JPA2ElementCollection && mappedByEntityName != null ? - StringHelper.unqualify( mappedByEntityName ) : unquotedMappedbyTable; - columnName = getMappings().getNamingStrategy().foreignKeyColumnName( - mappedByPropertyName, - mappedByEntityName, - ownerObjectName, - unquotedLogicalReferenceColumn - ); + if ( JPA2ElementCollection ) { + columnName = getNamingStrategyDelegate().determineElementCollectionForeignKeyColumnName( + mappedByPropertyName, + mappedByEntityName, + mappedByJpaEntityName, + unquotedMappedbyTable, + unquotedLogicalReferenceColumn + ); + } + else { + columnName = getNamingStrategyDelegate().determineEntityAssociationForeignKeyColumnName( + mappedByPropertyName, + mappedByEntityName, + mappedByJpaEntityName, + unquotedMappedbyTable, + unquotedLogicalReferenceColumn + ); + } //one element was quoted so we quote if ( isRefColumnQuoted || StringHelper.isQuoted( mappedByTableName ) ) { columnName = StringHelper.quote( columnName ); @@ -466,9 +480,10 @@ public class Ejb3JoinColumn extends Ejb3Column { else if ( ownerSide ) { String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() ); String unquotedLogicalTableName = StringHelper.unquote( logicalTableName ); - columnName = getMappings().getNamingStrategy().foreignKeyColumnName( + columnName = getNamingStrategyDelegate().determineEntityAssociationForeignKeyColumnName( getPropertyName(), referencedEntity.getEntityName(), + referencedEntity.getJpaEntityName(), unquotedLogicalTableName, unquotedLogicalReferenceColumn ); @@ -481,7 +496,7 @@ public class Ejb3JoinColumn extends Ejb3Column { //is an intra-entity hierarchy table join so copy the name by default String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() ); String unquotedLogicalTableName = StringHelper.unquote( logicalTableName ); - columnName = getMappings().getNamingStrategy().joinKeyColumnName( + columnName = getNamingStrategyDelegate().determineJoinKeyColumnName( unquotedLogicalReferenceColumn, unquotedLogicalTableName ); @@ -523,8 +538,11 @@ public class Ejb3JoinColumn extends Ejb3Column { final String referencedColumn = nameNormalizer.normalizeIdentifierQuoting( getReferencedColumn() ); final String unquotedLogColName = StringHelper.unquote( logicalColumnName ); final String unquotedRefColumn = StringHelper.unquote( referencedColumn ); - String logicalCollectionColumnName = getMappings().getNamingStrategy() - .logicalCollectionColumnName( unquotedLogColName, getPropertyName(), unquotedRefColumn ); + String logicalCollectionColumnName = getNamingStrategyDelegate().logicalCollectionColumnName( + unquotedLogColName, + getPropertyName(), + unquotedRefColumn + ); if ( isLogicalColumnQuoted ) { logicalCollectionColumnName = StringHelper.quote( logicalCollectionColumnName ); @@ -639,7 +657,7 @@ public class Ejb3JoinColumn extends Ejb3Column { if ( StringHelper.isNotEmpty( columnName ) ) { getMappingColumn().setName( applyNamingStrategy ? - getMappings().getNamingStrategy().columnName( columnName ) : + getNamingStrategyDelegate().toPhysicalColumnName( columnName ) : columnName ); } @@ -694,8 +712,9 @@ public class Ejb3JoinColumn extends Ejb3Column { return joinColumns; } - public void setMappedBy(String entityName, String logicalTableName, String mappedByProperty) { + public void setMappedBy(String entityName, String jpaEntityName, String logicalTableName, String mappedByProperty) { this.mappedByEntityName = entityName; + this.mappedByJpaEntityName = jpaEntityName; this.mappedByTableName = logicalTableName; this.mappedByPropertyName = mappedByProperty; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java index ea7ed994fa..ce7dc8c207 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java @@ -24,7 +24,6 @@ package org.hibernate.cfg; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -41,6 +40,7 @@ import org.hibernate.EntityMode; import org.hibernate.FetchMode; import org.hibernate.FlushMode; import org.hibernate.MappingException; +import org.hibernate.cfg.naming.NamingStrategyDelegate; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.FilterDefinition; @@ -883,16 +883,23 @@ public final class HbmBinder { String physicalTableName; if ( tableNameNode == null ) { logicalTableName = StringHelper.unqualify( model.getEntityName() ); - physicalTableName = mappings.getNamingStrategy().classToTableName( model.getEntityName() ); + physicalTableName = getNamingStrategyDelegate( mappings ).determinePrimaryTableLogicalName( + model.getEntityName(), + model.getJpaEntityName() + ); } else { logicalTableName = tableNameNode.getValue(); - physicalTableName = mappings.getNamingStrategy().tableName( logicalTableName ); + physicalTableName = getNamingStrategyDelegate( mappings ).toPhysicalTableName( logicalTableName ); } mappings.addTableBinding( schema, catalog, logicalTableName, physicalTableName, denormalizedSuperTable ); return physicalTableName; } + private static NamingStrategyDelegate getNamingStrategyDelegate(Mappings mappings) { + return mappings.getNamingStrategyDelegator().getNamingStrategyDelegate( true ); + } + public static void bindJoinedSubclass(Element node, JoinedSubclass joinedSubclass, Mappings mappings, java.util.Map inheritedMetas) throws MappingException { @@ -1068,11 +1075,10 @@ public final class HbmBinder { column.setTypeIndex( count++ ); bindColumn( columnElement, column, isNullable ); final String columnName = columnElement.attributeValue( "name" ); - String logicalColumnName = mappings.getNamingStrategy().logicalColumnName( + String logicalColumnName = getNamingStrategyDelegate( mappings ).logicalColumnName( columnName, propertyPath ); - column.setName( mappings.getNamingStrategy().columnName( - columnName ) ); + column.setName( getNamingStrategyDelegate( mappings ).toPhysicalColumnName( columnName ) ); if ( table != null ) { table.addColumn( column ); // table=null -> an association // - fill it in later @@ -1123,10 +1129,10 @@ public final class HbmBinder { ( (ManyToOne) simpleValue ).markAsLogicalOneToOne(); } final String columnName = columnAttribute.getValue(); - String logicalColumnName = mappings.getNamingStrategy().logicalColumnName( + String logicalColumnName = getNamingStrategyDelegate( mappings ).logicalColumnName( columnName, propertyPath ); - column.setName( mappings.getNamingStrategy().columnName( columnName ) ); + column.setName( getNamingStrategyDelegate( mappings ).toPhysicalColumnName( columnName ) ); if ( table != null ) { table.addColumn( column ); // table=null -> an association - fill // it in later @@ -1142,8 +1148,8 @@ public final class HbmBinder { Column column = new Column(); column.setValue( simpleValue ); bindColumn( node, column, isNullable ); - column.setName( mappings.getNamingStrategy().propertyToColumnName( propertyPath ) ); - String logicalName = mappings.getNamingStrategy().logicalColumnName( null, propertyPath ); + column.setName( getNamingStrategyDelegate( mappings ).determineAttributeColumnName( propertyPath ) ); + String logicalName = getNamingStrategyDelegate( mappings ).logicalColumnName( null, propertyPath ); mappings.addColumnBinding( logicalName, column, table ); /* TODO: joinKeyColumnName & foreignKeyColumnName should be called either here or at a * slightly higer level in the stack (to get all the information we need) @@ -1467,7 +1473,7 @@ public final class HbmBinder { Attribute tableNode = node.attribute( "table" ); String tableName; if ( tableNode != null ) { - tableName = mappings.getNamingStrategy().tableName( tableNode.getValue() ); + tableName = getNamingStrategyDelegate( mappings ).toPhysicalTableName( tableNode.getValue() ); } else { //tableName = mappings.getNamingStrategy().propertyToTableName( className, path ); @@ -1475,13 +1481,25 @@ public final class HbmBinder { //TODO mappings.getLogicalTableName(ownerTable) String logicalOwnerTableName = ownerTable.getName(); //FIXME we don't have the associated entity table name here, has to be done in a second pass - tableName = mappings.getNamingStrategy().collectionTableName( - collection.getOwner().getEntityName(), - logicalOwnerTableName , - null, - null, - path - ); + if ( node.element( "element" ) != null || node.element( "composite-element" ) != null ) { + tableName = getNamingStrategyDelegate( mappings ).determineElementCollectionTableLogicalName( + collection.getOwner().getClassName(), + collection.getOwner().getEntityName(), + logicalOwnerTableName, + path + ); + } + else { + tableName = getNamingStrategyDelegate( mappings ).determineEntityAssociationJoinTableLogicalName( + collection.getOwner().getEntityName(), + collection.getOwner().getJpaEntityName(), + logicalOwnerTableName, + null, + null, + null, + path + ); + } if ( ownerTable.isQuoted() ) { tableName = StringHelper.quote( tableName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java index 49f5c6eef6..240bad9d81 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Mappings.java @@ -36,6 +36,7 @@ import org.hibernate.MappingException; import org.hibernate.annotations.AnyMetaDef; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.engine.ResultSetMappingDefinition; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.NamedQueryDefinition; @@ -79,16 +80,43 @@ public interface Mappings { * Get the current naming strategy. * * @return The current naming strategy. + * + * @deprecated Use {@link #getNamingStrategyDelegator()} instead. */ + @Deprecated public NamingStrategy getNamingStrategy(); /** - * Set the current naming strategy. + * Set the current naming strategy. An instance of {@link org.hibernate.cfg.naming.LegacyNamingStrategyDelegator} + * will be constructed with the specified naming strategy. * - * @param namingStrategy The naming strategy to use. + * @param namingStrategy the {@link NamingStrategy} to set; must be non-null. + * + * @deprecated Use {@link #setNamingStrategyDelegator(org.hibernate.cfg.naming.NamingStrategyDelegator)} instead. + + * @see org.hibernate.cfg.naming.LegacyNamingStrategyDelegator#LegacyNamingStrategyDelegator(NamingStrategy) */ + @Deprecated public void setNamingStrategy(NamingStrategy namingStrategy); + /** + * Get the current naming strategy delegate. + * + * @return The current naming strategy delegate. + */ + public NamingStrategyDelegator getNamingStrategyDelegator(); + + /** + * Set a current naming strategy delegator. + * + * @param namingStrategyDelegator the {@link org.hibernate.cfg.naming.NamingStrategyDelegator} to set; + * must be non-null; if {@code namingStrategyDelegator} is an instance + * of {@link org.hibernate.cfg.naming.LegacyNamingStrategyDelegator}, then + * {@link org.hibernate.cfg.naming.LegacyNamingStrategyDelegator#getNamingStrategy()} + * must be non-null. + */ + public void setNamingStrategyDelegator(NamingStrategyDelegator namingStrategyDelegator); + /** * Returns the currently bound default schema name. * diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/NamingStrategy.java b/hibernate-core/src/main/java/org/hibernate/cfg/NamingStrategy.java index c78e0f2f03..9c1d4a53b3 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/NamingStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/NamingStrategy.java @@ -37,6 +37,8 @@ package org.hibernate.cfg; * @see ImprovedNamingStrategy * @author Gavin King * @author Emmanuel Bernard + * + * @deprecated */ public interface NamingStrategy { /** diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ObjectNameNormalizer.java b/hibernate-core/src/main/java/org/hibernate/cfg/ObjectNameNormalizer.java index ef629ccecb..6e07e55009 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ObjectNameNormalizer.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ObjectNameNormalizer.java @@ -23,6 +23,7 @@ */ package org.hibernate.cfg; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.internal.util.StringHelper; /** @@ -42,7 +43,10 @@ public abstract class ObjectNameNormalizer { * @param strategy The naming strategy in effect * * @return The implicit name + * + * * @deprecated Replaced by {@link #determineImplicitName(org.hibernate.cfg.naming.NamingStrategyDelegator)}. */ + @Deprecated public String determineImplicitName(NamingStrategy strategy); /** @@ -52,8 +56,30 @@ public abstract class ObjectNameNormalizer { * @param name The {@link ObjectNameNormalizer#normalizeIdentifierQuoting normalized} explicit object name. * * @return The strategy-handled name. + * + * @deprecated Replaced by {@link #determineImplicitName(org.hibernate.cfg.naming.NamingStrategyDelegator)}. */ + @Deprecated public String handleExplicitName(NamingStrategy strategy, String name); + + /** + * Called when the user supplied no explicit name/identifier for the given database object. + * + * @param strategyDelegator The naming strategy delegator in effect + * + * @return The implicit name + */ + public String determineImplicitName(NamingStrategyDelegator strategyDelegator); + + /** + * Called when the user has supplied an explicit name for the database object. + * + * @param strategyDelegator The naming strategy delegator in effect + * @param name The {@link ObjectNameNormalizer#normalizeIdentifierQuoting normalized} explicit object name. + * + * @return The strategy-handled name. + */ + public String handleExplicitName(NamingStrategyDelegator strategyDelegator, String name); } /** @@ -70,14 +96,14 @@ public abstract class ObjectNameNormalizer { if ( StringHelper.isEmpty( explicitName ) ) { // No explicit name given, so allow the naming strategy the chance // to determine it based on the corresponding mapped java name - objectName = helper.determineImplicitName( getNamingStrategy() ); + objectName = helper.determineImplicitName( getNamingStrategyDelegator() ); } else { // An explicit name was given: // in some cases we allow the naming strategy to "fine tune" these, but first // handle any quoting for consistent handling in naming strategies objectName = normalizeIdentifierQuoting( explicitName ); - objectName = helper.handleExplicitName( getNamingStrategy(), objectName ); + objectName = helper.handleExplicitName( getNamingStrategyDelegator(), objectName ); return normalizeIdentifierQuoting( objectName ); } // Conceivable that the naming strategy could return a quoted identifier, or @@ -134,6 +160,16 @@ public abstract class ObjectNameNormalizer { * Get the current {@link NamingStrategy}. * * @return The current {@link NamingStrategy}. + * + * @deprecated Replaced by {@link #getNamingStrategyDelegator()} */ + @Deprecated protected abstract NamingStrategy getNamingStrategy(); + + /** + * Get the current {@link org.hibernate.cfg.naming.NamingStrategyDelegator}. + * + * @return The current {@link org.hibernate.cfg.naming.NamingStrategyDelegator}. + */ + protected abstract NamingStrategyDelegator getNamingStrategyDelegator(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index 6615d11c09..fbc78c807e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -1131,7 +1131,9 @@ public abstract class CollectionBinder { ); Table ownerTable = collValue.getOwner().getTable(); column.setMappedBy( - collValue.getOwner().getEntityName(), mappings.getLogicalTableName( ownerTable ), + collValue.getOwner().getEntityName(), + collValue.getOwner().getJpaEntityName(), + mappings.getLogicalTableName( ownerTable ), mappedByProperty ); // String header = ( mappedByProperty == null ) ? mappings.getLogicalTableName( ownerTable ) : mappedByProperty; @@ -1141,8 +1143,10 @@ public abstract class CollectionBinder { //default value associationTableBinder.setDefaultName( collValue.getOwner().getEntityName(), + collValue.getOwner().getJpaEntityName(), mappings.getLogicalTableName( collValue.getOwner().getTable() ), collectionEntity != null ? collectionEntity.getEntityName() : null, + collectionEntity != null ? collectionEntity.getJpaEntityName() : null, collectionEntity != null ? mappings.getLogicalTableName( collectionEntity.getTable() ) : null, joinColumns[0].getPropertyName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index eca4a64e24..ea953a7e4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -84,6 +84,8 @@ import org.hibernate.cfg.ObjectNameNormalizer; import org.hibernate.cfg.ObjectNameSource; import org.hibernate.cfg.PropertyHolder; import org.hibernate.cfg.UniqueConstraintHolder; +import org.hibernate.cfg.naming.NamingStrategyDelegate; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.FilterDefinition; @@ -520,18 +522,39 @@ public class EntityBinder { private static class EntityTableNamingStrategyHelper implements ObjectNameNormalizer.NamingStrategyHelper { private final String entityName; + private final String jpaEntityName; - private EntityTableNamingStrategyHelper(String entityName) { + private EntityTableNamingStrategyHelper(String entityName, String jpaEntityName) { this.entityName = entityName; + this.jpaEntityName = jpaEntityName; } + @Override public String determineImplicitName(NamingStrategy strategy) { return strategy.classToTableName( entityName ); } + @Override public String handleExplicitName(NamingStrategy strategy, String name) { return strategy.tableName( name ); } + + @Override + public String determineImplicitName(NamingStrategyDelegator strategyDelegator) { + return getNamingStrategyDelegate( strategyDelegator ).determinePrimaryTableLogicalName( + entityName, + jpaEntityName + ); + } + + @Override + public String handleExplicitName(NamingStrategyDelegator strategyDelegator, String name) { + return getNamingStrategyDelegate( strategyDelegator ).toPhysicalTableName( name ); + } + } + + private static NamingStrategyDelegate getNamingStrategyDelegate(NamingStrategyDelegator strategyDelegator) { + return strategyDelegator.getNamingStrategyDelegate( false ); } public void bindTable( @@ -542,7 +565,10 @@ public class EntityBinder { String constraints, Table denormalizedSuperclassTable) { EntityTableObjectNameSource tableNameContext = new EntityTableObjectNameSource( tableName, name ); - EntityTableNamingStrategyHelper namingStrategyHelper = new EntityTableNamingStrategyHelper( name ); + EntityTableNamingStrategyHelper namingStrategyHelper = new EntityTableNamingStrategyHelper( + persistentClass.getEntityName(), + name + ); final Table table = TableBinder.buildAndFillTable( schema, catalog, @@ -736,6 +762,17 @@ public class EntityBinder { public String handleExplicitName(NamingStrategy strategy, String name) { return strategy.tableName( name ); } + + @Override + public String determineImplicitName(NamingStrategyDelegator strategyDelegator) { + // todo : throw an error? + return null; + } + + @Override + public String handleExplicitName(NamingStrategyDelegator strategyDelegator, String name) { + return getNamingStrategyDelegate( strategyDelegator ).toPhysicalTableName( name ); + } } private static SecondaryTableNamingStrategyHelper SEC_TBL_NS_HELPER = new SecondaryTableNamingStrategyHelper(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java index 64dfb89c30..94945b0169 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java @@ -41,6 +41,8 @@ import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; import org.hibernate.cfg.ObjectNameSource; import org.hibernate.cfg.UniqueConstraintHolder; +import org.hibernate.cfg.naming.NamingStrategyDelegate; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; @@ -78,7 +80,9 @@ public class TableBinder { private String associatedEntityTable; private String propertyName; private String ownerEntity; + private String ownerJpaEntity; private String associatedEntity; + private String associatedJpaEntity; private boolean isJPA2ElementCollection; public void setSchema(String schema) { @@ -146,33 +150,56 @@ public class TableBinder { final String unquotedAssocTable = StringHelper.unquote( associatedEntityTable ); //@ElementCollection use ownerEntity_property instead of the cleaner ownerTableName_property - // ownerEntity can be null when the table name is explicitly set + // ownerEntity can be null when the table name is explicitly set; <== gb: doesn't seem to be true... final String ownerObjectName = isJPA2ElementCollection && ownerEntity != null ? StringHelper.unqualify( ownerEntity ) : unquotedOwnerTable; final ObjectNameSource nameSource = buildNameContext( ownerObjectName, - unquotedAssocTable ); + unquotedAssocTable + ); final boolean ownerEntityTableQuoted = StringHelper.isQuoted( ownerEntityTable ); final boolean associatedEntityTableQuoted = StringHelper.isQuoted( associatedEntityTable ); final ObjectNameNormalizer.NamingStrategyHelper namingStrategyHelper = new ObjectNameNormalizer.NamingStrategyHelper() { public String determineImplicitName(NamingStrategy strategy) { + throw new AssertionFailure( "method call should have been replaced by #determineImplicitName(NamingStrategyDelegate strategyDelegate)" ); + } - final String strategyResult = strategy.collectionTableName( - ownerEntity, - ownerObjectName, - associatedEntity, - unquotedAssocTable, - propertyName + public String handleExplicitName(NamingStrategy strategy, String name) { + return strategy.tableName( name ); + } - ); + @Override + public String determineImplicitName(NamingStrategyDelegator strategyDelegator) { + final NamingStrategyDelegate strategyDelegate = getNamingStrategyDelegate( strategyDelegator ); + final String strategyResult; + if ( isJPA2ElementCollection ) { + strategyResult = strategyDelegate.determineElementCollectionTableLogicalName( + ownerEntity, + ownerJpaEntity, + unquotedOwnerTable, + propertyName + ); + } + else { + strategyResult = strategyDelegate.determineEntityAssociationJoinTableLogicalName( + ownerEntity, + ownerJpaEntity, + unquotedOwnerTable, + associatedEntity, + associatedJpaEntity, + unquotedAssocTable, + propertyName + ); + } return ownerEntityTableQuoted || associatedEntityTableQuoted ? StringHelper.quote( strategyResult ) : strategyResult; } - public String handleExplicitName(NamingStrategy strategy, String name) { - return strategy.tableName( name ); + @Override + public String handleExplicitName(NamingStrategyDelegator strategyDelegator, String name) { + return getNamingStrategyDelegate( strategyDelegator ).toPhysicalTableName( name ); } }; @@ -190,13 +217,34 @@ public class TableBinder { ); } - private ObjectNameSource buildNameContext(String unquotedOwnerTable, String unquotedAssocTable) { - String logicalName = mappings.getNamingStrategy().logicalCollectionTableName( - name, - unquotedOwnerTable, - unquotedAssocTable, - propertyName + private ObjectNameSource buildNameContext( + String unquotedOwnerTable, + String unquotedAssocTable) { + final NamingStrategyDelegate strategyDelegate = getNamingStrategyDelegate( + mappings.getNamingStrategyDelegator() ); + String logicalName; + if ( isJPA2ElementCollection ) { + logicalName = strategyDelegate.logicalElementCollectionTableName( + name, + ownerEntity, + ownerJpaEntity, + unquotedOwnerTable, + propertyName + ); + } + else { + logicalName = strategyDelegate.logicalEntityAssociationJoinTableName( + name, + ownerEntity, + ownerJpaEntity, + unquotedOwnerTable, + associatedEntity, + associatedJpaEntity, + unquotedAssocTable, + propertyName + ); + } if ( StringHelper.isQuoted( ownerEntityTable ) || StringHelper.isQuoted( associatedEntityTable ) ) { logicalName = StringHelper.quote( logicalName ); } @@ -204,6 +252,11 @@ public class TableBinder { return new AssociationTableNameSource( name, logicalName ); } + private NamingStrategyDelegate getNamingStrategyDelegate( + NamingStrategyDelegator strategyDelegator) { + return strategyDelegator.getNamingStrategyDelegate( false ); + } + public static Table buildAndFillTable( String schema, String catalog, @@ -556,12 +609,19 @@ public class TableBinder { } public void setDefaultName( - String ownerEntity, String ownerEntityTable, String associatedEntity, String associatedEntityTable, + String ownerEntity, + String ownerJpaEntity, + String ownerEntityTable, + String associatedEntity, + String associatedJpaEntity, + String associatedEntityTable, String propertyName ) { this.ownerEntity = ownerEntity; + this.ownerJpaEntity = ownerJpaEntity; this.ownerEntityTable = ownerEntityTable; this.associatedEntity = associatedEntity; + this.associatedJpaEntity = associatedJpaEntity; this.associatedEntityTable = associatedEntityTable; this.propertyName = propertyName; this.name = null; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractLegacyNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractLegacyNamingStrategyDelegate.java new file mode 100644 index 0000000000..6f5355dd8d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractLegacyNamingStrategyDelegate.java @@ -0,0 +1,79 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import java.io.Serializable; + +import org.hibernate.cfg.NamingStrategy; + +/** + * @author Gail Badner + */ +public abstract class AbstractLegacyNamingStrategyDelegate implements NamingStrategyDelegate, Serializable { + private final LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext context; + + public AbstractLegacyNamingStrategyDelegate(LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext context) { + this.context = context; + } + + protected NamingStrategy getNamingStrategy() { + return context.getNamingStrategy(); + } + + @Override + public String determinePrimaryTableLogicalName(String entityName, String jpaEntityName) { + // jpaEntity name is being passed here in order to not cause a regression. See HHH-4312. + return getNamingStrategy().classToTableName( jpaEntityName ); + } + + @Override + public String toPhysicalTableName(String tableName) { + return getNamingStrategy().tableName( tableName ); + } + + @Override + public String toPhysicalColumnName(String columnName) { + return getNamingStrategy().columnName( columnName ); + } + + @Override + public String determineAttributeColumnName(String propertyName) { + return getNamingStrategy().propertyToColumnName( propertyName ); + } + + @Override + public String determineJoinKeyColumnName(String joinedColumn, String joinedTable) { + return getNamingStrategy().joinKeyColumnName( joinedColumn, joinedTable ); + } + + @Override + public String logicalColumnName(String columnName, String propertyName) { + return getNamingStrategy().logicalColumnName( columnName, propertyName ); + } + + @Override + public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn) { + return getNamingStrategy().logicalCollectionColumnName( columnName, propertyName, referencedColumn ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractNamingStrategyDelegate.java new file mode 100644 index 0000000000..871527e79e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/AbstractNamingStrategyDelegate.java @@ -0,0 +1,66 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import java.io.Serializable; + +import org.hibernate.internal.util.StringHelper; + +/** + * @author Gail Badner + */ +public abstract class AbstractNamingStrategyDelegate implements NamingStrategyDelegate, Serializable { + + @Override + public String toPhysicalTableName(String tableName) { + return tableName; + } + + @Override + public String toPhysicalColumnName(String columnName) { + return columnName; + } + + @Override + public String determineAttributeColumnName(String propertyName) { + return StringHelper.unqualify( propertyName ); + } + + @Override + public String determineJoinKeyColumnName(String joinedColumn, String joinedTable) { + return toPhysicalColumnName( joinedColumn ); + } + + @Override + public String logicalColumnName(String columnName, String propertyName) { + return StringHelper.isNotEmpty( columnName ) ? columnName : StringHelper.unqualify( propertyName ); + } + + @Override + public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn) { + return StringHelper.isNotEmpty( columnName ) ? + columnName : + StringHelper.unqualify( propertyName ) + "_" + referencedColumn; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/DefaultNamingStrategyDelegator.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/DefaultNamingStrategyDelegator.java new file mode 100644 index 0000000000..38e46c0cc4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/DefaultNamingStrategyDelegator.java @@ -0,0 +1,48 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import java.io.Serializable; + +/** + * @author Gail Badner + */ +public class DefaultNamingStrategyDelegator implements NamingStrategyDelegator, Serializable { + public static final DefaultNamingStrategyDelegator INSTANCE = new DefaultNamingStrategyDelegator(); + + private final NamingStrategyDelegate hbmNamingStrategyDelegate; + private final NamingStrategyDelegate jpaNamingStrategyDelegate; + + private DefaultNamingStrategyDelegator() { + this.hbmNamingStrategyDelegate = new HbmNamingStrategyDelegate(); + this.jpaNamingStrategyDelegate = new JpaNamingStrategyDelegate(); + } + + @Override + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) { + return isHbm ? + hbmNamingStrategyDelegate : + jpaNamingStrategyDelegate; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/HbmNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/HbmNamingStrategyDelegate.java new file mode 100644 index 0000000000..b8f6e466cf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/HbmNamingStrategyDelegate.java @@ -0,0 +1,123 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import org.hibernate.internal.util.StringHelper; + +/** + * @author Gail Badner + */ +public class HbmNamingStrategyDelegate extends AbstractNamingStrategyDelegate { + + @Override + public String determinePrimaryTableLogicalName(String entityName, String jpaEntityName) { + return StringHelper.unqualify( entityName ); + } + + @Override + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyNamePath) { + return ownerEntityTable + + '_' + + StringHelper.unqualify( propertyNamePath ); + } + + @Override + public String determineElementCollectionForeignKeyColumnName(String propertyName, String propertyEntityName, String propertyJpaEntityName, String propertyTableName, String referencedColumnName) { + throw new UnsupportedOperationException( "Method not supported for Hibernate-specific mappings" ); + } + + @Override + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyNamePath) { + return ownerEntityTable + + '_' + + StringHelper.unqualify( propertyNamePath ); + } + + @Override + public String determineEntityAssociationForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + throw new UnsupportedOperationException( "Method not supported for Hibernate-specific mappings" ); + } + + @Override + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName) { + if ( tableName != null ) { + return tableName; + } + else { + return determineElementCollectionTableLogicalName( + ownerEntityName, + ownerJpaEntityName, + ownerEntityTable, + propertyName + ); + } + } + + @Override + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName) { + if ( tableName != null ) { + return tableName; + } + else { + return determineEntityAssociationJoinTableLogicalName( + ownerEntityName, + ownerJpaEntityName, + ownerEntityTable, + associatedEntityName, + associatedJpaEntityName, + associatedEntityTable, + propertyName + ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/JpaNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/JpaNamingStrategyDelegate.java new file mode 100644 index 0000000000..653de66ca6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/JpaNamingStrategyDelegate.java @@ -0,0 +1,183 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import org.hibernate.AssertionFailure; +import org.hibernate.internal.util.StringHelper; + +/** + * @author Gail Badner + */ +public class JpaNamingStrategyDelegate extends AbstractNamingStrategyDelegate { + + @Override + public String determinePrimaryTableLogicalName(String entityName, String jpaEntityName) { + return StringHelper.unqualify( determineEntityNameToUse( entityName, jpaEntityName ) ); + } + + @Override + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyNamePath) { + // JPA states we should use the following as default: + // "The concatenation of the name of the containing entity and the name of the + // collection attribute, separated by an underscore. + // aka: + // if owning entity has a JPA entity name: {OWNER JPA ENTITY NAME}_{COLLECTION ATTRIBUTE NAME} + // otherwise: {OWNER ENTITY NAME}_{COLLECTION ATTRIBUTE NAME} + return determineEntityNameToUse( ownerEntityName, ownerJpaEntityName ) + + '_' + + StringHelper.unqualify( propertyNamePath ); + } + + @Override + public String determineElementCollectionForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + // JPA states we should use the following as default: + // "The concatenation of the following: the name of the entity; "_"; the name of the + // referenced primary key column" + return determineEntityNameToUse( propertyEntityName, propertyJpaEntityName ) + + '_' + + referencedColumnName; + } + + @Override + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyNamePath) { + // JPA states we should use the following as default: + // "The concatenated names of the two associated primary entity tables (owning side + // first), separated by an underscore." + // aka: + // {OWNING SIDE PRIMARY TABLE NAME}_{NON-OWNING SIDE PRIMARY TABLE NAME} + + return ownerEntityTable + + '_' + + associatedEntityTable; + } + + @Override + public String determineEntityAssociationForeignKeyColumnName( + String referencingPropertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + // JPA states we should use the following as default: + // "The concatenation of the following: the name of the referencing relationship + // property or field of the referencing entity or embeddable class; "_"; the name + // of the referenced primary key column. If there is no such referencing relationship + // property or field in the entity, or if the join is for an element collection, the + // join column name is formed as the concatenation of the following: the name of the + // entity; "_"; the name of the referenced primary key column + // The part referring to an entity collection can be disregarded here since, determination of + // an element collection foreign key column name is covered by #entityAssociationJoinTableName(). + // + // For a unidirectional association: + // {PROPERTY_ENTITY_NAME}_{REFERENCED_COLUMN_NAME} + // For a bidirectional association: + // {REFERENCING_PROPERTY_NAME}_{REFERENCED_COLUMN_NAME} + final String header; + if ( referencingPropertyName == null ) { + // This is a unidirectional association. + header = determineEntityNameToUse( propertyEntityName, propertyJpaEntityName ); + } + else { + // This is a bidirectional association. + header = StringHelper.unqualify( referencingPropertyName ); + } + if ( header == null ) { + throw new AssertionFailure( "propertyJpaEntityName and referencingPropertyName cannot both be empty." ); + } + return toPhysicalColumnName( header + "_" + referencedColumnName ); + } + + @Override + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName) { + if ( tableName != null ) { + return tableName; + } + else { + return determineElementCollectionTableLogicalName( + ownerEntityName, + ownerJpaEntityName, + ownerEntityTable, + propertyName + ); + } + } + + @Override + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName) { + if ( tableName != null ) { + return tableName; + } + else { + return determineEntityAssociationJoinTableLogicalName( + ownerEntityName, + ownerJpaEntityName, + ownerEntityTable, + associatedEntityName, + associatedJpaEntityName, + associatedEntityTable, + propertyName + ); + } + } + + private String determineEntityNameToUse(String entityName, String jpaEntityName) { + if ( StringHelper.isNotEmpty( jpaEntityName ) ) { + // prefer the JPA entity name, if specified... + return jpaEntityName; + } + else { + // otherwise, use the Hibernate entity name + return StringHelper.unqualifyEntityName( entityName ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyHbmNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyHbmNamingStrategyDelegate.java new file mode 100644 index 0000000000..2a75f793e8 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyHbmNamingStrategyDelegate.java @@ -0,0 +1,125 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +/** + * @author Gail Badner + */ +public class LegacyHbmNamingStrategyDelegate extends AbstractLegacyNamingStrategyDelegate { + + public LegacyHbmNamingStrategyDelegate(LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext context) { + super( context ); + } + + @Override + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + ownerEntityTable, + null, + null, + propertyNamePath + ); + } + + @Override + public String determineElementCollectionForeignKeyColumnName(String propertyName, String propertyEntityName, String propertyJpaEntityName, String propertyTableName, String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + propertyTableName, + referencedColumnName + ); + } + + @Override + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + ownerEntityTable, + associatedEntityName, + associatedEntityTable, + propertyNamePath + ); + } + + @Override + public String determineEntityAssociationForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + propertyTableName, + referencedColumnName + ); + } + + @Override + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityTable, + null, + propertyName + ); + } + + @Override + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityTable, + associatedEntityTable, + propertyName + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegate.java new file mode 100644 index 0000000000..1a33e95ccb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegate.java @@ -0,0 +1,35 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import org.hibernate.cfg.NamingStrategy; + +/** + * @author Gail Badner + */ +public interface LegacyNamingStrategyDelegate extends NamingStrategyDelegate { + public static interface LegacyNamingStrategyDelegateContext { + public NamingStrategy getNamingStrategy(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegator.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegator.java new file mode 100644 index 0000000000..35a32ea726 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyNamingStrategyDelegator.java @@ -0,0 +1,67 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import java.io.Serializable; + +import org.hibernate.cfg.EJB3NamingStrategy; +import org.hibernate.cfg.NamingStrategy; + +import static org.hibernate.cfg.naming.LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext; + +/** + * + * @deprecated Needed as a transitory implementation until the deprecated NamingStrategy contract + * can be removed. + * + * @author Gail Badner + */ +@Deprecated +public class LegacyNamingStrategyDelegator + implements NamingStrategyDelegator, LegacyNamingStrategyDelegateContext, Serializable { + private final NamingStrategy namingStrategy; + private final NamingStrategyDelegate hbmNamingStrategyDelegate; + private final NamingStrategyDelegate jpaNamingStrategyDelegate; + + public LegacyNamingStrategyDelegator() { + this( EJB3NamingStrategy.INSTANCE ); + } + + public LegacyNamingStrategyDelegator(NamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + this.hbmNamingStrategyDelegate = new LegacyHbmNamingStrategyDelegate( this ); + this.jpaNamingStrategyDelegate = new LegacyStandardNamingStrategyDelegate( this ); + } + + public NamingStrategy getNamingStrategy() { + return namingStrategy; + } + + @Override + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) { + return isHbm ? + hbmNamingStrategyDelegate : + jpaNamingStrategyDelegate; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyStandardNamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyStandardNamingStrategyDelegate.java new file mode 100644 index 0000000000..57e5e080a6 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/LegacyStandardNamingStrategyDelegate.java @@ -0,0 +1,132 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import org.hibernate.internal.util.StringHelper; + +/** + * @author Gail Badner + */ +public class LegacyStandardNamingStrategyDelegate extends AbstractLegacyNamingStrategyDelegate { + + LegacyStandardNamingStrategyDelegate(LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext context) { + super( context ); + } + + @Override + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + StringHelper.unqualifyEntityName( ownerEntityName ), + null, + null, + propertyNamePath + ); + } + + @Override + public String determineElementCollectionForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + StringHelper.unqualifyEntityName( propertyEntityName ), + referencedColumnName + ); + } + + @Override + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + ownerEntityTable, + associatedEntityName, + associatedEntityTable, + propertyNamePath + ); + } + + @Override + public String determineEntityAssociationForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + propertyTableName, + referencedColumnName + ); + } + + @Override + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityName == null ? null : StringHelper.unqualifyEntityName( ownerEntityName ), + null, + propertyName + ); + } + + @Override + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityTable, + associatedEntityTable, + propertyName + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegate.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegate.java new file mode 100644 index 0000000000..de204ff000 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegate.java @@ -0,0 +1,102 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +/** + * @author Gail Badner + */ +public interface NamingStrategyDelegate { + + public String determinePrimaryTableLogicalName(String entityName, String jpaEntityName); + + public String determineAttributeColumnName(String propertyName); + + /** + * Alter the table name given in the mapping document + * @param tableName a table name + * @return a table name + */ + public String toPhysicalTableName(String tableName); + + /** + * Alter the column name given in the mapping document + * @param columnName a column name + * @return a column name + */ + public String toPhysicalColumnName(String columnName); + + + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName); + + public String determineElementCollectionForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName); + + + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName); + + public String determineEntityAssociationForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName); + + public String determineJoinKeyColumnName(String joinedColumn, String joinedTable); + + public String logicalColumnName(String columnName, String propertyName); + + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName); + + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName); + + public String logicalCollectionColumnName(String columnName, String propertyName, String referencedColumn); +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegator.java b/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegator.java new file mode 100644 index 0000000000..d13764acb2 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/naming/NamingStrategyDelegator.java @@ -0,0 +1,44 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.cfg.naming; + +import org.hibernate.cfg.NamingStrategy; + +/** + * Provides access to the appropriate {@link NamingStrategyDelegate}. + * + * @author Gail Badner + */ +public interface NamingStrategyDelegator { + + /** + * Returns the appropriate {@link NamingStrategyDelegate}. + * + * @param isHbm - true, if {@link NamingStrategyDelegate} is to be used for a + * hibernate-specific (hbm.xml) mapping; false, otherwise. + * + * @return the appropriate {@link NamingStrategyDelegate} + */ + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/IdentifierGeneratorResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/IdentifierGeneratorResolver.java index bec3410649..21e865b68e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/IdentifierGeneratorResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/internal/IdentifierGeneratorResolver.java @@ -29,6 +29,7 @@ import java.util.Properties; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.source.MetadataImplementor; @@ -93,5 +94,10 @@ public class IdentifierGeneratorResolver { protected NamingStrategy getNamingStrategy() { return namingStrategy; } + + @Override + protected NamingStrategyDelegator getNamingStrategyDelegator() { + return null; + } } } diff --git a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java index fe81d34d56..931ff291e5 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorNoIncrementTest.java @@ -37,6 +37,7 @@ import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -85,6 +86,11 @@ public class SequenceHiLoGeneratorNoIncrementTest extends BaseUnitTestCase { protected NamingStrategy getNamingStrategy() { return cfg.getNamingStrategy(); } + + @Override + protected NamingStrategyDelegator getNamingStrategyDelegator() { + return cfg.getNamingStrategyDelegator(); + } } ); diff --git a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java index 6fe9ff8064..663426c00e 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/SequenceHiLoGeneratorTest.java @@ -37,6 +37,7 @@ import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -82,6 +83,12 @@ public class SequenceHiLoGeneratorTest extends BaseUnitTestCase { protected NamingStrategy getNamingStrategy() { return cfg.getNamingStrategy(); } + + @Override + protected NamingStrategyDelegator getNamingStrategyDelegator() { + return cfg.getNamingStrategyDelegator(); + } + } ); Dialect dialect = new H2Dialect(); diff --git a/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java b/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java index 6d2b508b7c..258c81204c 100644 --- a/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java +++ b/hibernate-core/src/test/java/org/hibernate/id/enhanced/SequenceStyleConfigUnitTest.java @@ -31,6 +31,7 @@ import org.hibernate.MappingException; import org.hibernate.cfg.Environment; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.ObjectNameNormalizer; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.dialect.Dialect; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.testing.junit4.BaseUnitTestCase; @@ -79,6 +80,11 @@ public class SequenceStyleConfigUnitTest extends BaseUnitTestCase { protected NamingStrategy getNamingStrategy() { return null; } + + @Override + protected NamingStrategyDelegator getNamingStrategyDelegator() { + return null; + } } ); return props; diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java index 3f0cd8a4b9..cc7243a1ef 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CollectionElementTest.java @@ -38,7 +38,6 @@ import org.hibernate.mapping.Collection; import org.hibernate.mapping.Column; import org.hibernate.mapping.ForeignKey; import org.hibernate.test.annotations.Country; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -297,7 +296,6 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9387") - @FailureExpected( jiraKey = "HHH-9387") public void testDefaultTableNameOwnerEntityNameAndPKColumnOverride() { // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) // to ensure that entity names/tables are not changed (which would invalidate these test cases). @@ -308,7 +306,6 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9387") - @FailureExpected( jiraKey = "HHH-9387") public void testDefaultTableNameOwnerPrimaryTableAndEntityNamesOverride() { // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) // to ensure that entity names/tables are not changed (which would invalidate these test cases). @@ -318,7 +315,7 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { checkDefaultCollectionTableName( Owner.class, "elements", "OWNER_elements" ); } - private void checkDefaultCollectionTableName( + protected void checkDefaultCollectionTableName( Class ownerEntityClass, String ownerCollectionPropertyName, String expectedCollectionTableName) { @@ -351,7 +348,6 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9389") - @FailureExpected( jiraKey = "HHH-9389") public void testDefaultJoinColumnOwnerEntityNameAndPKColumnOverride() { // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) // to ensure that entity names/tables are not changed (which would invalidate these test cases). @@ -362,7 +358,6 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9389") - @FailureExpected( jiraKey = "HHH-9389") public void testDefaultJoinColumnOwnerPrimaryTableAndEntityNamesOverride() { // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) // to ensure that entity names/tables are not changed (which would invalidate these test cases). @@ -372,7 +367,7 @@ public class CollectionElementTest extends BaseCoreFunctionalTestCase { checkDefaultJoinColumnName( Owner.class, "elements", "OWNER_id" ); } - private void checkDefaultJoinColumnName( + protected void checkDefaultJoinColumnName( Class ownerEntityClass, String ownerCollectionPropertyName, String ownerForeignKeyNameExpected) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CustomNamingCollectionElementTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CustomNamingCollectionElementTest.java new file mode 100644 index 0000000000..bad57f1f03 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/CustomNamingCollectionElementTest.java @@ -0,0 +1,244 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.annotations.collectionelement; + +import org.junit.Test; + +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.EJB3NamingStrategy; +import org.hibernate.cfg.NamingStrategy; +import org.hibernate.cfg.naming.AbstractLegacyNamingStrategyDelegate; +import org.hibernate.cfg.naming.LegacyHbmNamingStrategyDelegate; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegate; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.cfg.naming.NamingStrategyDelegate; +import org.hibernate.testing.TestForIssue; + +/** + * @author Gail Badner + */ +public class CustomNamingCollectionElementTest extends CollectionElementTest { + + @Override + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setNamingStrategyDelegator( new MyLegacyNamingStrategyDelegator() ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + // MyNamingStrategyDelegator will use the owner primary table name (instead of JPA entity name) in generated collection table. + checkDefaultCollectionTableName( Matrix.class, "mvalues", "Mtx_mvalues" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + // MyNamingStrategyDelegator will use owner primary table name (instead of JPA entity name) in generated collection table. + checkDefaultCollectionTableName( Owner.class, "elements", "OWNER_TABLE_elements" ); + } + + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + // MyNamingStrategyDelegator will use owner primary table name, which will default to the JPA entity name + // in generated join column. + checkDefaultJoinColumnName( Matrix.class, "mvalues", "Mtx_mId" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + // MyNamingStrategyDelegator will use the table name (instead of JPA entity name) in generated join column. + checkDefaultJoinColumnName( Owner.class, "elements", "OWNER_TABLE_id" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerPrimaryTableOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Boy has @Entity @Table(name="tbl_Boys") + // MyNamingStrategyDelegator will use the table name (instead of JPA entity name) in generated join column. + checkDefaultJoinColumnName( Boy.class, "hatedNames", "tbl_Boys_id" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerPrimaryTableOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Boy has @Entity @Table(name="tbl_Boys") + // MyNamingStrategyDelegator will use the table name (instead of JPA entity name) in generated join column. + checkDefaultCollectionTableName( Boy.class, "hatedNames", "tbl_Boys_hatedNames" ); + } + + static class MyLegacyNamingStrategyDelegator extends LegacyNamingStrategyDelegator { + private final NamingStrategyDelegate hbmNamingStrategyDelegate = new LegacyHbmNamingStrategyDelegate( this ); + private final NamingStrategyDelegate nonHbmNamingStrategyDelegate = new MyNonHbmNamingStrategyDelegator( this ); + + @Override + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) { + return isHbm ? hbmNamingStrategyDelegate : nonHbmNamingStrategyDelegate; + } + + @Override + public NamingStrategy getNamingStrategy() { + return EJB3NamingStrategy.INSTANCE; + } + + private class MyNonHbmNamingStrategyDelegator extends AbstractLegacyNamingStrategyDelegate { + MyNonHbmNamingStrategyDelegator(LegacyNamingStrategyDelegate.LegacyNamingStrategyDelegateContext context) { + super( context ); + } + + @Override + public String toPhysicalTableName(String tableName) { + return getNamingStrategy().tableName( tableName ); + } + + @Override + public String toPhysicalColumnName(String columnName) { + return getNamingStrategy().columnName( columnName ); + } + + @Override + public String determineElementCollectionTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + ownerEntityTable, + null, + null, + propertyNamePath + ); + } + + @Override + public String determineElementCollectionForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + propertyTableName, + referencedColumnName + ); + } + + @Override + public String determineEntityAssociationJoinTableLogicalName( + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyNamePath) { + return getNamingStrategy().collectionTableName( + ownerEntityName, + ownerEntityTable, + associatedEntityName, + associatedEntityTable, + propertyNamePath + ); + } + + @Override + public String determineEntityAssociationForeignKeyColumnName( + String propertyName, + String propertyEntityName, + String propertyJpaEntityName, + String propertyTableName, + String referencedColumnName) { + return getNamingStrategy().foreignKeyColumnName( + propertyName, + propertyEntityName, + propertyTableName, + referencedColumnName + ); + } + + @Override + public String logicalElementCollectionTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityTable, + null, + propertyName + ); + } + + @Override + public String logicalEntityAssociationJoinTableName( + String tableName, + String ownerEntityName, + String ownerJpaEntityName, + String ownerEntityTable, + String associatedEntityName, + String associatedJpaEntityName, + String associatedEntityTable, + String propertyName) { + return getNamingStrategy().logicalCollectionTableName( + tableName, + ownerEntityTable, + associatedEntityTable, + propertyName + ); + } + } + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/LegacyNamingCollectionElementTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/LegacyNamingCollectionElementTest.java new file mode 100644 index 0000000000..e9b8ddd4d2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/collectionelement/LegacyNamingCollectionElementTest.java @@ -0,0 +1,88 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.annotations.collectionelement; + +import org.junit.Test; + +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.EJB3NamingStrategy; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.testing.TestForIssue; + +/** + * @author Gail Badner + */ +public class LegacyNamingCollectionElementTest extends CollectionElementTest { + + @Override + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setNamingStrategyDelegator( new LegacyNamingStrategyDelegator( EJB3NamingStrategy.INSTANCE ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + // Legacy behavior used unqualified entity name (instead of JPA entity name) in generated collection table. + checkDefaultCollectionTableName( Matrix.class, "mvalues", "Matrix_mvalues" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9387") + public void testDefaultTableNameOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + // Legacy behavior used unqualified entity name (instead of JPA entity name) in generated collection table. + checkDefaultCollectionTableName( Owner.class, "elements", "Owner_elements" ); + } + + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerEntityNameAndPKColumnOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Matrix has @Entity(name="Mtx"); entity table name defaults to "Mtx"; owner PK column is configured as "mId" + // Legacy behavior used unqualified entity name (instead of JPA entity name) in generated join column. + checkDefaultJoinColumnName( Matrix.class, "mvalues", "Matrix_mId" ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9389") + public void testDefaultJoinColumnOwnerPrimaryTableAndEntityNamesOverride() { + // NOTE: expected JPA entity names are explicit here (rather than just getting them from the PersistentClass) + // to ensure that entity names/tables are not changed (which would invalidate these test cases). + + // Owner has @Entity( name="OWNER") @Table( name="OWNER_TABLE") + // Legacy behavior used unqualified entity name (instead of JPA entity name) in generated join column. + checkDefaultJoinColumnName( Owner.class, "elements", "Owner_id" ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/Group.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/Group.java index d7d7f4662a..174d56d790 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/Group.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/Group.java @@ -4,6 +4,8 @@ import java.util.Collection; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OrderBy; import javax.persistence.Table; @@ -34,6 +36,7 @@ public class Group { } @ManyToMany(cascade = CascadeType.PERSIST) + @JoinTable(joinColumns = {@JoinColumn( name="groupId")}) @OrderBy("expirationDate") @Where(clause = "1=1") @WhereJoinTable(clause = "2=2") diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/GroupWithSet.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/GroupWithSet.java index 01bbe3b91a..b6551506fc 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/GroupWithSet.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/GroupWithSet.java @@ -4,6 +4,8 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.OrderBy; import javax.persistence.Table; @@ -34,6 +36,7 @@ public class GroupWithSet { } @ManyToMany(cascade = CascadeType.PERSIST) + @JoinTable(joinColumns = {@JoinColumn( name="groupId")}) @OrderBy("expirationDate") @Where(clause = "1=1") @WhereJoinTable(clause = "2=2") diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/LegacyManyToManyDefaultsTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/LegacyManyToManyDefaultsTest.java new file mode 100644 index 0000000000..24b8b81932 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/LegacyManyToManyDefaultsTest.java @@ -0,0 +1,84 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.test.annotations.manytomany.defaults; + +import org.junit.Test; + +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.EJB3NamingStrategy; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.testing.FailureExpected; +import org.hibernate.testing.TestForIssue; + +/** + * @author Gail Badner + */ +public class LegacyManyToManyDefaultsTest extends ManyToManyDefaultsTest { + @Override + public void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setNamingStrategyDelegator( new LegacyNamingStrategyDelegator( EJB3NamingStrategy.INSTANCE ) ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9390") + public void testUnidirOwnerPrimaryTableAssocEntityNamePKOverride() { + // City.stolenItems; associated entity: Item + // City has @Entity with no name configured and @Table(name = "tbl_city") + // Item has @Entity(name="ITEM") and no @Table + // PK column for City.id: id (default) + // PK column for Item: iId + // unidirectional + // legacy behavior would use the table name in the generated join column. + checkDefaultJoinTablAndJoinColumnNames( + City.class, + "stolenItems", + null, + "tbl_city_ITEM", + "tbl_city_id", + "stolenItems_iId" + ); + } + + @Test + @TestForIssue( jiraKey = "HHH-9390") + public void testUnidirOwnerEntityNamePrimaryTableOverride() { + // Category.clients: associated entity: KnownClient + // Category has @Entity(name="CATEGORY") @Table(name="CATEGORY_TAB") + // KnownClient has @Entity with no name configured and no @Table + // PK column for Category.id: id (default) + // PK column for KnownClient.id: id (default) + // unidirectional + // legacy behavior would use the table name in the generated join column. + checkDefaultJoinTablAndJoinColumnNames( + Category.class, + "clients", + null, + "CATEGORY_TAB_KnownClient", + "CATEGORY_TAB_id", + "clients_id" + + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java index 101026cf97..3a77a779b4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/manytomany/defaults/ManyToManyDefaultsTest.java @@ -30,7 +30,6 @@ import org.junit.Test; import org.hibernate.mapping.Column; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.PersistentClass; -import org.hibernate.testing.FailureExpected; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.type.EntityType; @@ -161,7 +160,6 @@ public class ManyToManyDefaultsTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9390") - @FailureExpected( jiraKey = "HHH-9390") public void testUnidirOwnerPrimaryTableAssocEntityNamePKOverride() { // City.stolenItems; associated entity: Item // City has @Entity with no name configured and @Table(name = "tbl_city") @@ -181,7 +179,6 @@ public class ManyToManyDefaultsTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9390") - @FailureExpected( jiraKey = "HHH-9390") public void testUnidirOwnerEntityNamePrimaryTableOverride() { // Category.clients: associated entity: KnownClient // Category has @Entity(name="CATEGORY") @Table(name="CATEGORY_TAB") @@ -200,7 +197,7 @@ public class ManyToManyDefaultsTest extends BaseCoreFunctionalTestCase { ); } - private void checkDefaultJoinTablAndJoinColumnNames( + protected void checkDefaultJoinTablAndJoinColumnNames( Class ownerEntityClass, String ownerCollectionPropertyName, String inverseCollectionPropertyName, diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AvailableSettings.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AvailableSettings.java index 35ec105614..37b192c449 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AvailableSettings.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/AvailableSettings.java @@ -252,6 +252,11 @@ public class AvailableSettings { */ public static final String NAMING_STRATEGY = "hibernate.ejb.naming_strategy"; + /** + * Naming strategy delegator class name, the class has to have a no-arg constructor that returns a non-null value for {@link } + */ + public static final String NAMING_STRATEGY_DELEGATOR = "hibernate.ejb.naming_strategy_delegator"; + /** * IdentifierGeneratorStrategyProvider class name, the class must have a no-arg constructor * @deprecated if possible wait of Hibernate 4.1 and theService registry (MutableIdentifierGeneratorStrategy service) diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/Ejb3Configuration.java b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/Ejb3Configuration.java index fda7c7f3ac..7cd09230b9 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/ejb/Ejb3Configuration.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/ejb/Ejb3Configuration.java @@ -72,6 +72,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.cfg.NamingStrategy; import org.hibernate.cfg.annotations.reflection.XMLContext; import org.hibernate.cfg.beanvalidation.BeanValidationIntegrator; +import org.hibernate.cfg.naming.NamingStrategyDelegator; import org.hibernate.ejb.cfg.spi.IdentifierGeneratorStrategyProvider; import org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider; import org.hibernate.ejb.event.JpaIntegrator; @@ -1062,6 +1063,7 @@ public class Ejb3Configuration implements Serializable, Referenceable { Interceptor defaultInterceptor = DEFAULT_CONFIGURATION.getInterceptor(); NamingStrategy defaultNamingStrategy = DEFAULT_CONFIGURATION.getNamingStrategy(); + NamingStrategyDelegator defaultNamingStrategyDelegator = DEFAULT_CONFIGURATION.getNamingStrategyDelegator(); Iterator propertyIt = preparedProperties.keySet().iterator(); while ( propertyIt.hasNext() ) { @@ -1101,9 +1103,28 @@ public class Ejb3Configuration implements Serializable, Referenceable { "naming strategy", NamingStrategy.class ); + + final NamingStrategyDelegator namingStrategyDelegator = instantiateCustomClassFromConfiguration( + preparedProperties, + defaultNamingStrategyDelegator, + cfg.getNamingStrategyDelegator(), + AvailableSettings.NAMING_STRATEGY_DELEGATOR, + "naming strategy delegator", + NamingStrategyDelegator.class + ); + + if ( namingStrategy != null && namingStrategyDelegator != null ) { + throw new PersistenceException( + getExceptionHeader() + AvailableSettings.NAMING_STRATEGY + " and " + AvailableSettings.NAMING_STRATEGY_DELEGATOR + + " properties cannot be used together. To be valid, only one of these properties can be set." + ); + } if ( namingStrategy != null ) { cfg.setNamingStrategy( namingStrategy ); } + else if ( namingStrategyDelegator != null ) { + cfg.setNamingStrategyDelegator( namingStrategyDelegator ); + } final SessionFactoryObserver observer = instantiateCustomClassFromConfiguration( preparedProperties, diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyLegacyNamingStrategyDelegator.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyLegacyNamingStrategyDelegator.java new file mode 100644 index 0000000000..86044c82d7 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyLegacyNamingStrategyDelegator.java @@ -0,0 +1,54 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.ejb.test; + +import org.hibernate.cfg.naming.HbmNamingStrategyDelegate; +import org.hibernate.cfg.naming.JpaNamingStrategyDelegate; +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.cfg.naming.NamingStrategyDelegate; + +/** + * @author Gail Badner + */ +public class MyLegacyNamingStrategyDelegator extends LegacyNamingStrategyDelegator { + private final NamingStrategyDelegate hbmNamingStrategyDelegate = new HbmNamingStrategyDelegate(); + private final NamingStrategyDelegate nonHbmNamingStrategyDelegate = new MyNonHbmNamingStrategyDelegate(); + + public MyLegacyNamingStrategyDelegator() { + super( new MyNamingStrategy() ); + } + + @Override + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) { + return isHbm ? hbmNamingStrategyDelegate :nonHbmNamingStrategyDelegate; + } + + private class MyNonHbmNamingStrategyDelegate extends JpaNamingStrategyDelegate { + + @Override + public String toPhysicalColumnName(String columnName) { + return super.toPhysicalColumnName( "c_" + columnName ); + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyNamingStrategyDelegator.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyNamingStrategyDelegator.java new file mode 100644 index 0000000000..e06d7aeb26 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/MyNamingStrategyDelegator.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat Inc. or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.ejb.test; + +import org.hibernate.cfg.naming.HbmNamingStrategyDelegate; +import org.hibernate.cfg.naming.JpaNamingStrategyDelegate; +import org.hibernate.cfg.naming.NamingStrategyDelegate; +import org.hibernate.cfg.naming.NamingStrategyDelegator; + +/** + * @author Gail Badner + */ +public class MyNamingStrategyDelegator implements NamingStrategyDelegator { + private final NamingStrategyDelegate hbmNamingStrategyDelegate = new HbmNamingStrategyDelegate(); + private final NamingStrategyDelegate nonHbmNamingStrategyDelegate = new MyNonHbmNamingStrategyDelegate(); + + @Override + public NamingStrategyDelegate getNamingStrategyDelegate(boolean isHbm) { + return isHbm ? hbmNamingStrategyDelegate :nonHbmNamingStrategyDelegate; + } + + private class MyNonHbmNamingStrategyDelegate extends JpaNamingStrategyDelegate { + + @Override + public String toPhysicalColumnName(String columnName) { + return super.toPhysicalColumnName( "c_" + columnName ); + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/ejb3configuration/NamingStrategyDelegatorConfigurationTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/ejb3configuration/NamingStrategyDelegatorConfigurationTest.java new file mode 100644 index 0000000000..6729984876 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/ejb/test/ejb3configuration/NamingStrategyDelegatorConfigurationTest.java @@ -0,0 +1,114 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2014, Red Hat, Inc. and/or its affiliates or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat, Inc. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + */ +package org.hibernate.ejb.test.ejb3configuration; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.persistence.PersistenceException; + +import org.junit.Test; + +import org.hibernate.cfg.naming.LegacyNamingStrategyDelegator; +import org.hibernate.cfg.naming.NamingStrategyDelegator; +import org.hibernate.ejb.AvailableSettings; +import org.hibernate.ejb.Ejb3Configuration; +import org.hibernate.ejb.test.MyNamingStrategy; +import org.hibernate.ejb.test.MyNamingStrategyDelegator; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + + +/** + * @author Gail Badner + */ +public class NamingStrategyDelegatorConfigurationTest extends BaseUnitTestCase { + + @Test + public void testNamingStrategyDelegatorFromProperty() { + + // configure NamingStrategy + { + PersistenceUnitInfoAdapter adapter = new PersistenceUnitInfoAdapter(); + Ejb3Configuration cfg = new Ejb3Configuration(); + cfg.configure( + adapter, + Collections.singletonMap( AvailableSettings.NAMING_STRATEGY, MyNamingStrategy.class.getName() ) + ); + assertEquals( + MyNamingStrategy.class.getName(), + cfg.getProperties().get( AvailableSettings.NAMING_STRATEGY ) + ); + assertEquals( null, cfg.getProperties().get( AvailableSettings.NAMING_STRATEGY_DELEGATOR ) ); + final NamingStrategyDelegator namingStrategyDelegator = + cfg.getHibernateConfiguration().getNamingStrategyDelegator(); + assertTrue( LegacyNamingStrategyDelegator.class.isInstance( namingStrategyDelegator ) ); + assertTrue( + MyNamingStrategy.class.isInstance( + ( (LegacyNamingStrategyDelegator)namingStrategyDelegator ).getNamingStrategy() + ) + ); + } + + // configure NamingStrategyDelegator + { + PersistenceUnitInfoAdapter adapter = new PersistenceUnitInfoAdapter(); + Ejb3Configuration cfg = new Ejb3Configuration(); + cfg.configure( + adapter, + Collections.singletonMap( + AvailableSettings.NAMING_STRATEGY_DELEGATOR, + MyNamingStrategyDelegator.class.getName() + ) + ); + assertEquals( null, cfg.getProperties().get( AvailableSettings.NAMING_STRATEGY ) ); + assertEquals( + MyNamingStrategyDelegator.class.getName(), + cfg.getProperties().get( AvailableSettings.NAMING_STRATEGY_DELEGATOR ) + ); + final NamingStrategyDelegator namingStrategyDelegator = + cfg.getHibernateConfiguration().getNamingStrategyDelegator(); + assertTrue( MyNamingStrategyDelegator.class.isInstance( namingStrategyDelegator ) ); + } + + // configure NamingStrategy and NamingStrategyDelegator + { + PersistenceUnitInfoAdapter adapter = new PersistenceUnitInfoAdapter(); + Ejb3Configuration cfg = new Ejb3Configuration(); + final Map integrationArgs = new HashMap(); + integrationArgs.put( AvailableSettings.NAMING_STRATEGY, MyNamingStrategy.class.getName() ); + integrationArgs.put( AvailableSettings.NAMING_STRATEGY_DELEGATOR, MyNamingStrategyDelegator.class.getName() ); + try { + cfg.configure( adapter, integrationArgs ); + fail( "Should have thrown a PersistenceException because setting both properties is not allowed." ); + } + catch (PersistenceException ex) { + // expected + } + } + } +}