diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java index 8d390e2d3b..d8dd383f43 100755 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/CollectionHelper.java @@ -127,4 +127,20 @@ public final class CollectionHelper { public static List arrayList(int anticipatedSize) { return new ArrayList( anticipatedSize ); } + + public static boolean isEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + + public static boolean isNotEmpty(Collection collection) { + return !isEmpty( collection ); + } + + public static boolean isNotEmpty(Map map) { + return !isEmpty( map ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityBinding.java b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityBinding.java index b814d9c81b..650a997098 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityBinding.java @@ -54,6 +54,7 @@ public class EntityBinding implements AttributeBindingContainer { private Entity entity; private TableSpecification primaryTable; + private String primaryTableName; private Map secondaryTables = new HashMap(); private Value> proxyInterfaceType; @@ -142,17 +143,29 @@ public class EntityBinding implements AttributeBindingContainer { this.primaryTable = primaryTable; } - public TableSpecification locateTable(String tableName) { - if ( tableName == null ) { - return primaryTable; - } + public TableSpecification locateTable(String tableName) { + if ( tableName == null || tableName.equals( getPrimaryTableName() ) ) { + return primaryTable; + } + TableSpecification tableSpec = secondaryTables.get( tableName ); + if ( tableSpec == null ) { + throw new AssertionFailure( + String.format( + "Unable to find table %s amongst tables %s", + tableName, + secondaryTables.keySet() + ) + ); + } + return tableSpec; + } + public String getPrimaryTableName() { + return primaryTableName; + } - TableSpecification tableSpec = secondaryTables.get( tableName ); - if ( tableSpec == null ) { - throw new AssertionFailure( String.format("Unable to find table %s amongst tables %s", tableName, secondaryTables.keySet()) ); - } - return tableSpec; - } + public void setPrimaryTableName(String primaryTableName) { + this.primaryTableName = primaryTableName; + } public void addSecondaryTable(String tableName, TableSpecification table) { secondaryTables.put( tableName, table ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Column.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Column.java index 234a30a32c..577712bc44 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Column.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Column.java @@ -34,7 +34,7 @@ import org.hibernate.metamodel.relational.state.ColumnRelationalState; * @author Gavin King * @author Steve Ebersole */ -public class Column extends AbstractSimpleValue implements SimpleValue { +public class Column extends AbstractSimpleValue { private final Identifier columnName; private boolean nullable; private boolean unique; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/DerivedValue.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/DerivedValue.java index bb2e151256..13e8e39bdc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/DerivedValue.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/DerivedValue.java @@ -30,7 +30,7 @@ import org.hibernate.dialect.Dialect; * * @author Steve Ebersole */ -public class DerivedValue extends AbstractSimpleValue implements SimpleValue { +public class DerivedValue extends AbstractSimpleValue { private final String expression; public DerivedValue(TableSpecification table, int position, String expression) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationMetadataSourceProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationMetadataSourceProcessorImpl.java index b0dfe64a6d..cf2a3ad94e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationMetadataSourceProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationMetadataSourceProcessorImpl.java @@ -35,6 +35,7 @@ import org.jboss.logging.Logger; import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.MetadataSources; import org.hibernate.metamodel.source.MetadataImplementor; import org.hibernate.metamodel.source.MetadataSourceProcessor; @@ -97,7 +98,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc index = parseAndUpdateIndex( mappings, index ); } - if ( index.getAnnotations( PseudoJpaDotNames.DEFAULT_DELIMITED_IDENTIFIERS ) != null ) { + if ( CollectionHelper.isNotEmpty( index.getAnnotations( PseudoJpaDotNames.DEFAULT_DELIMITED_IDENTIFIERS ) ) ) { // todo : this needs to move to AnnotationBindingContext // what happens right now is that specifying this in an orm.xml causes it to effect all orm.xmls metadata.setGloballyQuotedIdentifiers( true ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/DiscriminatorSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/DiscriminatorSourceImpl.java index 40023701f7..609f184cae 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/DiscriminatorSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/DiscriminatorSourceImpl.java @@ -47,10 +47,12 @@ public class DiscriminatorSourceImpl implements DiscriminatorSource { return entityClass.isDiscriminatorIncludedInSql(); } - @Override - public RelationalValueSource getDiscriminatorRelationalValueSource() { - return new ColumnValuesSourceImpl( entityClass.getDiscriminatorColumnValues() ); - } + @Override + public RelationalValueSource getDiscriminatorRelationalValueSource() { + return entityClass.getDiscriminatorFormula() != null ? entityClass.getDiscriminatorFormula() : new ColumnValuesSourceImpl( + entityClass.getDiscriminatorColumnValues() + ); + } @Override public String getExplicitHibernateTypeName() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java index 16cd2a241b..d7fd0a651e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java @@ -56,6 +56,7 @@ import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.JandexHelper; import org.hibernate.metamodel.source.annotations.attribute.ColumnValues; import org.hibernate.metamodel.source.binder.ConstraintSource; +import org.hibernate.metamodel.source.binder.DerivedValueSource; import org.hibernate.metamodel.source.binder.TableSource; /** @@ -96,6 +97,7 @@ public class EntityClass extends ConfiguredClass { private String proxy; private ColumnValues discriminatorColumnValues; + private DerivedValueSource discriminatorFormula; private Class discriminatorType; private String discriminatorMatchValue; private boolean isDiscriminatorForced = true; @@ -144,6 +146,10 @@ public class EntityClass extends ConfiguredClass { return discriminatorColumnValues; } + public DerivedValueSource getDiscriminatorFormula() { + return discriminatorFormula; + } + public Class getDiscriminatorType() { return discriminatorType; } @@ -339,10 +345,22 @@ public class EntityClass extends ConfiguredClass { final AnnotationInstance discriminatorColumnAnnotation = JandexHelper.getSingleAnnotation( getClassInfo(), JPADotNames.DISCRIMINATOR_COLUMN ); - discriminatorColumnValues = new ColumnValues( discriminatorColumnAnnotation ); - discriminatorColumnValues.setNullable( false ); // discriminator column cannot be null + + final AnnotationInstance discriminatorFormulaAnnotation = JandexHelper.getSingleAnnotation( + getClassInfo(), + HibernateDotNames.DISCRIMINATOR_FORMULA + ); + + Class type = String.class; // string is the discriminator default + if ( discriminatorFormulaAnnotation != null ) { + String expression = JandexHelper.getValue( discriminatorFormulaAnnotation, "value", String.class ); + discriminatorFormula = new FormulaImpl( getPrimaryTableSource().getExplicitTableName(), expression ); + } + discriminatorColumnValues = new ColumnValues( null ); //(stliu) give null here, will populate values below + discriminatorColumnValues.setNullable( false ); // discriminator column cannot be null if ( discriminatorColumnAnnotation != null ) { + DiscriminatorType discriminatorType = Enum.valueOf( DiscriminatorType.class, discriminatorColumnAnnotation.value( "discriminatorType" ).asEnum() ); @@ -378,12 +396,13 @@ public class EntityClass extends ConfiguredClass { Integer.class ) ); - if ( discriminatorColumnAnnotation.value( "columnDefinition" ) != null ) { - discriminatorColumnValues.setColumnDefinition( - discriminatorColumnAnnotation.value( "columnDefinition" ) - .asString() + discriminatorColumnValues.setColumnDefinition( + JandexHelper.getValue( + discriminatorColumnAnnotation, + "columnDefinition", + String.class + ) ); - } } discriminatorType = type; @@ -547,58 +566,30 @@ public class EntityClass extends ConfiguredClass { } /** + * todo see {@code Binder#createTable} * @param tableAnnotation a annotation instance, either {@link javax.persistence.Table} or {@link javax.persistence.SecondaryTable} * * @return A table source for the specified annotation instance */ private TableSource createTableSource(AnnotationInstance tableAnnotation) { - String schema = getLocalBindingContext().getMappingDefaults().getSchemaName(); - String catalog = getLocalBindingContext().getMappingDefaults().getCatalogName(); - - + String schema = null; + String catalog = null; if ( tableAnnotation != null ) { - final AnnotationValue schemaValue = tableAnnotation.value( "schema" ); - if ( schemaValue != null ) { - schema = schemaValue.asString(); - } - - final AnnotationValue catalogValue = tableAnnotation.value( "catalog" ); - if ( catalogValue != null ) { - catalog = catalogValue.asString(); - } + schema = JandexHelper.getValue( tableAnnotation, "schema", String.class ); + catalog = JandexHelper.getValue( tableAnnotation, "catalog", String.class ); } - - if ( getLocalBindingContext().isGloballyQuotedIdentifiers() ) { - schema = StringHelper.quote( schema ); - catalog = StringHelper.quote( catalog ); - } - // process the table name String tableName = null; - String explicitTableName = null; + String logicalTableName = null; if ( tableAnnotation != null ) { - explicitTableName = JandexHelper.getValue( tableAnnotation, "name", String.class ); - if ( StringHelper.isNotEmpty( explicitTableName ) ) { - tableName = getLocalBindingContext().getNamingStrategy().tableName( explicitTableName ); - if ( getLocalBindingContext().isGloballyQuotedIdentifiers() && !Identifier.isQuoted( explicitTableName ) ) { - tableName = StringHelper.quote( tableName ); - } + logicalTableName = JandexHelper.getValue( tableAnnotation, "name", String.class ); + if ( StringHelper.isNotEmpty( logicalTableName ) ) { + tableName = logicalTableName; } createUniqueConstraints( tableAnnotation, tableName ); } - - // use the simple table name as default in case there was no table annotation - if ( tableName == null ) { - if ( explicitEntityName == null ) { - tableName = getConfiguredClass().getSimpleName(); - } - else { - tableName = explicitEntityName; - } - } - TableSourceImpl tableSourceImpl; if ( tableAnnotation == null || JPADotNames.TABLE.equals( tableAnnotation.name() ) ) { // for the main table @Table we use 'null' as logical name @@ -606,24 +597,29 @@ public class EntityClass extends ConfiguredClass { } else { // for secondary tables a name must be specified which is used as logical table name - tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, explicitTableName ); + tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, logicalTableName ); } return tableSourceImpl; } - private Set createSecondaryTableSources() { + private Set createSecondaryTableSources() { Set secondaryTableSources = new HashSet(); - + AnnotationInstance secondaryTables = JandexHelper.getSingleAnnotation( + getClassInfo(), + JPADotNames.SECONDARY_TABLES + ); + AnnotationInstance secondaryTable = JandexHelper.getSingleAnnotation( + getClassInfo(), + JPADotNames.SECONDARY_TABLE + ); // collect all @secondaryTable annotations List secondaryTableAnnotations = new ArrayList(); - secondaryTableAnnotations.add( - JandexHelper.getSingleAnnotation( getClassInfo(), JPADotNames.SECONDARY_TABLE ) - ); + if ( secondaryTable != null ) { + secondaryTableAnnotations.add( + secondaryTable + ); + } - AnnotationInstance secondaryTables = JandexHelper.getSingleAnnotation( - getClassInfo(), - JPADotNames.SECONDARY_TABLES - ); if ( secondaryTables != null ) { secondaryTableAnnotations.addAll( Arrays.asList( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/FormulaImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/FormulaImpl.java new file mode 100644 index 0000000000..e0a1b0b51e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/FormulaImpl.java @@ -0,0 +1,26 @@ +package org.hibernate.metamodel.source.annotations.entity; + +import org.hibernate.metamodel.source.binder.DerivedValueSource; + +/** + * @author Strong Liu + */ +public class FormulaImpl implements DerivedValueSource{ + private String tableName; + private final String expression; + + FormulaImpl(String tableName, String expression) { + this.tableName = tableName; + this.expression = expression; + } + + @Override + public String getExpression() { + return expression; + } + + @Override + public String getContainingTableName() { + return tableName; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java index bcd292d5e8..be8b09b081 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/binder/Binder.java @@ -269,7 +269,7 @@ public class Binder { final EntityBinding entityBinding = buildBasicEntityBinding( entitySource, superEntityBinding ); entityBinding.setPrimaryTable( superEntityBinding.getPrimaryTable() ); - + entityBinding.setPrimaryTableName( superEntityBinding.getPrimaryTableName() ); bindDiscriminatorValue( entitySource, entityBinding ); return entityBinding; @@ -439,7 +439,9 @@ public class Binder { } private void bindPersistentCollection(PluralAttributeSource attributeSource, AttributeBindingContainer attributeBindingContainer) { - final PluralAttribute existingAttribute = attributeBindingContainer.getAttributeContainer().locatePluralAttribute( attributeSource.getName() ); + final PluralAttribute existingAttribute = attributeBindingContainer.getAttributeContainer().locatePluralAttribute( + attributeSource.getName() + ); final AbstractPluralAttributeBinding pluralAttributeBinding; if ( attributeSource.getPluralAttributeNature() == PluralAttributeNature.BAG ) { @@ -471,7 +473,9 @@ public class Binder { private BasicAttributeBinding doBasicSingularAttributeBindingCreation( SingularAttributeSource attributeSource, AttributeBindingContainer attributeBindingContainer) { - final SingularAttribute existingAttribute = attributeBindingContainer.getAttributeContainer().locateSingularAttribute( attributeSource.getName() ); + final SingularAttribute existingAttribute = attributeBindingContainer.getAttributeContainer().locateSingularAttribute( + attributeSource.getName() + ); final SingularAttribute attribute; if ( existingAttribute != null ) { attribute = existingAttribute; @@ -641,6 +645,7 @@ public class Binder { final TableSource tableSource = entitySource.getPrimaryTable(); final Table table = createTable( entityBinding, tableSource ); entityBinding.setPrimaryTable( table ); + entityBinding.setPrimaryTableName( table.getTableName().getName() ); } private void bindSecondaryTables(EntitySource entitySource, EntityBinding entityBinding) { @@ -653,12 +658,12 @@ public class Binder { private Table createTable(EntityBinding entityBinding, TableSource tableSource) { final String schemaName = StringHelper.isEmpty( tableSource.getExplicitSchemaName() ) ? currentBindingContext.getMappingDefaults().getSchemaName() - : currentBindingContext.getMetadataImplementor().getOptions().isGloballyQuotedIdentifiers() + : currentBindingContext.isGloballyQuotedIdentifiers() ? StringHelper.quote( tableSource.getExplicitSchemaName() ) : tableSource.getExplicitSchemaName(); final String catalogName = StringHelper.isEmpty( tableSource.getExplicitCatalogName() ) ? currentBindingContext.getMappingDefaults().getCatalogName() - : currentBindingContext.getMetadataImplementor().getOptions().isGloballyQuotedIdentifiers() + : currentBindingContext.isGloballyQuotedIdentifiers() ? StringHelper.quote( tableSource.getExplicitCatalogName() ) : tableSource.getExplicitCatalogName(); diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/InheritanceBindingTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/InheritanceBindingTest.java index 1b2f47a1c5..1767c6bf56 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/InheritanceBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/InheritanceBindingTest.java @@ -23,6 +23,8 @@ */ package org.hibernate.metamodel.source.annotations.entity; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -30,9 +32,12 @@ import javax.persistence.Id; import org.junit.Test; +import org.hibernate.annotations.DiscriminatorFormula; import org.hibernate.annotations.DiscriminatorOptions; import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityDiscriminator; +import org.hibernate.metamodel.relational.DerivedValue; +import org.hibernate.metamodel.relational.SimpleValue; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -114,7 +119,23 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertFalse( "Wrong default value", discriminator.isInserted() ); } - @Entity + @Test + @Resources(annotatedClasses = { Fruit.class, Apple.class }) + public void testDiscriminatorFormula() { + EntityBinding rootEntityBinding = getEntityBinding( Fruit.class ); + assertTrue( rootEntityBinding.isRoot() ); + EntityBinding entityBinding = getEntityBinding( Apple.class ); + assertFalse( entityBinding.isRoot() ); + EntityDiscriminator discriminator = rootEntityBinding.getHierarchyDetails().getEntityDiscriminator(); + SimpleValue simpleValue = discriminator.getBoundValue(); + assertTrue( simpleValue instanceof DerivedValue); + DerivedValue derivedValue = (DerivedValue)simpleValue; + assertEquals( "case when zik_type is null then 0 else zik_type end", derivedValue.getExpression() ); + assertTrue( "Wrong default value", discriminator.isForced() ); + assertFalse( "Wrong default value", discriminator.isInserted() ); + } + + @Entity class SingleEntity { @Id @GeneratedValue @@ -144,6 +165,21 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { @Entity class Jump extends Base { } + + @Entity + @DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER) + @DiscriminatorFormula("case when zik_type is null then 0 else zik_type end") + @DiscriminatorOptions(force = true, insert = false) + class Fruit { + @Id + private int id; + } + + @Entity + class Apple extends Fruit { + + } + }