HHH-6212 Adding 'Iterable<TableSource> getSecondaryTables()' to EntitySource

Extending EntityClass to collect secondary table information
This commit is contained in:
Hardy Ferentschik 2011-07-25 17:27:35 +02:00
parent 5954d1c2c4
commit 57cb51fd14
11 changed files with 399 additions and 108 deletions

View File

@ -52,6 +52,7 @@ public class EntityBinding {
private Entity entity;
private TableSpecification baseTable;
private Map<String, TableSpecification> secondaryTables = new HashMap<String, TableSpecification>();
private Value<Class<?>> proxyInterfaceType;
@ -142,8 +143,19 @@ public class EntityBinding {
}
public TableSpecification getTable(String containingTableName) {
// todo : implement this for secondary table look ups. for now we just return the base table
return baseTable;
if ( containingTableName == null ) {
return baseTable;
}
TableSpecification tableSpec = secondaryTables.get( containingTableName );
if ( tableSpec == null ) {
// todo throw exception !? (HF)
}
return tableSpec;
}
public void addSecondaryTable(String tableName, TableSpecification table) {
secondaryTables.put( tableName, table );
}
public boolean isVersioned() {

View File

@ -41,7 +41,7 @@ public class ColumnValues {
private boolean insertable = true;
private boolean updatable = true;
private String columnDefinition = "";
private String table = "";
private String table = null;
private int length = 255;
private int precision = 0;
private int scale = 0;

View File

@ -57,25 +57,26 @@ import org.hibernate.metamodel.source.annotations.JandexHelper;
import org.hibernate.metamodel.source.annotations.attribute.DiscriminatorColumnValues;
import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.TableSource;
import org.hibernate.metamodel.source.binder.UniqueConstraintSource;
/**
* Represents an entity or mapped superclass configured via annotations/xml.
* Represents an entity or mapped superclass configured via annotations/orm-xml.
*
* @author Hardy Ferentschik
*/
public class EntityClass extends ConfiguredClass {
private final IdType idType;
private final InheritanceType inheritanceType;
private final TableSource tableSource;
private final Set<ConstraintSource> constraintSources;
private final boolean hasOwnTable;
private final String explicitEntityName;
private final String customLoaderQueryName;
private final List<String> synchronizedTableNames;
private final String customTuplizer;
private final int batchSize;
private final TableSource primaryTableSource;
private final Set<TableSource> secondaryTableSources;
private final Set<ConstraintSource> constraintSources;
private boolean isMutable;
private boolean isExplicitPolymorphism;
private OptimisticLockStyle optimisticLockStyle;
@ -107,10 +108,23 @@ public class EntityClass extends ConfiguredClass {
super( classInfo, hierarchyAccessType, parent, context );
this.inheritanceType = inheritanceType;
this.idType = determineIdType();
this.hasOwnTable = definesItsOwnTable();
boolean hasOwnTable = definesItsOwnTable();
this.explicitEntityName = determineExplicitEntityName();
this.constraintSources = new HashSet<ConstraintSource>();
this.tableSource = createTableSource();
if ( hasOwnTable ) {
this.primaryTableSource = createTableSource(
JandexHelper.getSingleAnnotation(
getClassInfo(),
JPADotNames.TABLE
)
);
}
else {
this.primaryTableSource = null;
}
this.secondaryTableSources = createSecondaryTableSources();
this.customLoaderQueryName = determineCustomLoader();
this.synchronizedTableNames = determineSynchronizedTableNames();
this.customTuplizer = determineCustomTuplizer();
@ -159,15 +173,19 @@ public class EntityClass extends ConfiguredClass {
return caching;
}
public TableSource getTableSource() {
public TableSource getPrimaryTableSource() {
if ( definesItsOwnTable() ) {
return tableSource;
return primaryTableSource;
}
else {
return ( (EntityClass) getParent() ).getTableSource();
return ( (EntityClass) getParent() ).getPrimaryTableSource();
}
}
public Set<TableSource> getSecondaryTableSources() {
return secondaryTableSources;
}
public Set<ConstraintSource> getConstraintSources() {
return constraintSources;
}
@ -249,22 +267,6 @@ public class EntityClass extends ConfiguredClass {
return !InheritanceType.SINGLE_TABLE.equals( inheritanceType ) || isEntityRoot();
}
private String processTableAnnotation(AnnotationInstance tableAnnotation) {
String tableName = null;
if ( tableAnnotation != null ) {
String explicitTableName = JandexHelper.getValue( tableAnnotation, "name", String.class );
if ( StringHelper.isNotEmpty( explicitTableName ) ) {
tableName = getContext().getNamingStrategy().tableName( explicitTableName );
if ( getContext().isGloballyQuotedIdentifiers() && !Identifier.isQuoted( explicitTableName ) ) {
tableName = StringHelper.quote( tableName );
}
}
createUniqueConstraints( tableAnnotation, tableName );
}
return tableName;
}
private IdType determineIdType() {
List<AnnotationInstance> idAnnotations = findIdAnnotations( JPADotNames.ID );
List<AnnotationInstance> embeddedIdAnnotations = findIdAnnotations( JPADotNames.EMBEDDED_ID );
@ -501,17 +503,16 @@ public class EntityClass extends ConfiguredClass {
);
}
private TableSource createTableSource() {
if ( !hasOwnTable ) {
return null;
}
/**
* @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 = getContext().getMappingDefaults().getSchemaName();
String catalog = getContext().getMappingDefaults().getCatalogName();
final AnnotationInstance tableAnnotation = JandexHelper.getSingleAnnotation(
getClassInfo(), JPADotNames.TABLE
);
if ( tableAnnotation != null ) {
final AnnotationValue schemaValue = tableAnnotation.value( "schema" );
if ( schemaValue != null ) {
@ -529,7 +530,22 @@ public class EntityClass extends ConfiguredClass {
catalog = StringHelper.quote( catalog );
}
String tableName = processTableAnnotation( tableAnnotation );
// process the table name
String tableName = null;
String explicitTableName = null;
if ( tableAnnotation != null ) {
explicitTableName = JandexHelper.getValue( tableAnnotation, "name", String.class );
if ( StringHelper.isNotEmpty( explicitTableName ) ) {
tableName = getContext().getNamingStrategy().tableName( explicitTableName );
if ( getContext().isGloballyQuotedIdentifiers() && !Identifier.isQuoted( explicitTableName ) ) {
tableName = StringHelper.quote( tableName );
}
}
createUniqueConstraints( tableAnnotation, tableName );
}
// use the simple table name as default in case there was no table annotation
if ( tableName == null ) {
if ( explicitEntityName == null ) {
@ -540,9 +556,48 @@ public class EntityClass extends ConfiguredClass {
}
}
return new TableSourceImpl( schema, catalog, tableName );
TableSourceImpl tableSourceImpl;
if ( tableAnnotation == null || JPADotNames.TABLE.equals( tableAnnotation.name() ) ) {
// for the main table @Table we use 'null' as logical name
tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, null );
}
else {
// for secondary tables a name must be specified which is used as logical table name
tableSourceImpl = new TableSourceImpl( schema, catalog, tableName, explicitTableName );
}
return tableSourceImpl;
}
private Set<TableSource> createSecondaryTableSources() {
Set<TableSource> secondaryTableSources = new HashSet<TableSource>();
// collect all @secondaryTable annotations
List<AnnotationInstance> secondaryTableAnnotations = new ArrayList<AnnotationInstance>();
secondaryTableAnnotations.add(
JandexHelper.getSingleAnnotation( getClassInfo(), JPADotNames.SECONDARY_TABLE )
);
AnnotationInstance secondaryTables = JandexHelper.getSingleAnnotation(
getClassInfo(),
JPADotNames.SECONDARY_TABLES
);
if ( secondaryTables != null ) {
secondaryTableAnnotations.addAll(
Arrays.asList(
JandexHelper.getValue( secondaryTables, "value", AnnotationInstance[].class )
)
);
}
// create table sources
for ( AnnotationInstance annotationInstance : secondaryTableAnnotations ) {
secondaryTableSources.add( createTableSource( annotationInstance ) );
}
return secondaryTableSources;
}
private void createUniqueConstraints(AnnotationInstance tableAnnotation, String tableName) {
AnnotationValue value = tableAnnotation.value( "uniqueConstraints" );
if ( value == null ) {
@ -692,64 +747,4 @@ public class EntityClass extends ConfiguredClass {
public String getDiscriminatorMatchValue() {
return discriminatorMatchValue;
}
class UniqueConstraintSourceImpl implements UniqueConstraintSource {
private final String name;
private final String tableName;
private final List<String> columnNames;
UniqueConstraintSourceImpl(String name, String tableName, List<String> columnNames) {
this.name = name;
this.tableName = tableName;
this.columnNames = columnNames;
}
@Override
public String name() {
return name;
}
@Override
public String getTableName() {
return tableName;
}
@Override
public Iterable<String> columnNames() {
return columnNames;
}
}
class TableSourceImpl implements TableSource {
private final String schema;
private final String catalog;
private final String tableName;
TableSourceImpl(String schema, String catalog, String tableName) {
this.schema = schema;
this.catalog = catalog;
this.tableName = tableName;
}
@Override
public String getExplicitSchemaName() {
return schema;
}
@Override
public String getExplicitCatalogName() {
return catalog;
}
@Override
public String getExplicitTableName() {
return tableName;
}
@Override
public String getLogicalName() {
// todo : (steve) hardy, not sure what to use here... null is ok for the primary table name. this is part of the secondary table support.
return null;
}
}
}

View File

@ -96,7 +96,7 @@ public class EntitySourceImpl implements EntitySource {
@Override
public TableSource getPrimaryTable() {
return entityClass.getTableSource();
return entityClass.getPrimaryTableSource();
}
@Override
@ -206,6 +206,11 @@ public class EntitySourceImpl implements EntitySource {
return entityClass.getConstraintSources();
}
@Override
public Iterable<TableSource> getSecondaryTables() {
return entityClass.getSecondaryTableSources();
}
class LocalBindingContextImpl implements LocalBindingContext {
private final AnnotationBindingContext contextDelegate;

View File

@ -0,0 +1,110 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, 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.metamodel.source.annotations.entity;
import org.hibernate.metamodel.source.binder.TableSource;
class TableSourceImpl implements TableSource {
private final String schema;
private final String catalog;
private final String tableName;
private final String logicalName;
TableSourceImpl(String schema, String catalog, String tableName, String logicalName) {
this.schema = schema;
this.catalog = catalog;
this.tableName = tableName;
this.logicalName = logicalName;
}
@Override
public String getExplicitSchemaName() {
return schema;
}
@Override
public String getExplicitCatalogName() {
return catalog;
}
@Override
public String getExplicitTableName() {
return tableName;
}
@Override
public String getLogicalName() {
return logicalName;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
TableSourceImpl that = (TableSourceImpl) o;
if ( catalog != null ? !catalog.equals( that.catalog ) : that.catalog != null ) {
return false;
}
if ( logicalName != null ? !logicalName.equals( that.logicalName ) : that.logicalName != null ) {
return false;
}
if ( schema != null ? !schema.equals( that.schema ) : that.schema != null ) {
return false;
}
if ( tableName != null ? !tableName.equals( that.tableName ) : that.tableName != null ) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = schema != null ? schema.hashCode() : 0;
result = 31 * result + ( catalog != null ? catalog.hashCode() : 0 );
result = 31 * result + ( tableName != null ? tableName.hashCode() : 0 );
result = 31 * result + ( logicalName != null ? logicalName.hashCode() : 0 );
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "TableSourceImpl" );
sb.append( "{schema='" ).append( schema ).append( '\'' );
sb.append( ", catalog='" ).append( catalog ).append( '\'' );
sb.append( ", tableName='" ).append( tableName ).append( '\'' );
sb.append( ", logicalName='" ).append( logicalName ).append( '\'' );
sb.append( '}' );
return sb.toString();
}
}

View File

@ -0,0 +1,103 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, 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.metamodel.source.annotations.entity;
import java.util.List;
import org.hibernate.metamodel.source.binder.UniqueConstraintSource;
/**
* @author Hardy Ferentschik
*/
class UniqueConstraintSourceImpl implements UniqueConstraintSource {
private final String name;
private final String tableName;
private final List<String> columnNames;
UniqueConstraintSourceImpl(String name, String tableName, List<String> columnNames) {
this.name = name;
this.tableName = tableName;
this.columnNames = columnNames;
}
@Override
public String name() {
return name;
}
@Override
public String getTableName() {
return tableName;
}
@Override
public Iterable<String> columnNames() {
return columnNames;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
UniqueConstraintSourceImpl that = (UniqueConstraintSourceImpl) o;
if ( columnNames != null ? !columnNames.equals( that.columnNames ) : that.columnNames != null ) {
return false;
}
if ( name != null ? !name.equals( that.name ) : that.name != null ) {
return false;
}
if ( tableName != null ? !tableName.equals( that.tableName ) : that.tableName != null ) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + ( tableName != null ? tableName.hashCode() : 0 );
result = 31 * result + ( columnNames != null ? columnNames.hashCode() : 0 );
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append( "UniqueConstraintSourceImpl" );
sb.append( "{name='" ).append( name ).append( '\'' );
sb.append( ", tableName='" ).append( tableName ).append( '\'' );
sb.append( ", columnNames=" ).append( columnNames );
sb.append( '}' );
return sb.toString();
}
}

View File

@ -55,6 +55,7 @@ import org.hibernate.metamodel.relational.DerivedValue;
import org.hibernate.metamodel.relational.Identifier;
import org.hibernate.metamodel.relational.Schema;
import org.hibernate.metamodel.relational.SimpleValue;
import org.hibernate.metamodel.relational.Table;
import org.hibernate.metamodel.relational.TableSpecification;
import org.hibernate.metamodel.relational.Tuple;
import org.hibernate.metamodel.relational.UniqueKey;
@ -610,11 +611,22 @@ public class Binder {
return subContext;
}
// Relational ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private void bindPrimaryTable(EntitySource entitySource, EntityBinding entityBinding) {
final TableSource tableSource = entitySource.getPrimaryTable();
final Table table = createTable( entityBinding, tableSource );
entityBinding.setBaseTable( table );
}
private void bindSecondaryTables(EntitySource entitySource, EntityBinding entityBinding) {
for ( TableSource secondaryTableSource : entitySource.getSecondaryTables() ) {
final Table table = createTable( entityBinding, secondaryTableSource );
entityBinding.addSecondaryTable( secondaryTableSource.getLogicalName(), table );
}
}
private Table createTable(EntityBinding entityBinding, TableSource tableSource) {
final String schemaName = StringHelper.isEmpty( tableSource.getExplicitSchemaName() )
? currentBindingContext.getMappingDefaults().getSchemaName()
: currentBindingContext.getMetadataImplementor().getOptions().isGloballyQuotedIdentifiers()
@ -638,16 +650,10 @@ public class Binder {
tableName = StringHelper.quote( tableName );
}
final org.hibernate.metamodel.relational.Table table = currentBindingContext.getMetadataImplementor()
return currentBindingContext.getMetadataImplementor()
.getDatabase()
.getSchema( new Schema.Name( schemaName, catalogName ) )
.locateOrCreateTable( Identifier.toIdentifier( tableName ) );
entityBinding.setBaseTable( table );
}
private void bindSecondaryTables(EntitySource entitySource, EntityBinding entityBinding) {
// todo : implement
}
private void bindTableUniqueConstraints(EntitySource entitySource, EntityBinding entityBinding) {

View File

@ -77,6 +77,13 @@ public interface EntitySource extends SubclassEntityContainer, AttributeSourceCo
*/
public TableSource getPrimaryTable();
/**
* Obtain the secondary tables for this entity
*
* @return returns an iterator over the secondary tables for this entity
*/
public Iterable<TableSource> getSecondaryTables();
/**
* Obtain the name of a custom tuplizer class to be used.
*

View File

@ -25,6 +25,7 @@ package org.hibernate.metamodel.source.hbm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.hibernate.EntityMode;
@ -37,6 +38,7 @@ import org.hibernate.metamodel.source.binder.ConstraintSource;
import org.hibernate.metamodel.source.binder.EntitySource;
import org.hibernate.metamodel.source.binder.MetaAttributeSource;
import org.hibernate.metamodel.source.binder.SubclassEntitySource;
import org.hibernate.metamodel.source.binder.TableSource;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.EntityElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLAnyElement;
import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLManyToManyElement;
@ -49,6 +51,7 @@ import org.hibernate.metamodel.source.hbm.jaxb.mapping.XMLTuplizerElement;
/**
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public abstract class AbstractEntitySourceImpl implements EntitySource {
private final MappingDocument sourceMappingDocument;
@ -254,4 +257,9 @@ public abstract class AbstractEntitySourceImpl implements EntitySource {
public Iterable<ConstraintSource> getConstraints() {
return Collections.emptySet();
}
@Override
public Iterable<TableSource> getSecondaryTables() {
return Collections.emptySet();
}
}

View File

@ -23,6 +23,8 @@
*/
package org.hibernate.metamodel.source.hbm;
import java.util.Iterator;
import org.hibernate.EntityMode;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.engine.OptimisticLockStyle;

View File

@ -0,0 +1,43 @@
package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.SecondaryTable;
import org.junit.Test;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.relational.Table;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
/**
* @author Hardy Ferentschik
*/
public class SecondaryTableTest extends BaseAnnotationBindingTestCase {
@Entity
@SecondaryTable(name = "SECOND_TABLE")
class EntityWithSecondaryTable {
@Id
private long id;
}
@Test
@Resources(annotatedClasses = EntityWithSecondaryTable.class)
public void testSecondaryTableExists() {
EntityBinding binding = getEntityBinding( EntityWithSecondaryTable.class );
Table table = (Table) binding.getTable( "SECOND_TABLE" );
assertEquals( "The secondary table should exist", "SECOND_TABLE", table.getTableName().getName() );
}
@Test
@Resources(annotatedClasses = EntityWithSecondaryTable.class)
public void testRetrievingUnknownTable() {
EntityBinding binding = getEntityBinding( EntityWithSecondaryTable.class );
Table table = (Table) binding.getTable( "FOO" );
assertNull( table ); // todo - what should really happen for a non existing table
}
}