HHH-7736 partial joined subclass support

This commit is contained in:
Strong Liu 2012-11-15 19:54:51 +08:00
parent 4359b9971b
commit e998269402
20 changed files with 386 additions and 194 deletions

View File

@ -26,9 +26,34 @@ package org.hibernate.cache.spi.entry;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* {@link CacheEntry} structure, used for construct / deconstruct the cache entry to different format that store in the 2LC.
*
* @author Gavin King
*/
public interface CacheEntryStructure {
public Object structure(Object item);
public Object destructure(Object map, SessionFactoryImplementor factory);
public interface CacheEntryStructure<S,T> {
/**
* Convert the giving {@param source} to the target format of {@link T}.
* <br>
* The generic type of {@link S} should be either of :
* <ul>
* <li>{@link CacheEntry}</li>
* <li>{@link CollectionCacheEntry}</li>
* </ul>
* </br>
*
* This is called just before cache entry being stored into 2LC.
*
* @param source The raw cache entry.
* @return The target type of value that being persisted into 2LC.
*/
public T structure(S source);
/**
* Deconstruct the {@param target} that load from 2LC to its source type of {@link S}.
*
* @param target The item that load from the 2LC.
* @param factory The SessionFactoryImplementor.
* @return The source type of cache entry.
*/
public S destructure(T target, SessionFactoryImplementor factory);
}

View File

@ -33,36 +33,36 @@ import org.hibernate.persister.entity.EntityPersister;
/**
* @author Gavin King
*/
public class StructuredCacheEntry implements CacheEntryStructure {
public class StructuredCacheEntry implements CacheEntryStructure<CacheEntry, Map> {
private EntityPersister persister;
private final EntityPersister persister;
public StructuredCacheEntry(EntityPersister persister) {
this.persister = persister;
}
public Object destructure(Object item, SessionFactoryImplementor factory) {
Map map = (Map) item;
boolean lazyPropertiesUnfetched = ( (Boolean) map.get("_lazyPropertiesUnfetched") ).booleanValue();
String subclass = (String) map.get("_subclass");
Object version = map.get("_version");
EntityPersister subclassPersister = factory.getEntityPersister(subclass);
@Override
public CacheEntry destructure(Map map, SessionFactoryImplementor factory) {
boolean lazyPropertiesUnfetched = ( (Boolean) map.get( "_lazyPropertiesUnfetched" ) ).booleanValue();
String subclass = (String) map.get( "_subclass" );
Object version = map.get( "_version" );
EntityPersister subclassPersister = factory.getEntityPersister( subclass );
String[] names = subclassPersister.getPropertyNames();
Serializable[] state = new Serializable[names.length];
for ( int i=0; i<names.length; i++ ) {
for ( int i = 0; i < names.length; i++ ) {
state[i] = (Serializable) map.get( names[i] );
}
return new CacheEntry(state, subclass, lazyPropertiesUnfetched, version);
return new CacheEntry( state, subclass, lazyPropertiesUnfetched, version );
}
public Object structure(Object item) {
CacheEntry entry = (CacheEntry) item;
@Override
public Map structure(CacheEntry entry) {
String[] names = persister.getPropertyNames();
Map map = new HashMap(names.length+2);
Map map = new HashMap( names.length + 3 );
map.put( "_subclass", entry.getSubclass() );
map.put( "_version", entry.getVersion() );
map.put( "_lazyPropertiesUnfetched", entry.areLazyPropertiesUnfetched() );
for ( int i=0; i<names.length; i++ ) {
for ( int i = 0; i < names.length; i++ ) {
map.put( names[i], entry.getDisassembledState()[i] );
}
return map;

View File

@ -30,17 +30,19 @@ import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* For other plural attributes except map, this impl is used.
* </br>
* Internally, a list that contains all collection states is stored into 2LC.
*
* @author Gavin King
*/
public class StructuredCollectionCacheEntry implements CacheEntryStructure {
public Object structure(Object item) {
CollectionCacheEntry entry = (CollectionCacheEntry) item;
public class StructuredCollectionCacheEntry implements CacheEntryStructure<CollectionCacheEntry, List<Serializable>> {
@Override
public List<Serializable> structure(CollectionCacheEntry entry) {
return Arrays.asList( entry.getState() );
}
public Object destructure(Object item, SessionFactoryImplementor factory) {
List list = (List) item;
@Override
public CollectionCacheEntry destructure(List<Serializable> list, SessionFactoryImplementor factory) {
return new CollectionCacheEntry( list.toArray( new Serializable[list.size()] ) );
}

View File

@ -31,31 +31,30 @@ import java.util.Map;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* (De)structure the map type collection attribute that is being cached into 2LC.
*
* @author Gavin King
*/
public class StructuredMapCacheEntry implements CacheEntryStructure {
public Object structure(Object item) {
CollectionCacheEntry entry = (CollectionCacheEntry) item;
Serializable[] state = entry.getState();
Map map = new HashMap(state.length);
for ( int i=0; i<state.length; ) {
map.put( state[i++], state[i++] );
public class StructuredMapCacheEntry implements CacheEntryStructure<CollectionCacheEntry, Map<Serializable,Serializable>> {
@Override
public Map<Serializable, Serializable> structure(CollectionCacheEntry entry) {
final Serializable[] states = entry.getState();
final Map<Serializable, Serializable> map = new HashMap<Serializable, Serializable>( states.length );
for ( final Serializable state : states ) {
map.put( state, state );
}
return map;
}
public Object destructure(Object item, SessionFactoryImplementor factory) {
Map map = (Map) item;
Serializable[] state = new Serializable[ map.size()*2 ];
int i=0;
Iterator iter = map.entrySet().iterator();
while ( iter.hasNext() ) {
Map.Entry me = (Map.Entry) iter.next();
state[i++] = (Serializable) me.getKey();
state[i++] = (Serializable) me.getValue();
@Override
public CollectionCacheEntry destructure(Map<Serializable, Serializable> map, SessionFactoryImplementor factory) {
Serializable[] states = new Serializable[map.size() * 2];
int i = 0;
for ( final Serializable key : map.keySet() ) {
states[i++] = key;
states[i++] = map.get( key );
}
return new CollectionCacheEntry(state);
return new CollectionCacheEntry( states );
}
}

View File

@ -26,14 +26,23 @@ package org.hibernate.cache.spi.entry;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* The very simple implementation of {@code CacheEntryStructure} that doing nothing but just return what it get.
*
* </br>
*
* This is used when {@link org.hibernate.cfg.AvailableSettings#USE_STRUCTURED_CACHE} is set to {@code false}.
*
* NOTE: This property is set to {@code false} by default.
*
* @author Gavin King
*/
public class UnstructuredCacheEntry implements CacheEntryStructure {
public class UnstructuredCacheEntry implements CacheEntryStructure<Object, Object> {
@Override
public Object structure(Object item) {
return item;
}
@Override
public Object destructure(Object map, SessionFactoryImplementor factory) {
return map;
}

View File

@ -36,33 +36,33 @@ import java.util.List;
* invoke the Iterators in sequence until all Iterators are exhausted.
*
*/
public class JoinedIterator implements Iterator {
public class JoinedIterator<T> implements Iterator<T> {
private static final Iterator[] ITERATORS = {};
// wrapped iterators
private Iterator[] iterators;
private Iterator<T>[] iterators;
// index of current iterator in the wrapped iterators array
private int currentIteratorIndex;
// the current iterator
private Iterator currentIterator;
private Iterator<T> currentIterator;
// the last used iterator
private Iterator lastUsedIterator;
private Iterator<T> lastUsedIterator;
public JoinedIterator(List iterators) {
this( (Iterator[]) iterators.toArray(ITERATORS) );
public JoinedIterator(List<Iterator<T>> iterators) {
this( iterators.toArray(ITERATORS) );
}
public JoinedIterator(Iterator[] iterators) {
public JoinedIterator(Iterator<T>[] iterators) {
if( iterators==null )
throw new NullPointerException("Unexpected NULL iterators argument");
this.iterators = iterators;
}
public JoinedIterator(Iterator first, Iterator second) {
public JoinedIterator(Iterator<T> first, Iterator<T> second) {
this( new Iterator[] { first, second } );
}
@ -71,7 +71,7 @@ public class JoinedIterator implements Iterator {
return currentIterator.hasNext();
}
public Object next() {
public T next() {
updateCurrentIterator();
return currentIterator.next();
}

View File

@ -29,17 +29,17 @@ import java.util.Iterator;
/**
* @author Gavin King
*/
public final class SingletonIterator implements Iterator {
public final class SingletonIterator<T> implements Iterator<T> {
private Object value;
private T value;
private boolean hasNext = true;
public boolean hasNext() {
return hasNext;
}
public Object next() {
if (hasNext) {
public T next() {
if ( hasNext ) {
hasNext = false;
return value;
}
@ -52,7 +52,7 @@ public final class SingletonIterator implements Iterator {
throw new UnsupportedOperationException();
}
public SingletonIterator(Object value) {
public SingletonIterator(T value) {
this.value = value;
}

View File

@ -54,6 +54,7 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.internal.HibernateTypeHelper.ReflectedCollectionJavaTypes;
import org.hibernate.metamodel.spi.MetadataImplementor;
import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeBinding;
@ -133,6 +134,7 @@ import org.hibernate.metamodel.spi.source.PluralAttributeElementSource;
import org.hibernate.metamodel.spi.source.PluralAttributeIndexSource;
import org.hibernate.metamodel.spi.source.PluralAttributeKeySource;
import org.hibernate.metamodel.spi.source.PluralAttributeSource;
import org.hibernate.metamodel.spi.source.PrimaryKeyJoinColumnSource;
import org.hibernate.metamodel.spi.source.RelationalValueSource;
import org.hibernate.metamodel.spi.source.RelationalValueSourceContainer;
import org.hibernate.metamodel.spi.source.RootEntitySource;
@ -158,7 +160,7 @@ import org.jboss.logging.Logger;
/**
* The common binder shared between annotations and {@code hbm.xml} processing.
* <p/>
* The API consists of {@link #Binder(org.hibernate.metamodel.spi.MetadataImplementor, IdentifierGeneratorFactory)} and {@link #bindEntities(Iterable)}
* The API consists of {@link #Binder(org.hibernate.metamodel.spi.MetadataImplementor, IdentifierGeneratorFactory)} and {@link #bindEntityHierarchies}
*
* @author Steve Ebersole
* @author Hardy Ferentschik
@ -270,7 +272,7 @@ public class Binder {
private AttributeBinding attributeBinding( final String entityName, final String attributeName ) {
// Check if binding has already been created
final EntityBinding entityBinding = entityBinding( entityName );
final EntityBinding entityBinding = findOrBindingEntityBinding( entityName );
final AttributeSource attributeSource = attributeSourcesByName.get( attributeSourcesByNameKey( entityName, attributeName ) );
bindAttribute( entityBinding, attributeSource );
return entityBinding.locateAttributeBinding( attributeName );
@ -772,21 +774,100 @@ public class Binder {
typeHelper.bindJdbcDataType( resolvedType, value );
}
private EntityBinding bindEntities( final EntityHierarchy entityHierarchy ) {
private EntityBinding bindSubEntity(final EntitySource entitySource, final EntityBinding superEntityBinding) {
// Return existing binding if available
EntityBinding entityBinding = metadata.getEntityBinding( entitySource.getEntityName() );
if ( entityBinding != null ) {
return entityBinding;
}
final LocalBindingContext bindingContext = entitySource.getLocalBindingContext();
bindingContexts.push( bindingContext );
try {
// Create new entity binding
entityBinding = createEntityBinding( entitySource, superEntityBinding );
entityBinding.setMutable( entityBinding.getHierarchyDetails().getRootEntityBinding().isMutable() );
bindPrimaryTable( entityBinding, entitySource );
bindSubEntityPrimaryKey( entityBinding, entitySource );
bindSecondaryTables( entityBinding, entitySource );
bindUniqueConstraints( entityBinding, entitySource );
bindAttributes( entityBinding, entitySource );
bindSubEntities( entityBinding, entitySource );
return entityBinding;
} finally {
bindingContexts.pop();
}
}
private void bindSubEntityPrimaryKey(final EntityBinding entityBinding, final EntitySource entitySource) {
final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType();
final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding();
if ( superEntityBinding == null ) {
throw new AssertionFailure( "super entitybinding is null " );
}
if ( inheritanceType == InheritanceType.JOINED ) {
SubclassEntitySource subclassEntitySource = (SubclassEntitySource) entitySource;
ForeignKey fk = entityBinding.getPrimaryTable().createForeignKey(
superEntityBinding.getPrimaryTable(),
subclassEntitySource.getJoinedForeignKeyName()
);
final List<PrimaryKeyJoinColumnSource> primaryKeyJoinColumnSources = subclassEntitySource.getPrimaryKeyJoinColumnSources();
final boolean hasPrimaryKeyJoinColumns = CollectionHelper.isNotEmpty( primaryKeyJoinColumnSources );
final List<Column> superEntityBindingPrimaryKeyColumns = superEntityBinding.getPrimaryTable().getPrimaryKey().getColumns();
for ( int i = 0; i < superEntityBindingPrimaryKeyColumns.size(); i++ ) {
Column superEntityBindingPrimaryKeyColumn = superEntityBindingPrimaryKeyColumns.get( i );
PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource = hasPrimaryKeyJoinColumns && i < primaryKeyJoinColumnSources
.size() ? primaryKeyJoinColumnSources.get( i ) : null;
final String columnName;
if ( primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnName() ) ) {
columnName = bindingContext().getNamingStrategy().columnName( primaryKeyJoinColumnSource.getColumnName() );
} else {
columnName = superEntityBindingPrimaryKeyColumn.getColumnName().getText();
}
Column column = entityBinding.getPrimaryTable().locateOrCreateColumn( columnName );
column.setCheckCondition( superEntityBindingPrimaryKeyColumn.getCheckCondition() );
column.setComment( superEntityBindingPrimaryKeyColumn.getComment() );
column.setDefaultValue( superEntityBindingPrimaryKeyColumn.getDefaultValue() );
column.setIdentity( superEntityBindingPrimaryKeyColumn.isIdentity() );
column.setNullable( superEntityBindingPrimaryKeyColumn.isNullable() );
column.setReadFragment( superEntityBindingPrimaryKeyColumn.getReadFragment() );
column.setWriteFragment( superEntityBindingPrimaryKeyColumn.getWriteFragment() );
column.setUnique( superEntityBindingPrimaryKeyColumn.isUnique() );
final String sqlType;
if(primaryKeyJoinColumnSource!=null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnDefinition() )){
sqlType = primaryKeyJoinColumnSource.getColumnDefinition();
} else {
sqlType = superEntityBindingPrimaryKeyColumn.getSqlType();
}
column.setSqlType( sqlType );
column.setSize( superEntityBindingPrimaryKeyColumn.getSize() );
column.setJdbcDataType( superEntityBindingPrimaryKeyColumn.getJdbcDataType() );
entityBinding.getPrimaryTable().getPrimaryKey().addColumn( column );
//todo still need to figure out how to handle the referencedColumnName property
fk.addColumnMapping( column, superEntityBindingPrimaryKeyColumn );
}
}
}
/**
* Binding a single entity hierarchy.
*
* @param entityHierarchy The entity hierarchy to be binded.
*
* @return The root {@link EntityBinding} of the entity hierarchy mapping.
*/
private EntityBinding bindEntityHierarchy(final EntityHierarchy entityHierarchy) {
final RootEntitySource rootEntitySource = entityHierarchy.getRootEntitySource();
// Return existing binding if available
EntityBinding rootEntityBinding = metadata.getEntityBinding( rootEntitySource.getEntityName() );
if ( rootEntityBinding != null ) {
return rootEntityBinding;
}
// Save inheritance type and entity mode that will apply to entire hierarchy
inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() );
entityModes.push( rootEntitySource.getEntityMode() );
final LocalBindingContext bindingContext = rootEntitySource.getLocalBindingContext();
bindingContexts.push( bindingContext );
setupBindingContext( entityHierarchy, rootEntitySource );
try {
// Create root entity binding
rootEntityBinding = createEntityBinding( rootEntitySource, null );
bindPrimaryTable( rootEntityBinding, rootEntitySource );
// Create/Bind root-specific information
bindIdentifier( rootEntityBinding, rootEntitySource.getIdentifierSource() );
bindSecondaryTables( rootEntityBinding, rootEntitySource );
@ -808,14 +889,30 @@ public class Binder {
bindSubEntities( rootEntityBinding, rootEntitySource );
}
} finally {
bindingContexts.pop();
inheritanceTypes.pop();
entityModes.pop();
cleanupBindingContext();
}
return rootEntityBinding;
}
public void bindEntities( final Iterable< EntityHierarchy > entityHierarchies ) {
private void cleanupBindingContext() {
bindingContexts.pop();
inheritanceTypes.pop();
entityModes.pop();
}
private void setupBindingContext(EntityHierarchy entityHierarchy, RootEntitySource rootEntitySource) {
// Save inheritance type and entity mode that will apply to entire hierarchy
inheritanceTypes.push( entityHierarchy.getHierarchyInheritanceType() );
entityModes.push( rootEntitySource.getEntityMode() );
bindingContexts.push( rootEntitySource.getLocalBindingContext() );
}
/**
* The entry point of {@linkplain Binder} class, binds all the entity hierarchy one by one.
*
* @param entityHierarchies The entity hierarchies resolved from mappings
*/
public void bindEntityHierarchies(final Iterable<EntityHierarchy> entityHierarchies) {
entitySourcesByName.clear();
attributeSourcesByName.clear();
inheritanceTypes.clear();
@ -829,29 +926,7 @@ public class Binder {
}
// Bind each entity hierarchy
for ( final EntityHierarchy entityHierarchy : entityHierarchies ) {
bindEntities( entityHierarchy );
}
}
private EntityBinding bindEntity( final EntitySource entitySource, final EntityBinding superEntityBinding ) {
// Return existing binding if available
EntityBinding entityBinding = metadata.getEntityBinding( entitySource.getEntityName() );
if ( entityBinding != null ) {
return entityBinding;
}
final LocalBindingContext bindingContext = entitySource.getLocalBindingContext();
bindingContexts.push( bindingContext );
try {
// Create new entity binding
entityBinding = createEntityBinding( entitySource, superEntityBinding );
entityBinding.setMutable( entityBinding.getHierarchyDetails().getRootEntityBinding().isMutable() );
bindSecondaryTables( entityBinding, entitySource );
bindUniqueConstraints( entityBinding, entitySource );
bindAttributes( entityBinding, entitySource );
bindSubEntities( entityBinding, entitySource );
return entityBinding;
} finally {
bindingContexts.pop();
bindEntityHierarchy( entityHierarchy );
}
}
@ -1122,7 +1197,7 @@ public class Binder {
bindingContext().qualifyClassName( attributeSource.getReferencedEntityName() != null
? attributeSource.getReferencedEntityName()
: referencedJavaTypeValue.getValue().getName() );
final EntityBinding referencedEntityBinding = entityBinding( referencedEntityName );
final EntityBinding referencedEntityBinding = findOrBindingEntityBinding( referencedEntityName );
//now find the referenced attribute binding, either the referenced entity's id attribute or the referenced attribute
//todo referenced entityBinding null check?
// Foreign key...
@ -1528,7 +1603,7 @@ public class Binder {
createAttributePath( attributeBinding )
) );
}
EntityBinding referencedEntityBinding = entityBinding( referencedEntityName );
EntityBinding referencedEntityBinding = findOrBindingEntityBinding( referencedEntityName );
bindOneToManyCollectionKey( attributeBinding, attributeSource, referencedEntityBinding );
bindOneToManyCollectionElement(
(OneToManyPluralAttributeElementBinding) attributeBinding.getPluralAttributeElementBinding(),
@ -1563,8 +1638,22 @@ public class Binder {
return attributeBinding;
}
private void bindPrimaryTable( final EntityBinding entityBinding, final EntitySource entitySource ) {
final TableSpecification table = createTable(
private void bindPrimaryTable(final EntityBinding entityBinding, final EntitySource entitySource) {
final EntityBinding superEntityBinding = entityBinding.getSuperEntityBinding();
final InheritanceType inheritanceType = entityBinding.getHierarchyDetails().getInheritanceType();
final TableSpecification table;
final String tableName;
if ( superEntityBinding != null && inheritanceType == InheritanceType.SINGLE_TABLE ) {
table = superEntityBinding.getPrimaryTable();
tableName = superEntityBinding.getPrimaryTableName();
// Configure discriminator if present
final String discriminatorValue = entitySource.getDiscriminatorMatchValue() != null ?
entitySource.getDiscriminatorMatchValue()
: entitySource.getEntityName();
entityBinding.setDiscriminatorMatchValue( discriminatorValue );
}
else {
table = createTable(
entitySource.getPrimaryTable(), new DefaultNamingStrategy() {
@Override
@ -1576,8 +1665,10 @@ public class Binder {
}
}
);
tableName = table.getLogicalName().getText();
}
entityBinding.setPrimaryTable( table );
entityBinding.setPrimaryTableName( table.getLogicalName().getText() );
entityBinding.setPrimaryTableName( tableName );
}
private void bindSecondaryTables( final EntityBinding entityBinding, final EntitySource entitySource ) {
@ -1727,7 +1818,7 @@ public class Binder {
private void bindSubEntities( final EntityBinding entityBinding, final EntitySource entitySource ) {
for ( final SubclassEntitySource subEntitySource : entitySource.subclassEntitySources() ) {
bindEntity( subEntitySource, entityBinding );
bindSubEntity( subEntitySource, entityBinding );
}
}
@ -1954,6 +2045,7 @@ public class Binder {
entityClassName,
bindingContext.makeClassReference( entityClassName ),
superEntityBinding == null ? null : superEntityBinding.getEntity() ) );
entityBinding.setEntityName( entitySource.getEntityName() );
entityBinding.setJpaEntityName( entitySource.getJpaEntityName() ); //must before creating primary table
entityBinding.setDynamicUpdate( entitySource.isDynamicUpdate() );
@ -1961,39 +2053,11 @@ public class Binder {
entityBinding.setBatchSize( entitySource.getBatchSize() );
entityBinding.setSelectBeforeUpdate( entitySource.isSelectBeforeUpdate() );
entityBinding.setAbstract( entitySource.isAbstract() );
entityBinding.setCustomLoaderName( entitySource.getCustomLoaderName() );
entityBinding.setCustomInsert( entitySource.getCustomSqlInsert() );
entityBinding.setCustomUpdate( entitySource.getCustomSqlUpdate() );
entityBinding.setCustomDelete( entitySource.getCustomSqlDelete() );
entityBinding.setJpaCallbackClasses( entitySource.getJpaCallbackClasses() );
// Create relational table
if ( superEntityBinding != null && inheritanceType == InheritanceType.SINGLE_TABLE ) {
entityBinding.setPrimaryTable( superEntityBinding.getPrimaryTable() );
entityBinding.setPrimaryTableName( superEntityBinding.getPrimaryTableName() );
// Configure discriminator if present
final String discriminatorValue = entitySource.getDiscriminatorMatchValue();
if ( discriminatorValue != null ) {
entityBinding.setDiscriminatorMatchValue( discriminatorValue );
}
else {
entityBinding.setDiscriminatorMatchValue( entitySource.getEntityName() );
}
}
else {
bindPrimaryTable( entityBinding, entitySource );
}
if ( inheritanceType == InheritanceType.JOINED && superEntityBinding != null ) {
ForeignKey fk = entityBinding.getPrimaryTable().createForeignKey(
superEntityBinding.getPrimaryTable(),
( (SubclassEntitySource) entitySource ).getJoinedForeignKeyName()
);
// explicitly maps to target table pk
for ( Column column : entityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) {
fk.addColumn( column );
}
}
// todo: deal with joined and unioned subclass bindings
// todo: bind fetch profiles
@ -2313,27 +2377,33 @@ public class Binder {
:referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ) );
}
private EntityBinding entityBinding( final String entityName ) {
/**
*
* @param entityName
* @return
*/
private EntityBinding findOrBindingEntityBinding(final String entityName) {
// Check if binding has already been created
EntityBinding entityBinding = metadata.getEntityBinding( entityName );
if ( entityBinding == null ) {
// Find appropriate source to create binding
final EntitySource entitySource = entitySourcesByName.get( entityName );
if(entitySource == null) {
if ( entitySource == null ) {
String msg = log.missingEntitySource( entityName );
bindingContext().makeMappingException( msg );
}
// Get super entity binding (creating it if necessary using recursive call to this method)
final EntityBinding superEntityBinding =
SubclassEntitySource.class.isInstance( entitySource )
? entityBinding( ( ( SubclassEntitySource ) entitySource ).superclassEntitySource().getEntityName() )
: null;
// Create entity binding
entityBinding =
superEntityBinding == null
? bindEntities( entityHierarchiesByRootEntitySource.get( entitySource ) )
: bindEntity( entitySource, superEntityBinding );
if ( SubclassEntitySource.class.isInstance( entitySource ) ) {
String superEntityName = ( (SubclassEntitySource) entitySource ).superclassEntitySource()
.getEntityName();
EntityBinding superEntityBinding = findOrBindingEntityBinding( superEntityName );
entityBinding = bindSubEntity( entitySource, superEntityBinding );
}
else {
EntityHierarchy entityHierarchy = entityHierarchiesByRootEntitySource.get( entitySource );
entityBinding = bindEntityHierarchy( entityHierarchy );
}
}
return entityBinding;
}

View File

@ -211,7 +211,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
}
final HbmMetadataSourceProcessorImpl processor = new HbmMetadataSourceProcessorImpl( this, jaxbRoots );
final Binder binder = new Binder( this, identifierGeneratorFactory );
binder.bindEntities( processor.extractEntityHierarchies() );
binder.bindEntityHierarchies( processor.extractEntityHierarchies() );
}
@ -350,7 +350,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable {
private void processMappings(MetadataSourceProcessor[] metadataSourceProcessors) {
final Binder binder = new Binder( this, identifierGeneratorFactory );
for ( MetadataSourceProcessor processor : metadataSourceProcessors ) {
binder.bindEntities( processor.extractEntityHierarchies() );
binder.bindEntityHierarchies( processor.extractEntityHierarchies() );
}
}

View File

@ -473,7 +473,8 @@ public class AssociationAttribute extends MappedAttribute {
if ( joinTableAnnotation != null ) {
if ( JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_ONE ) == null
&& JandexHelper.getSingleAnnotation( annotations, JPADotNames.ONE_TO_MANY ) == null
&& JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_MANY ) == null ) {
&& JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_MANY ) == null
&& JandexHelper.getSingleAnnotation( annotations, JPADotNames.MANY_TO_ONE ) == null ) {
String msg = coreLogger.joinTableForNonAssociationAttribute(
getContext().getOrigin().getName(),
getName()

View File

@ -262,7 +262,7 @@ public class EntityClass extends ConfiguredClass {
return JandexHelper.getValue( jpaEntityAnnotation, "name", String.class );
}
private List<PrimaryKeyJoinColumnSource> determinPrimaryKeyJoinColumns() {
protected List<PrimaryKeyJoinColumnSource> determinPrimaryKeyJoinColumns() {
if ( inheritanceType != InheritanceType.JOINED ) {
return null;
}

View File

@ -32,9 +32,12 @@ import javax.persistence.DiscriminatorType;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.internal.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.internal.source.annotations.attribute.BasicAttribute;
import org.hibernate.metamodel.internal.source.annotations.attribute.Column;
@ -43,6 +46,7 @@ import org.hibernate.metamodel.internal.source.annotations.util.HibernateDotName
import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames;
import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper;
import org.hibernate.metamodel.spi.binding.InheritanceType;
import org.hibernate.metamodel.spi.source.PrimaryKeyJoinColumnSource;
/**
* Represents an root entity configured via annotations/orm-xml.
@ -51,6 +55,10 @@ import org.hibernate.metamodel.spi.binding.InheritanceType;
* @author Brett Meyer
*/
public class RootEntityClass extends EntityClass {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
RootEntityClass.class.getName()
);
private final IdType idType;
private final List<MappedSuperclass> mappedSuperclasses;
@ -132,6 +140,14 @@ public class RootEntityClass extends EntityClass {
return isDiscriminatorIncludedInSql;
}
protected List<PrimaryKeyJoinColumnSource> determinPrimaryKeyJoinColumns() {
List<PrimaryKeyJoinColumnSource> results = super.determinPrimaryKeyJoinColumns();
if ( CollectionHelper.isNotEmpty( results ) ) {
LOG.invalidPrimaryKeyJoinColumnAnnotation();
}
return null;
}
@Override
public Collection<BasicAttribute> getSimpleAttributes() {
List<BasicAttribute> attributes = new ArrayList<BasicAttribute>();

View File

@ -153,9 +153,9 @@ public interface JPADotNames {
DotName GENERATED_VALUE = DotName.createSimple( GeneratedValue.class.getName() );
DotName ID = DotName.createSimple( Id.class.getName() );
DotName ID_CLASS = DotName.createSimple( IdClass.class.getName() );
DotName INHERITANCE = DotName.createSimple( Inheritance.class.getName() );
DotName INHERITANCE_TYPE = DotName.createSimple( InheritanceType.class.getName() );
DotName JOIN_COLUMN = DotName.createSimple( JoinColumn.class.getName() );
DotName INHERITANCE = DotName.createSimple( Inheritance.class.getName() );
DotName JOIN_COLUMNS = DotName.createSimple( JoinColumns.class.getName() );
DotName JOIN_TABLE = DotName.createSimple( JoinTable.class.getName() );
DotName LOB = DotName.createSimple( Lob.class.getName() );

View File

@ -26,6 +26,7 @@ package org.hibernate.metamodel.spi.binding;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -35,6 +36,9 @@ import org.hibernate.EntityMode;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.ValueHolder;
import org.hibernate.internal.util.collections.JoinedIterable;
import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.internal.util.collections.SingletonIterator;
import org.hibernate.mapping.PropertyGeneration;
import org.hibernate.metamodel.spi.domain.AttributeContainer;
import org.hibernate.metamodel.spi.domain.Entity;
import org.hibernate.metamodel.spi.domain.SingularAttribute;
@ -102,7 +106,8 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
private Map<String, AttributeBinding> attributeBindingMap = new HashMap<String, AttributeBinding>();
private List<JpaCallbackSource> jpaCallbackClasses = new ArrayList<JpaCallbackSource>();
private final int subEntityBindingId;
private int nextSubEntityBindingId = 0;
/**
* Used to instantiate the EntityBinding for an entity that is the root of an inheritance hierarchy
*
@ -112,6 +117,7 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
public EntityBinding(InheritanceType inheritanceType, EntityMode entityMode) {
this.superEntityBinding = null;
this.hierarchyDetails = new HierarchyDetails( this, inheritanceType, entityMode );
this.subEntityBindingId = 0;
}
/**
@ -123,6 +129,11 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
this.superEntityBinding = superEntityBinding;
this.superEntityBinding.subEntityBindings.add( this );
this.hierarchyDetails = superEntityBinding.getHierarchyDetails();
this.subEntityBindingId = superEntityBinding.nextSubEntityBindingId();
}
private int nextSubEntityBindingId(){
return ++nextSubEntityBindingId;
}
public HierarchyDetails getHierarchyDetails() {
@ -133,6 +144,10 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
return superEntityBinding;
}
public int getSubEntityBindingId() {
return subEntityBindingId;
}
public boolean isRoot() {
return superEntityBinding == null;
}
@ -578,6 +593,21 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
return new JoinedIterable<AttributeBinding>( iterables );
}
public Iterator<TableSpecification> getTableClosureIterator() {
if ( superEntityBinding == null ) {
return new SingletonIterator<TableSpecification>( getPrimaryTable() );
}
else {
return new JoinedIterator<TableSpecification>(
superEntityBinding.getTableClosureIterator(),
new SingletonIterator<TableSpecification>( getPrimaryTable() )
);
}
}
public Iterator getKeyClosureIterator(){
return null;
}
public void setJpaCallbackClasses(List<JpaCallbackSource> jpaCallbackClasses) {
this.jpaCallbackClasses = jpaCallbackClasses;
}

View File

@ -25,7 +25,9 @@ package org.hibernate.metamodel.spi.relational;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.function.SQLFunctionRegistry;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.sql.Template;
/**
* Models a physical column
@ -128,6 +130,25 @@ public class Column extends AbstractValue {
this.comment = comment;
}
public String getTemplate(Dialect dialect, SQLFunctionRegistry functionRegistry) {
return hasCustomRead()
? Template.renderWhereStringTemplate( readFragment, dialect, functionRegistry )
: Template.TEMPLATE + '.' + getColumnName().getText( dialect );
}
public boolean hasCustomRead() {
return StringHelper.isNotEmpty( readFragment );
}
public String getReadExpr(Dialect dialect) {
return hasCustomRead() ? readFragment : getColumnName().getText( dialect );
}
public String getWriteExpr() {
return StringHelper.isNotEmpty( writeFragment ) ? writeFragment : "?";
}
public Size getSize() {
return size;
}

View File

@ -2140,7 +2140,7 @@ public abstract class AbstractCollectionPersister
}
}
@Override
public int getSize(Serializable key, SessionImplementor session) {
try {
PreparedStatement st = session.getTransactionCoordinator()

View File

@ -186,16 +186,16 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
return delete.toStatementString();
}
@Override
public boolean consumesEntityAlias() {
return false;
}
@Override
public boolean consumesCollectionAlias() {
// return !isOneToMany();
return true;
}
@Override
public boolean isOneToMany() {
return false;
}
@ -211,7 +211,9 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
protected int doUpdateRows(Serializable id, PersistentCollection collection, SessionImplementor session)
throws HibernateException {
if ( ArrayHelper.isAllFalse(elementColumnIsSettable) ) return 0;
if ( ArrayHelper.isAllFalse( elementColumnIsSettable ) ) {
return 0;
}
try {
PreparedStatement st = null;
@ -342,11 +344,11 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
throws MappingException {
return BatchingCollectionInitializer.createBatchingCollectionInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
}
@Override
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
return "";
}
@Override
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
return "";
}

View File

@ -507,8 +507,8 @@ public abstract class AbstractEntityPersister
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ?
(CacheEntryStructure) new StructuredCacheEntry(this) :
(CacheEntryStructure) new UnstructuredCacheEntry();
new StructuredCacheEntry(this) :
new UnstructuredCacheEntry();
this.entityMetamodel = new EntityMetamodel( persistentClass, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer();

View File

@ -53,6 +53,8 @@ import org.hibernate.mapping.Table;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.EntityDiscriminator;
import org.hibernate.metamodel.spi.relational.DerivedValue;
import org.hibernate.metamodel.spi.relational.PrimaryKey;
import org.hibernate.metamodel.spi.relational.TableSpecification;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.SelectFragment;
import org.hibernate.type.*;
@ -517,20 +519,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
super( entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// DISCRIMINATOR
if ( entityBinding.isPolymorphic() ) {
EntityDiscriminator discriminator = entityBinding.getHierarchyDetails().getEntityDiscriminator();
discriminator.getRelationalValue();
Type discriminatorType = discriminator
.getExplicitHibernateTypeDescriptor()
.getResolvedTypeMapping();
try {
org.hibernate.type.DiscriminatorType dtype = (org.hibernate.type.DiscriminatorType) discriminatorType;
discriminatorValue = dtype.stringToObject( entityBinding.getDiscriminatorMatchValue() );
discriminatorSQLString = dtype.objectToSQLString( discriminatorValue, factory.getDialect() );
}
catch ( ClassCastException cce ) {
throw new MappingException( "Illegal discriminator type: " + discriminatorType.getName() );
}
catch ( Exception e ) {
try{
discriminatorValue = entityBinding.getSubEntityBindingId();
discriminatorSQLString = discriminatorValue.toString();
} catch ( Exception e ){
throw new MappingException( "Could not format discriminator value to SQL string", e );
}
}
@ -546,27 +538,54 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
final int idColumnSpan = getIdentifierColumnSpan();
ArrayList tables = new ArrayList();
ArrayList<String> tables = new ArrayList<String>();
ArrayList keyColumns = new ArrayList();
ArrayList keyColumnReaders = new ArrayList();
ArrayList keyColumnReaderTemplates = new ArrayList();
ArrayList cascadeDeletes = new ArrayList();
// Iterator titer = persistentClass.getTableClosureIterator();
// Iterator kiter = persistentClass.getKeyClosureIterator();
Iterator<TableSpecification> titer = entityBinding.getTableClosureIterator();
while ( titer.hasNext() ){
TableSpecification table = titer.next();
String tableName = table.getLogicalName().getText(factory.getDialect());
tables.add( tableName );
String[] keyCols = new String[idColumnSpan];
String[] keyColReaders = new String[idColumnSpan];
String[] keyColReaderTemplates = new String[idColumnSpan];
PrimaryKey primaryKey= table.getPrimaryKey();
for ( int k = 0; k < idColumnSpan; k++ ) {
org.hibernate.metamodel.spi.relational.Column column = primaryKey.getColumns().get( k );
keyCols[k] = column.getColumnName().getText(factory.getDialect());
keyColReaders[k] = column.getReadExpr( factory.getDialect() );
keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() );
}
keyColumns.add( keyCols );
keyColumnReaders.add( keyColReaders );
keyColumnReaderTemplates.add( keyColReaderTemplates );
cascadeDeletes.add( false && factory.getDialect().supportsCascadeDelete() ); //todo add @OnDelete support
}
//Span of the tables directly mapped by this entity and super-classes, if any
coreTableSpan = tables.size();
//todo secondary table
isNullableTable = new boolean[]{true};
naturalOrderTableNames = ArrayHelper.toStringArray( tables );
naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray( keyColumns );
naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray( keyColumnReaders );
naturalOrderTableKeyColumnReaderTemplates = ArrayHelper.to2DStringArray( keyColumnReaderTemplates );
naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray( cascadeDeletes );
ArrayList subtables = new ArrayList();
ArrayList isConcretes = new ArrayList();
ArrayList isDeferreds = new ArrayList();
ArrayList isLazies = new ArrayList();
keyColumns = new ArrayList();
//todo add sub class tables here
//----------------------------------------------
tableSpan = -1;
tableNames = null;
naturalOrderTableNames = null;
tableKeyColumns = null;
tableKeyColumnReaders = null;
tableKeyColumnReaderTemplates = null;
naturalOrderTableKeyColumns = null;
naturalOrderTableKeyColumnReaders = null;
naturalOrderTableKeyColumnReaderTemplates = null;
naturalOrderCascadeDeleteEnabled = null;
spaces = null;
subclassClosure = null;
subclassTableNameClosure = null;
@ -584,8 +603,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
notNullColumnTableNumbers = null;
constraintOrderedTableNames = null;
constraintOrderedKeyColumnNames = null;
coreTableSpan = -1;
isNullableTable = null;
//-----------------------------
initLockers();
initSubclassPropertyAliasesMap( entityBinding );

View File

@ -75,7 +75,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
private final String[] subclassSpaces;
private final Object discriminatorValue;
private final String discriminatorSQLValue;
private final Map subclassByDiscriminatorValue = new HashMap();
private final Map<Integer,String> subclassByDiscriminatorValue = new HashMap<Integer,String>();
private final String[] constraintOrderedTableNames;
private final String[][] constraintOrderedKeyColumnNames;
@ -185,7 +185,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
spaces[i] = (String) iter.next();
}
HashSet subclassTables = new HashSet();
HashSet<String> subclassTables = new HashSet<String>();
iter = persistentClass.getSubclassTableClosureIterator();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
@ -251,7 +251,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
super(entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// TODO: implement!!! initializing final fields to null to make compiler happy.
subquery = null;
tableName = null;
tableName = entityBinding.getPrimaryTable().getQualifiedName( factory.getDialect() );
subclassClosure = null;
spaces = null;
subclassSpaces = null;