HHH-6911 - Write DiscriminatorValue to DiscriminatorColumn when combined with InheritanceType#JOINED

This commit is contained in:
Steve Ebersole 2013-12-13 00:11:25 -06:00
parent e64e89b2cc
commit 5329bba1ea
8 changed files with 560 additions and 65 deletions

View File

@ -40,6 +40,7 @@ import javax.persistence.Basic;
import javax.persistence.Cacheable; import javax.persistence.Cacheable;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue; import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection; import javax.persistence.ElementCollection;
@ -97,6 +98,7 @@ import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Check; import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionId; import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.Columns; import org.hibernate.annotations.Columns;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.DiscriminatorOptions; import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.annotations.Fetch; import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchProfile; import org.hibernate.annotations.FetchProfile;
@ -635,12 +637,27 @@ public final class AnnotationBinder {
Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns( Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns(
clazzToProcess, mappings, inheritanceState, superEntity clazzToProcess, mappings, inheritanceState, superEntity
); );
Ejb3DiscriminatorColumn discriminatorColumn = null;
final Ejb3DiscriminatorColumn discriminatorColumn;
if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) { if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
discriminatorColumn = processDiscriminatorProperties( discriminatorColumn = processSingleTableDiscriminatorProperties(
clazzToProcess, mappings, inheritanceState, entityBinder clazzToProcess,
mappings,
inheritanceState,
entityBinder
); );
} }
else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
discriminatorColumn = processJoinedDiscriminatorProperties(
clazzToProcess,
mappings,
inheritanceState,
entityBinder
);
}
else {
discriminatorColumn = null;
}
entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) ); entityBinder.setProxy( clazzToProcess.getAnnotation( Proxy.class ) );
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) ); entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
@ -685,7 +702,13 @@ public final class AnnotationBinder {
OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class ); OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class );
boolean onDeleteAppropriate = false; boolean onDeleteAppropriate = false;
if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) && inheritanceState.hasParents() ) {
// todo : sucks that this is separate from RootClass distinction
final boolean isInheritanceRoot = !inheritanceState.hasParents();
final boolean hasSubclasses = inheritanceState.hasSiblings();
if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
if ( inheritanceState.hasParents() ) {
onDeleteAppropriate = true; onDeleteAppropriate = true;
final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass; final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass;
SimpleValue key = new DependantValue( mappings, jsc.getTable(), jsc.getIdentifier() ); SimpleValue key = new DependantValue( mappings, jsc.getTable(), jsc.getIdentifier() );
@ -704,27 +727,46 @@ public final class AnnotationBinder {
SecondPass sp = new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, mappings ); SecondPass sp = new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, mappings );
mappings.addSecondPass( sp ); mappings.addSecondPass( sp );
mappings.addSecondPass( new CreateKeySecondPass( jsc ) ); mappings.addSecondPass( new CreateKeySecondPass( jsc ) );
} }
else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
if ( ! inheritanceState.hasParents() ) { if ( isInheritanceRoot ) {
if ( inheritanceState.hasSiblings() || !discriminatorColumn.isImplicit() ) { // the class we are processing is the root of the hierarchy, see if we had a discriminator column
//need a discriminator column // (it is perfectly valid for joined subclasses to not have discriminators).
bindDiscriminatorToPersistentClass( if ( discriminatorColumn != null ) {
( RootClass ) persistentClass, // we have a discriminator column
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn, discriminatorColumn,
entityBinder.getSecondaryTables(), entityBinder.getSecondaryTables(),
propertyHolder, propertyHolder,
mappings mappings
); );
entityBinder.bindDiscriminatorValue();//bind it again since the type might have changed //bind it again since the type might have changed
entityBinder.bindDiscriminatorValue();
} }
} }
} }
else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
//nothing to do
} }
if (onDeleteAnn != null && !onDeleteAppropriate) LOG.invalidOnDeleteAnnotation(propertyHolder.getEntityName()); else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
if ( isInheritanceRoot ) {
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn,
entityBinder.getSecondaryTables(),
propertyHolder,
mappings
);
//bind it again since the type might have changed
entityBinder.bindDiscriminatorValue();
}
}
}
if ( onDeleteAnn != null && !onDeleteAppropriate ) {
LOG.invalidOnDeleteAnnotation(propertyHolder.getEntityName());
}
// try to find class level generators // try to find class level generators
HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings ); HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
@ -782,32 +824,43 @@ public final class AnnotationBinder {
entityBinder.processComplementaryTableDefinitions( tabAnn ); entityBinder.processComplementaryTableDefinitions( tabAnn );
} }
// parse everything discriminator column relevant in case of single table inheritance /**
private static Ejb3DiscriminatorColumn processDiscriminatorProperties(XClass clazzToProcess, Mappings mappings, InheritanceState inheritanceState, EntityBinder entityBinder) { * Process all discriminator-related metadata per rules for "single table" inheritance
*/
private static Ejb3DiscriminatorColumn processSingleTableDiscriminatorProperties(
XClass clazzToProcess,
Mappings mappings,
InheritanceState inheritanceState,
EntityBinder entityBinder) {
final boolean isRoot = !inheritanceState.hasParents();
Ejb3DiscriminatorColumn discriminatorColumn = null; Ejb3DiscriminatorColumn discriminatorColumn = null;
javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation( javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation(
javax.persistence.DiscriminatorColumn.class javax.persistence.DiscriminatorColumn.class
); );
DiscriminatorType discriminatorType = discAnn != null ? DiscriminatorType discriminatorType = discAnn != null
discAnn.discriminatorType() : ? discAnn.discriminatorType()
DiscriminatorType.STRING; : DiscriminatorType.STRING;
org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation( org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.DiscriminatorFormula.class org.hibernate.annotations.DiscriminatorFormula.class
); );
if ( !inheritanceState.hasParents() ) { if ( isRoot ) {
discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn( discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType, discAnn, discFormulaAnn, mappings discriminatorType,
discAnn,
discFormulaAnn,
mappings
); );
} }
if ( discAnn != null && inheritanceState.hasParents() ) { if ( discAnn != null && !isRoot ) {
LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() ); LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() );
} }
String discrimValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class ) ? final String discriminatorValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class )
clazzToProcess.getAnnotation( DiscriminatorValue.class ).value() : ? clazzToProcess.getAnnotation( DiscriminatorValue.class ).value()
null; : null;
entityBinder.setDiscriminatorValue( discrimValue ); entityBinder.setDiscriminatorValue( discriminatorValue );
DiscriminatorOptions discriminatorOptions = clazzToProcess.getAnnotation( DiscriminatorOptions.class ); DiscriminatorOptions discriminatorOptions = clazzToProcess.getAnnotation( DiscriminatorOptions.class );
if ( discriminatorOptions != null) { if ( discriminatorOptions != null) {
@ -818,6 +871,53 @@ public final class AnnotationBinder {
return discriminatorColumn; return discriminatorColumn;
} }
/**
* Process all discriminator-related metadata per rules for "joined" inheritance
*/
private static Ejb3DiscriminatorColumn processJoinedDiscriminatorProperties(
XClass clazzToProcess,
Mappings mappings,
InheritanceState inheritanceState,
EntityBinder entityBinder) {
if ( clazzToProcess.isAnnotationPresent( DiscriminatorFormula.class ) ) {
throw new MappingException( "@DiscriminatorFormula on joined inheritance not supported at this time" );
}
// DiscriminatorValue handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final DiscriminatorValue discriminatorValueAnnotation = clazzToProcess.getAnnotation( DiscriminatorValue.class );
final String discriminatorValue = discriminatorValueAnnotation != null
? clazzToProcess.getAnnotation( DiscriminatorValue.class ).value()
: null;
entityBinder.setDiscriminatorValue( discriminatorValue );
// DiscriminatorColumn handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final DiscriminatorColumn discriminatorColumnAnnotation = clazzToProcess.getAnnotation( DiscriminatorColumn.class );
if ( !inheritanceState.hasParents() ) {
if ( discriminatorColumnAnnotation != null || mappings.useImplicitDiscriminatorColumnForJoinedInheritance() ) {
final DiscriminatorType discriminatorType = discriminatorColumnAnnotation != null
? discriminatorColumnAnnotation.discriminatorType()
: DiscriminatorType.STRING;
return Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType,
discriminatorColumnAnnotation,
null,
mappings
);
}
}
else {
if ( discriminatorColumnAnnotation != null ) {
LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() );
}
}
return null;
}
private static void processIdPropertiesIfNotAlready( private static void processIdPropertiesIfNotAlready(
Map<XClass, InheritanceState> inheritanceStatePerClass, Map<XClass, InheritanceState> inheritanceStatePerClass,
Mappings mappings, Mappings mappings,
@ -1375,7 +1475,7 @@ public final class AnnotationBinder {
} }
private static void bindDiscriminatorToPersistentClass( private static void bindDiscriminatorColumnToRootPersistentClass(
RootClass rootClass, RootClass rootClass,
Ejb3DiscriminatorColumn discriminatorColumn, Ejb3DiscriminatorColumn discriminatorColumn,
Map<String, Join> secondaryTables, Map<String, Join> secondaryTables,
@ -1387,10 +1487,10 @@ public final class AnnotationBinder {
} }
discriminatorColumn.setJoins( secondaryTables ); discriminatorColumn.setJoins( secondaryTables );
discriminatorColumn.setPropertyHolder( propertyHolder ); discriminatorColumn.setPropertyHolder( propertyHolder );
SimpleValue discrim = new SimpleValue( mappings, rootClass.getTable() ); SimpleValue discriminatorColumnBinding = new SimpleValue( mappings, rootClass.getTable() );
rootClass.setDiscriminator( discrim ); rootClass.setDiscriminator( discriminatorColumnBinding );
discriminatorColumn.linkWithValue( discrim ); discriminatorColumn.linkWithValue( discriminatorColumnBinding );
discrim.setTypeName( discriminatorColumn.getDiscriminatorTypeName() ); discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
rootClass.setPolymorphic( true ); rootClass.setPolymorphic( true );
if ( LOG.isTraceEnabled() ) { if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Setting discriminator for entity {0}", rootClass.getEntityName() ); LOG.tracev( "Setting discriminator for entity {0}", rootClass.getEntityName() );

View File

@ -604,6 +604,21 @@ public interface AvailableSettings {
String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select"; String FORCE_DISCRIMINATOR_IN_SELECTS_BY_DEFAULT = "hibernate.discriminator.force_in_select";
/**
* The legacy behavior of Hibernate is to not use discriminators for joined inheritance (Hibernate does not need
* the discriminator...). However, some JPA providers do need the discriminator for handling joined inheritance.
* In the interest of portability this capability has been added to Hibernate too.
* <p/>
* However, we want to make sure that legacy applications continue to work as well. Which puts us in a bind in
* terms of how to handle "implicit" discriminator mappings. The solution is to assume that the absence of
* discriminator metadata means to follow the legacy behavior *unless* this setting is enabled. With this setting
* enabled, Hibernate will interpret the absence of discriminator metadata as an indication to use the JPA
* defined defaults for these absent annotations.
*
* See Hibernate Jira issue HHH-6911 for additional background info,
*/
String IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS = "hibernate.discriminator.implicit_for_joined";
String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans"; String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans";
String HQL_BULK_ID_STRATEGY = "hibernate.hql.bulk_id_strategy"; String HQL_BULK_ID_STRATEGY = "hibernate.hql.bulk_id_strategy";
@ -678,4 +693,5 @@ public interface AvailableSettings {
String LOG_SESSION_METRICS = "hibernate.session.events.log"; String LOG_SESSION_METRICS = "hibernate.session.events.log";
String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto"; String AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto";
} }

View File

@ -3375,6 +3375,7 @@ public class Configuration implements Serializable {
} }
private Boolean useNewGeneratorMappings; private Boolean useNewGeneratorMappings;
@Override @Override
public boolean useNewGeneratorMappings() { public boolean useNewGeneratorMappings() {
if ( useNewGeneratorMappings == null ) { if ( useNewGeneratorMappings == null ) {
@ -3385,6 +3386,20 @@ public class Configuration implements Serializable {
return useNewGeneratorMappings; return useNewGeneratorMappings;
} }
private Boolean implicitDiscriminatorColumnForJoinedInheritance;
@Override
public boolean useImplicitDiscriminatorColumnForJoinedInheritance() {
if ( implicitDiscriminatorColumnForJoinedInheritance == null ) {
final String booleanName = getConfigurationProperties()
.getProperty( AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS );
implicitDiscriminatorColumnForJoinedInheritance = Boolean.valueOf( booleanName );
}
return implicitDiscriminatorColumnForJoinedInheritance;
}
private Boolean useNationalizedCharacterData; private Boolean useNationalizedCharacterData;
@Override @Override

View File

@ -34,8 +34,8 @@ import org.hibernate.annotations.DiscriminatorFormula;
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class Ejb3DiscriminatorColumn extends Ejb3Column { public class Ejb3DiscriminatorColumn extends Ejb3Column {
private static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "DTYPE"; public static final String DEFAULT_DISCRIMINATOR_COLUMN_NAME = "DTYPE";
private static final String DEFAULT_DISCRIMINATOR_TYPE = "string"; public static final String DEFAULT_DISCRIMINATOR_TYPE = "string";
private static final int DEFAULT_DISCRIMINATOR_LENGTH = 31; private static final int DEFAULT_DISCRIMINATOR_LENGTH = 31;
private String discriminatorTypeName; private String discriminatorTypeName;

View File

@ -797,6 +797,17 @@ public interface Mappings {
*/ */
public boolean useNewGeneratorMappings(); public boolean useNewGeneratorMappings();
/**
* Should we handle discriminators for joined inheritance per legacy Hibernate rules, or
* Should we use the new generator strategy mappings. This is controlled by the
* {@link AvailableSettings#USE_NEW_ID_GENERATOR_MAPPINGS} setting.
*
* @return True if the new generators should be used, false otherwise.
*
* @see AvailableSettings#IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS
*/
public boolean useImplicitDiscriminatorColumnForJoinedInheritance();
/** /**
* Should we use nationalized variants of character data by default? This is controlled by the * Should we use nationalized variants of character data by default? This is controlled by the
* {@link AvailableSettings#USE_NATIONALIZED_CHARACTER_DATA} setting. * {@link AvailableSettings#USE_NATIONALIZED_CHARACTER_DATA} setting.

View File

@ -43,8 +43,10 @@ import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.DynamicFilterAliasGenerator; import org.hibernate.internal.DynamicFilterAliasGenerator;
import org.hibernate.internal.FilterAliasGenerator; import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Join; import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass; import org.hibernate.mapping.MappedSuperclass;
@ -53,11 +55,16 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.binding.EntityBinding; import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.sql.CaseFragment; import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert;
import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SelectFragment;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.*;
import org.hibernate.type.Type; import org.hibernate.type.DiscriminatorType;
import org.jboss.logging.Logger;
/** /**
* An <tt>EntityPersister</tt> implementing the normalized "table-per-subclass" * An <tt>EntityPersister</tt> implementing the normalized "table-per-subclass"
@ -66,6 +73,13 @@ import org.hibernate.type.Type;
* @author Gavin King * @author Gavin King
*/ */
public class JoinedSubclassEntityPersister extends AbstractEntityPersister { public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private static final Logger log = Logger.getLogger( JoinedSubclassEntityPersister.class );
private static final String IMPLICIT_DISCRIMINATOR_ALIAS = "clazz_";
private static final Object NULL_DISCRIMINATOR = new MarkerObject("<null discriminator>");
private static final Object NOT_NULL_DISCRIMINATOR = new MarkerObject("<not null discriminator>");
private static final String NULL_STRING = "null";
private static final String NOT_NULL_STRING = "not null";
// the class hierarchy structure // the class hierarchy structure
private final int tableSpan; private final int tableSpan;
@ -116,6 +130,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final Object discriminatorValue; private final Object discriminatorValue;
private final String discriminatorSQLString; private final String discriminatorSQLString;
private final DiscriminatorType discriminatorType;
private final String explicitDiscriminatorColumnName;
private final String discriminatorAlias;
// Span of the tables directly mapped by this entity and super-classes, if any // Span of the tables directly mapped by this entity and super-classes, if any
private final int coreTableSpan; private final int coreTableSpan;
@ -136,6 +153,45 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// DISCRIMINATOR // DISCRIMINATOR
if ( persistentClass.isPolymorphic() ) { if ( persistentClass.isPolymorphic() ) {
final Value discriminatorMapping = persistentClass.getDiscriminator();
if ( discriminatorMapping != null ) {
log.debug( "Encountered explicit discriminator mapping for joined inheritance" );
final Selectable selectable = discriminatorMapping.getColumnIterator().next();
if ( Formula.class.isInstance( selectable ) ) {
throw new MappingException( "Discriminator formulas on joined inheritance hierarchies not supported at this time" );
}
else {
final Column column = (Column) selectable;
explicitDiscriminatorColumnName = column.getQuotedName( factory.getDialect() );
discriminatorAlias = column.getAlias( factory.getDialect(), persistentClass.getRootTable() );
}
discriminatorType = (DiscriminatorType) persistentClass.getDiscriminator().getType();
if ( persistentClass.isDiscriminatorValueNull() ) {
discriminatorValue = NULL_DISCRIMINATOR;
discriminatorSQLString = InFragment.NULL;
}
else if ( persistentClass.isDiscriminatorValueNotNull() ) {
discriminatorValue = NOT_NULL_DISCRIMINATOR;
discriminatorSQLString = InFragment.NOT_NULL;
}
else {
try {
discriminatorValue = discriminatorType.stringToObject( persistentClass.getDiscriminatorValue() );
discriminatorSQLString = discriminatorType.objectToSQLString( discriminatorValue, factory.getDialect() );
}
catch (ClassCastException cce) {
throw new MappingException("Illegal discriminator type: " + discriminatorType.getName() );
}
catch (Exception e) {
throw new MappingException("Could not format discriminator value to SQL string", e);
}
}
}
else {
explicitDiscriminatorColumnName = null;
discriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;
discriminatorType = StandardBasicTypes.INTEGER;
try { try {
discriminatorValue = persistentClass.getSubclassId(); discriminatorValue = persistentClass.getSubclassId();
discriminatorSQLString = discriminatorValue.toString(); discriminatorSQLString = discriminatorValue.toString();
@ -144,7 +200,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
throw new MappingException( "Could not format discriminator value to SQL string", e ); throw new MappingException( "Could not format discriminator value to SQL string", e );
} }
} }
}
else { else {
explicitDiscriminatorColumnName = null;
discriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;
discriminatorType = StandardBasicTypes.INTEGER;
discriminatorValue = null; discriminatorValue = null;
discriminatorSQLString = null; discriminatorSQLString = null;
} }
@ -477,12 +537,35 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
subclassClosure[k] = sc.getEntityName(); subclassClosure[k] = sc.getEntityName();
try { try {
if ( persistentClass.isPolymorphic() ) { if ( persistentClass.isPolymorphic() ) {
final Object discriminatorValue;
if ( explicitDiscriminatorColumnName != null ) {
if ( sc.isDiscriminatorValueNull() ) {
discriminatorValue = NULL_DISCRIMINATOR;
}
else if ( sc.isDiscriminatorValueNotNull() ) {
discriminatorValue = NOT_NULL_DISCRIMINATOR;
}
else {
try {
discriminatorValue = discriminatorType.stringToObject( sc.getDiscriminatorValue() );
}
catch (ClassCastException cce) {
throw new MappingException( "Illegal discriminator type: " + discriminatorType.getName() );
}
catch (Exception e) {
throw new MappingException( "Could not format discriminator value to SQL string", e);
}
}
}
else {
// we now use subclass ids that are consistent across all // we now use subclass ids that are consistent across all
// persisters for a class hierarchy, so that the use of // persisters for a class hierarchy, so that the use of
// "foo.class = Bar" works in HQL // "foo.class = Bar" works in HQL
Integer subclassId = sc.getSubclassId(); discriminatorValue = sc.getSubclassId();
subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() ); }
discriminatorValues[k] = subclassId.toString();
subclassesByDiscriminatorValue.put( discriminatorValue, sc.getEntityName() );
discriminatorValues[k] = discriminatorValue.toString();
int id = getTableId( int id = getTableId(
sc.getTable().getQualifiedName( sc.getTable().getQualifiedName(
factory.getDialect(), factory.getDialect(),
@ -702,6 +785,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
constraintOrderedKeyColumnNames = null; constraintOrderedKeyColumnNames = null;
discriminatorValue = null; discriminatorValue = null;
discriminatorSQLString = null; discriminatorSQLString = null;
discriminatorType = StandardBasicTypes.INTEGER;
explicitDiscriminatorColumnName = null;
discriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;
coreTableSpan = -1; coreTableSpan = -1;
isNullableTable = null; isNullableTable = null;
subclassNamesBySubclassTable = null; subclassNamesBySubclassTable = null;
@ -729,21 +815,50 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
} }
public Type getDiscriminatorType() { public Type getDiscriminatorType() {
return StandardBasicTypes.INTEGER; return discriminatorType;
} }
public Object getDiscriminatorValue() { public Object getDiscriminatorValue() {
return discriminatorValue; return discriminatorValue;
} }
@Override
public String getDiscriminatorSQLValue() { public String getDiscriminatorSQLValue() {
return discriminatorSQLString; return discriminatorSQLString;
} }
@Override
public String getDiscriminatorColumnName() {
return explicitDiscriminatorColumnName == null
? super.getDiscriminatorColumnName()
: explicitDiscriminatorColumnName;
}
@Override
public String getDiscriminatorColumnReaders() {
return getDiscriminatorColumnName();
}
@Override
public String getDiscriminatorColumnReaderTemplate() {
return getDiscriminatorColumnName();
}
protected String getDiscriminatorAlias() {
return discriminatorAlias;
}
public String getSubclassForDiscriminatorValue(Object value) { public String getSubclassForDiscriminatorValue(Object value) {
return (String) subclassesByDiscriminatorValue.get( value ); return (String) subclassesByDiscriminatorValue.get( value );
} }
@Override
protected void addDiscriminatorToInsert(Insert insert) {
if ( explicitDiscriminatorColumnName != null ) {
insert.addColumn( explicitDiscriminatorColumnName, getDiscriminatorSQLValue() );
}
}
public Serializable[] getPropertySpaces() { public Serializable[] getPropertySpaces() {
return spaces; // don't need subclass tables, because they can't appear in conditions return spaces; // don't need subclass tables, because they can't appear in conditions
} }
@ -858,8 +973,13 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
public void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) { public void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
if ( hasSubclasses() ) { if ( hasSubclasses() ) {
if ( explicitDiscriminatorColumnName == null ) {
select.setExtraSelectList( discriminatorFragment( name ), getDiscriminatorAlias() ); select.setExtraSelectList( discriminatorFragment( name ), getDiscriminatorAlias() );
} }
else {
select.addColumn( name, explicitDiscriminatorColumnName, discriminatorAlias );
}
}
} }
private CaseFragment discriminatorFragment(String alias) { private CaseFragment discriminatorFragment(String alias) {

View File

@ -0,0 +1,115 @@
package org.hibernate.test.joinedsubclass;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import org.junit.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-6911" )
public class JoinedSubclassWithExplicitDiscriminatorTest extends BaseCoreFunctionalTestCase {
@Entity( name = "Animal" )
@Table( name = "animal" )
@Inheritance( strategy = InheritanceType.JOINED )
@DiscriminatorColumn( name = "type", discriminatorType = DiscriminatorType.STRING )
@DiscriminatorValue( value = "???animal???" )
public static abstract class Animal {
@Id
public Integer id;
protected Animal() {
}
protected Animal(Integer id) {
this.id = id;
}
}
@Entity( name = "Cat" )
@DiscriminatorValue( value = "cat" )
public static class Cat extends Animal {
public Cat() {
super();
}
public Cat(Integer id) {
super( id );
}
}
@Entity( name = "Dog" )
@DiscriminatorValue( value = "dog" )
public static class Dog extends Animal {
public Dog() {
super();
}
public Dog(Integer id) {
super( id );
}
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Animal.class, Cat.class, Dog.class };
}
@Test
public void metadataAssertions() {
EntityPersister p = sessionFactory().getEntityPersister( Dog.class.getName() );
assertNotNull( p );
final JoinedSubclassEntityPersister dogPersister = assertTyping( JoinedSubclassEntityPersister.class, p );
assertEquals( "string", dogPersister.getDiscriminatorType().getName() );
assertEquals( "type", dogPersister.getDiscriminatorColumnName() );
assertEquals( "dog", dogPersister.getDiscriminatorValue() );
p = sessionFactory().getEntityPersister( Cat.class.getName() );
assertNotNull( p );
final JoinedSubclassEntityPersister catPersister = assertTyping( JoinedSubclassEntityPersister.class, p );
assertEquals( "string", catPersister.getDiscriminatorType().getName() );
assertEquals( "type", catPersister.getDiscriminatorColumnName() );
assertEquals( "cat", catPersister.getDiscriminatorValue() );
}
@Test
public void basicUsageTest() {
Session session = openSession();
session.beginTransaction();
session.save( new Cat( 1 ) );
session.save( new Dog( 2 ) );
session.getTransaction().commit();
session.close();
session = openSession();
session.beginTransaction();
session.createQuery( "from Animal" ).list();
Cat cat = (Cat) session.get( Cat.class, 1 );
assertNotNull( cat );
session.delete( cat );
Dog dog = (Dog) session.get( Dog.class, 2 );
assertNotNull( dog );
session.delete( dog );
session.getTransaction().commit();
session.close();
}
}

View File

@ -0,0 +1,118 @@
package org.hibernate.test.joinedsubclass;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import org.hibernate.Session;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Ejb3DiscriminatorColumn;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Steve Ebersole
*/
@TestForIssue( jiraKey = "HHH-6911" )
public class JoinedSubclassWithImplicitDiscriminatorTest extends BaseCoreFunctionalTestCase {
@Entity( name = "Animal" )
@Table( name = "animal" )
@Inheritance( strategy = InheritanceType.JOINED )
public static abstract class Animal {
@Id
public Integer id;
protected Animal() {
}
protected Animal(Integer id) {
this.id = id;
}
}
@Entity( name = "Cat" )
public static class Cat extends Animal {
public Cat() {
super();
}
public Cat(Integer id) {
super( id );
}
}
@Entity( name = "Dog" )
public static class Dog extends Animal {
public Dog() {
super();
}
public Dog(Integer id) {
super( id );
}
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { Animal.class, Cat.class, Dog.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
configuration.setProperty( AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS, "true" );
}
@Test
public void metadataAssertions() {
EntityPersister p = sessionFactory().getEntityPersister( Dog.class.getName() );
assertNotNull( p );
final JoinedSubclassEntityPersister dogPersister = assertTyping( JoinedSubclassEntityPersister.class, p );
assertEquals( Ejb3DiscriminatorColumn.DEFAULT_DISCRIMINATOR_TYPE, dogPersister.getDiscriminatorType().getName() );
assertEquals( Ejb3DiscriminatorColumn.DEFAULT_DISCRIMINATOR_COLUMN_NAME, dogPersister.getDiscriminatorColumnName() );
assertEquals( "Dog", dogPersister.getDiscriminatorValue() );
p = sessionFactory().getEntityPersister( Cat.class.getName() );
assertNotNull( p );
final JoinedSubclassEntityPersister catPersister = assertTyping( JoinedSubclassEntityPersister.class, p );
assertEquals( Ejb3DiscriminatorColumn.DEFAULT_DISCRIMINATOR_TYPE, catPersister.getDiscriminatorType().getName() );
assertEquals( Ejb3DiscriminatorColumn.DEFAULT_DISCRIMINATOR_COLUMN_NAME, catPersister.getDiscriminatorColumnName() );
assertEquals( "Cat", catPersister.getDiscriminatorValue() );
}
@Test
public void basicUsageTest() {
Session session = openSession();
session.beginTransaction();
session.save( new Cat( 1 ) );
session.save( new Dog( 2 ) );
session.getTransaction().commit();
session.close();
session = openSession();
session.beginTransaction();
session.createQuery( "from Animal" ).list();
Cat cat = (Cat) session.get( Cat.class, 1 );
assertNotNull( cat );
session.delete( cat );
Dog dog = (Dog) session.get( Dog.class, 2 );
assertNotNull( dog );
session.delete( dog );
session.getTransaction().commit();
session.close();
}
}