HHH-6278 quote all db identifiers

also this commit contains code that make annotation binder applying naming strategy
This commit is contained in:
Strong Liu 2011-07-07 00:21:35 +08:00
parent 5457b6c707
commit ce47766281
23 changed files with 248 additions and 92 deletions

View File

@ -65,23 +65,24 @@ public abstract class ObjectNameNormalizer {
* @return The normalized identifier.
*/
public String normalizeDatabaseIdentifier(final String explicitName, NamingStrategyHelper helper) {
String objectName = null;
// apply naming strategy
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
final String objectName = helper.determineImplicitName( getNamingStrategy() );
// Conceivable that the naming strategy could return a quoted identifier, or
// that user enabled <delimited-identifiers/>
return normalizeIdentifierQuoting( objectName );
objectName = helper.determineImplicitName( getNamingStrategy() );
}
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
String objectName = normalizeIdentifierQuoting( explicitName );
objectName = normalizeIdentifierQuoting( explicitName );
objectName = helper.handleExplicitName( getNamingStrategy(), objectName );
return normalizeIdentifierQuoting( objectName );
}
// Conceivable that the naming strategy could return a quoted identifier, or
// that user enabled <delimited-identifiers/>
return normalizeIdentifierQuoting( objectName );
}
/**

View File

@ -581,12 +581,15 @@ public final class StringHelper {
* @return The quoted version.
*/
public static String quote(String name) {
if ( name == null || name.length() == 0 || isQuoted( name ) ) {
if ( isEmpty( name ) || isQuoted( name ) ) {
return name;
}
else {
return new StringBuilder( name.length() + 2 ).append('`').append( name ).append( '`' ).toString();
}
// Convert the JPA2 specific quoting character (double quote) to Hibernate's (back tick)
else if ( name.startsWith( "\"" ) && name.endsWith( "\"" ) ) {
name = name.substring( 1, name.length() - 1 );
}
return new StringBuilder( name.length() + 2 ).append('`').append( name ).append( '`' ).toString();
}
/**

View File

@ -52,6 +52,7 @@ public interface Metadata {
public SharedCacheMode getSharedCacheMode();
public AccessType getDefaultAccessType();
public boolean useNewIdentifierGenerators();
public boolean isGloballyQuotedIdentifiers();
public String getDefaultSchemaName();
public String getDefaultCatalogName();
}

View File

@ -44,7 +44,7 @@ public class Identifier {
* @return
*/
public static Identifier toIdentifier(String name) {
if ( name == null ) {
if ( StringHelper.isEmpty( name ) ) {
return null;
}
final String trimmedName = name.trim();

View File

@ -34,6 +34,8 @@ import org.hibernate.metamodel.relational.Size;
public interface ColumnRelationalState extends SimpleValueRelationalState {
NamingStrategy getNamingStrategy();
boolean isGloballyQuotedIdentifiers();
String getExplicitColumnName();
boolean isUnique();

View File

@ -24,6 +24,7 @@
package org.hibernate.metamodel.relational.state;
import org.hibernate.MappingException;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.relational.Column;
import org.hibernate.metamodel.relational.DerivedValue;
import org.hibernate.metamodel.relational.SimpleValue;
@ -44,7 +45,7 @@ public class ValueCreator {
) {
final String explicitName = state.getExplicitColumnName();
final String logicalColumnName = state.getNamingStrategy().logicalColumnName( explicitName, attributeName );
final String columnName =
String columnName =
explicitName == null ?
state.getNamingStrategy().propertyToColumnName( attributeName ) :
state.getNamingStrategy().columnName( explicitName );
@ -54,6 +55,9 @@ public class ValueCreator {
if ( columnName == null ) {
throw new IllegalArgumentException( "columnName must be non-null." );
}
if( state.isGloballyQuotedIdentifiers()){
columnName = StringHelper.quote( columnName );
}
Column value = table.getOrCreateColumn( columnName );
value.initialize( state, forceNonNullable, forceUnique );
return value;

View File

@ -53,6 +53,7 @@ import org.hibernate.metamodel.source.annotations.global.TableBinder;
import org.hibernate.metamodel.source.annotations.global.TypeDefBinder;
import org.hibernate.metamodel.source.annotations.util.ConfiguredClassHierarchyBuilder;
import org.hibernate.metamodel.source.annotations.xml.OrmXmlParser;
import org.hibernate.metamodel.source.annotations.xml.PseudoJpaDotNames;
import org.hibernate.metamodel.source.internal.JaxbRoot;
import org.hibernate.metamodel.source.internal.MetadataImpl;
import org.hibernate.metamodel.source.spi.Binder;
@ -73,7 +74,7 @@ public class AnnotationBinder implements Binder {
AnnotationBinder.class.getName()
);
private final MetadataImplementor metadata;
private final MetadataImpl metadata;
private Index index;
private ClassLoaderService classLoaderService;
@ -109,6 +110,10 @@ public class AnnotationBinder implements Binder {
final OrmXmlParser ormParser = new OrmXmlParser( metadata );
index = ormParser.parseAndUpdateIndex( mappings, index );
}
if( index.getAnnotations( PseudoJpaDotNames.DEFAULT_DELIMITED_IDENTIFIERS ) != null ) {
metadata.setGloballyQuotedIdentifiers( true );
}
}
/**

View File

@ -48,6 +48,7 @@ public class ColumnRelationalStateImpl implements ColumnRelationalState {
private final String columnName;
private final boolean unique;
private final boolean nullable;
private final boolean globallyQuotedIdentifiers;
private final Size size;
private final String checkCondition;
private final String customWriteFragment;
@ -64,6 +65,7 @@ public class ColumnRelationalStateImpl implements ColumnRelationalState {
public ColumnRelationalStateImpl(SimpleAttribute attribute, MetadataImplementor meta) {
ColumnValues columnValues = attribute.getColumnValues();
namingStrategy = meta.getOptions().getNamingStrategy();
globallyQuotedIdentifiers = meta.isGloballyQuotedIdentifiers();
columnName = columnValues.getName().isEmpty() ? attribute.getName() : columnValues.getName();
unique = columnValues.isUnique();
nullable = columnValues.isNullable();
@ -83,7 +85,12 @@ public class ColumnRelationalStateImpl implements ColumnRelationalState {
return namingStrategy;
}
@Override
@Override
public boolean isGloballyQuotedIdentifiers() {
return globallyQuotedIdentifiers;
}
@Override
public String getExplicitColumnName() {
return columnName;
}

View File

@ -61,6 +61,7 @@ import org.hibernate.metamodel.domain.Hierarchical;
import org.hibernate.metamodel.domain.SingularAttribute;
import org.hibernate.metamodel.relational.Identifier;
import org.hibernate.metamodel.relational.Schema;
import org.hibernate.metamodel.relational.Table;
import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.relational.UniqueKey;
import org.hibernate.metamodel.source.annotations.HibernateDotNames;
@ -205,8 +206,7 @@ public class EntityBinder {
);
if ( whereAnnotation != null ) {
// no null check needed, it is a required attribute
String clause = whereAnnotation.value( "clause" ).asString();
entityBindingState.setWhereFilter( clause );
entityBindingState.setWhereFilter( JandexHelper.getValueAsString( whereAnnotation, "clause" ) );
}
}
@ -399,31 +399,44 @@ public class EntityBinder {
return new Caching( region, defaultAccessType, true );
}
private Schema.Name createSchemaName() {
String schema = null;
String catalog = null;
private Table createTable() {
String schmaName = null;
String catalogName = null;
String tableName = null;
AnnotationInstance tableAnnotation = JandexHelper.getSingleAnnotation(
entityClass.getClassInfo(), JPADotNames.TABLE
);
if ( tableAnnotation != null ) {
schmaName = JandexHelper.getValueAsString( tableAnnotation, "schema" );
catalogName = JandexHelper.getValueAsString( tableAnnotation, "catalog" );
tableName = JandexHelper.getValueAsString( tableAnnotation, "name" );
}
AnnotationInstance tableAnnotation = JandexHelper.getSingleAnnotation(
entityClass.getClassInfo(), JPADotNames.TABLE
);
if ( tableAnnotation != null ) {
AnnotationValue schemaValue = tableAnnotation.value( "schema" );
AnnotationValue catalogValue = tableAnnotation.value( "catalog" );
schema = schemaValue != null ? schemaValue.asString() : null;
catalog = catalogValue != null ? catalogValue.asString() : null;
}
if ( StringHelper.isEmpty( tableName ) ) {
tableName = meta.getNamingStrategy().classToTableName( entityClass.getPrimaryTableName() );
}
else {
tableName = meta.getNamingStrategy().tableName( tableName );
}
if ( meta.isGloballyQuotedIdentifiers() ) {
schmaName = StringHelper.quote( schmaName );
catalogName = StringHelper.quote( catalogName );
tableName = StringHelper.quote( tableName );
}
final Identifier tableNameIdentifier = Identifier.toIdentifier( tableName );
final Schema schema = meta.getDatabase().getSchema( new Schema.Name( schmaName, catalogName ) );
Table table = schema.getTable( tableNameIdentifier );
if ( table == null ) {
table = schema.createTable( tableNameIdentifier );
}
return table;
}
return new Schema.Name( schema, catalog );
}
private void bindTable(EntityBinding entityBinding) {
final Schema schema = meta.getDatabase().getSchema( createSchemaName() );
final Identifier tableName = Identifier.toIdentifier( entityClass.getPrimaryTableName() );
org.hibernate.metamodel.relational.Table table = schema.getTable( tableName );
if ( table == null ) {
table = schema.createTable( tableName );
}
Table table = createTable();
entityBinding.setBaseTable( table );
AnnotationInstance checkAnnotation = JandexHelper.getSingleAnnotation(
@ -574,9 +587,9 @@ public class EntityBinder {
GenerationType.class
);
String strategy = IdGeneratorBinder.generatorType(
generationType,
meta.getOptions().useNewIdentifierGenerators()
);
generationType,
meta.getOptions().useNewIdentifierGenerators()
);
if ( idGenerator != null && !strategy.equals( idGenerator.getStrategy() ) ) {
//todo how to ?
throw new MappingException(

View File

@ -94,7 +94,7 @@ public class EntityClass extends ConfiguredClass {
public boolean hasOwnTable() {
return hasOwnTable;
}
//todo change a better method name
public String getPrimaryTableName() {
return primaryTableName;
}
@ -145,16 +145,6 @@ public class EntityClass extends ConfiguredClass {
String tableName = null;
if ( hasOwnTable() ) {
tableName = getConfiguredClass().getSimpleName();
AnnotationInstance tableAnnotation = JandexHelper.getSingleAnnotation(
getClassInfo(), JPADotNames.TABLE
);
if ( tableAnnotation != null ) {
AnnotationValue value = tableAnnotation.value( "name" );
String tmp = value == null ? null : value.asString();
if ( tmp != null && !tmp.isEmpty() ) {
tableName = tmp;
}
}
}
else if ( getParent() != null
&& !getParent().getConfiguredClassType().equals( ConfiguredClassType.MAPPED_SUPERCLASS ) ) {

View File

@ -262,21 +262,18 @@ public class EntityBindingStateImpl implements EntityBindingState {
@Override
public int getOptimisticLockMode() {
if ( optimisticLock == OptimisticLockType.ALL ) {
return Versioning.OPTIMISTIC_LOCK_ALL;
}
else if ( optimisticLock == OptimisticLockType.NONE ) {
return Versioning.OPTIMISTIC_LOCK_NONE;
}
else if ( optimisticLock == OptimisticLockType.DIRTY ) {
return Versioning.OPTIMISTIC_LOCK_DIRTY;
}
else if ( optimisticLock == OptimisticLockType.VERSION ) {
return Versioning.OPTIMISTIC_LOCK_VERSION;
}
else {
throw new AssertionFailure( "Unexpected optimistic lock type: " + optimisticLock );
}
switch ( optimisticLock ){
case ALL:
return Versioning.OPTIMISTIC_LOCK_ALL;
case NONE:
return Versioning.OPTIMISTIC_LOCK_NONE;
case DIRTY:
return Versioning.OPTIMISTIC_LOCK_DIRTY;
case VERSION:
return Versioning.OPTIMISTIC_LOCK_VERSION;
default:
throw new AssertionFailure( "Unexpected optimistic lock type: " + optimisticLock );
}
}
@Override

View File

@ -31,14 +31,14 @@ import org.jboss.jandex.DotName;
* @author Strong Liu
*/
public interface PseudoJpaDotNames {
public static final DotName DEFAULT_ACCESS = DotName.createSimple( "default.access" );
public static final DotName DEFAULT_DELIMITED_IDENTIFIERS = DotName.createSimple( "default.delimited.identifiers" );
public static final DotName DEFAULT_ENTITY_LISTENERS = DotName.createSimple( "default.entity.listeners" );
public static final DotName DEFAULT_POST_LOAD = DotName.createSimple( "default.entity.listener.post.load" );
public static final DotName DEFAULT_POST_PERSIST = DotName.createSimple( "default.entity.listener.post.persist" );
public static final DotName DEFAULT_POST_REMOVE = DotName.createSimple( "default.entity.listener.post.remove" );
public static final DotName DEFAULT_POST_UPDATE = DotName.createSimple( "default.entity.listener.post.update" );
public static final DotName DEFAULT_PRE_PERSIST = DotName.createSimple( "default.entity.listener.pre.persist" );
public static final DotName DEFAULT_PRE_REMOVE = DotName.createSimple( "default.entity.listener.pre.remove" );
public static final DotName DEFAULT_PRE_UPDATE = DotName.createSimple( "default.entity.listener.pre.update" );
DotName DEFAULT_ACCESS = DotName.createSimple( "default.access" );
DotName DEFAULT_DELIMITED_IDENTIFIERS = DotName.createSimple( "default.delimited.identifiers" );
DotName DEFAULT_ENTITY_LISTENERS = DotName.createSimple( "default.entity.listeners" );
DotName DEFAULT_POST_LOAD = DotName.createSimple( "default.entity.listener.post.load" );
DotName DEFAULT_POST_PERSIST = DotName.createSimple( "default.entity.listener.post.persist" );
DotName DEFAULT_POST_REMOVE = DotName.createSimple( "default.entity.listener.post.remove" );
DotName DEFAULT_POST_UPDATE = DotName.createSimple( "default.entity.listener.post.update" );
DotName DEFAULT_PRE_PERSIST = DotName.createSimple( "default.entity.listener.pre.persist" );
DotName DEFAULT_PRE_REMOVE = DotName.createSimple( "default.entity.listener.pre.remove" );
DotName DEFAULT_PRE_UPDATE = DotName.createSimple( "default.entity.listener.pre.update" );
}

View File

@ -123,7 +123,12 @@ public class HibernateMappingProcessor implements HbmBindingContext {
return metadata.getOptions().getNamingStrategy();
}
@Override
@Override
public boolean isGloballyQuotedIdentifiers() {
return metadata.isGloballyQuotedIdentifiers();
}
@Override
public MappingDefaults getMappingDefaults() {
return mappingDefaults;
}

View File

@ -25,6 +25,7 @@ package org.hibernate.metamodel.source.hbm;
import org.hibernate.InvalidMappingException;
import org.hibernate.MappingException;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.InheritanceType;
import org.hibernate.metamodel.binding.state.DiscriminatorBindingState;
@ -96,7 +97,11 @@ class RootEntityBinder extends AbstractEntityBinder {
entityBinding.setBaseTable( inLineView );
}
else {
final Identifier tableName = Identifier.toIdentifier( getClassTableName( xmlClazz, entityBinding, null ) );
String classTableName = getClassTableName( xmlClazz, entityBinding, null );
if(getBindingContext().isGloballyQuotedIdentifiers()){
classTableName = StringHelper.quote( classTableName );
}
final Identifier tableName = Identifier.toIdentifier( classTableName );
org.hibernate.metamodel.relational.Table table = schema.getTable( tableName );
if ( table == null ) {
table = schema.createTable( tableName );

View File

@ -206,6 +206,10 @@ public class HbmColumnRelationalState implements ColumnRelationalState {
return container.getNamingStrategy();
}
public boolean isGloballyQuotedIdentifiers(){
return container.isGloballyQuotedIdentifiers();
}
public String getExplicitColumnName() {
return explicitColumnName;
}

View File

@ -55,7 +55,9 @@ public class HbmSimpleValueRelationalStateContainer implements TupleRelationalSt
public BindingContext getBindingContext() {
return bindingContext;
}
public boolean isGloballyQuotedIdentifiers(){
return getBindingContext().isGloballyQuotedIdentifiers();
}
public NamingStrategy getNamingStrategy() {
return getBindingContext().getNamingStrategy();
}

View File

@ -89,6 +89,7 @@ public class MetadataBuilderImpl implements MetadataBuilder {
private SharedCacheMode sharedCacheMode = SharedCacheMode.ENABLE_SELECTIVE;
private AccessType defaultCacheAccessType;
private boolean useNewIdentifierGenerators;
private boolean globallyQuotedIdentifiers;
private String defaultSchemaName;
private String defaultCatalogName;
@ -138,6 +139,17 @@ public class MetadataBuilderImpl implements MetadataBuilder {
},
null
);
globallyQuotedIdentifiers = configService.getSetting(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS,
new ConfigurationService.Converter<Boolean>() {
@Override
public Boolean convert(Object value) {
return Boolean.parseBoolean( value.toString() );
}
},
false
);
}
@ -166,7 +178,12 @@ public class MetadataBuilderImpl implements MetadataBuilder {
return useNewIdentifierGenerators;
}
@Override
@Override
public boolean isGloballyQuotedIdentifiers() {
return globallyQuotedIdentifiers;
}
@Override
public String getDefaultSchemaName() {
return defaultSchemaName;
}

View File

@ -29,6 +29,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.functors.FalsePredicate;
import org.jboss.logging.Logger;
import org.hibernate.DuplicateMappingException;
@ -110,6 +111,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
// todo : keep as part of Database?
private List<AuxiliaryDatabaseObject> auxiliaryDatabaseObjects = new ArrayList<AuxiliaryDatabaseObject>();
private boolean globallyQuotedIdentifiers = false;
public MetadataImpl(MetadataSources metadataSources, Options options) {
this.serviceRegistry = metadataSources.getServiceRegistry();
@ -423,7 +425,16 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
return options.getNamingStrategy();
}
@Override
@Override
public boolean isGloballyQuotedIdentifiers() {
return globallyQuotedIdentifiers || getOptions().isGloballyQuotedIdentifiers();
}
public void setGloballyQuotedIdentifiers(boolean globallyQuotedIdentifiers){
this.globallyQuotedIdentifiers = globallyQuotedIdentifiers;
}
@Override
public MappingDefaults getMappingDefaults() {
return mappingDefaults;
}

View File

@ -31,17 +31,19 @@ import org.hibernate.service.ServiceRegistry;
* @author Steve Ebersole
*/
public interface BindingContext {
public ServiceRegistry getServiceRegistry();
public ServiceRegistry getServiceRegistry();
public NamingStrategy getNamingStrategy();
public NamingStrategy getNamingStrategy();
public MappingDefaults getMappingDefaults();
public MappingDefaults getMappingDefaults();
public MetaAttributeContext getMetaAttributeContext();
public MetaAttributeContext getMetaAttributeContext();
public MetadataImplementor getMetadataImplementor();
public MetadataImplementor getMetadataImplementor();
public <T> Class<T> locateClassByName(String name);
public <T> Class<T> locateClassByName(String name);
public JavaType makeJavaType(String className);
public JavaType makeJavaType(String className);
public boolean isGloballyQuotedIdentifiers();
}

View File

@ -42,13 +42,20 @@ public abstract class BaseAnnotationBindingTestCase extends BaseUnitTestCase {
public void tearDown() {
sources = null;
meta = null;
}
}
public void buildMetadataSources(String ormPath, Class<?>... classes) {
sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
if(ormPath!=null){
sources.addResource( ormPath );
}
for ( Class clazz : classes ) {
sources.addAnnotatedClass( clazz );
}
}
public void buildMetadataSources(Class<?>... classes) {
sources = new MetadataSources( new ServiceRegistryBuilder().buildServiceRegistry() );
for ( Class clazz : classes ) {
sources.addAnnotatedClass( clazz );
}
buildMetadataSources( null, classes );
}
public EntityBinding getEntityBinding(Class<?> clazz) {

View File

@ -0,0 +1,69 @@
package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.junit.Test;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.relational.Identifier;
import static org.junit.Assert.assertEquals;
/**
* @author Strong Liu
*/
public class QuotedIdentifierTests extends BaseAnnotationBindingTestCase {
String ormPath = "org/hibernate/metamodel/source/annotations/xml/orm-quote-identifier.xml";
@Test
public void testDelimitedIdentifiers() {
buildMetadataSources( ormPath, Item.class, Item2.class, Item3.class, Item4.class );
EntityBinding item = getEntityBinding( Item.class );
assertIdentifierEquals( "`Item`",item );
item = getEntityBinding( Item2.class );
assertIdentifierEquals( "`TABLE_ITEM2`",item );
item = getEntityBinding( Item3.class );
assertIdentifierEquals( "`TABLE_ITEM3`",item );
item = getEntityBinding( Item4.class );
assertIdentifierEquals( "`TABLE_ITEM4`",item );
}
private void assertIdentifierEquals(String expected, EntityBinding realValue) {
org.hibernate.metamodel.relational.Table table = (org.hibernate.metamodel.relational.Table) realValue.getBaseTable();
assertEquals( Identifier.toIdentifier( expected ), table.getTableName() );
}
@Entity
private static class Item {
@Id
Long id;
}
@Entity
@Table(name = "TABLE_ITEM2")
private static class Item2 {
@Id
Long id;
}
@Entity
@Table(name = "`TABLE_ITEM3`")
private static class Item3 {
@Id
Long id;
}
@Entity
@Table(name = "\"TABLE_ITEM4\"")
private static class Item4 {
@Id
Long id;
}
}

View File

@ -197,7 +197,7 @@ public class TableNameTest extends BaseUnitTestCase {
"wrong inheritance type", InheritanceType.JOINED, entityClass.getInheritanceType()
);
Assert.assertEquals(
"wrong table name", "FOO", entityClass.getPrimaryTableName()
"wrong table name", "A", entityClass.getPrimaryTableName()
);
assertTrue( iter.hasNext() );

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.0">
<persistence-unit-metadata>
<persistence-unit-defaults>
<delimited-identifiers/>
</persistence-unit-defaults>
</persistence-unit-metadata>
</entity-mappings>