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.CollectionTable;
import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection;
@ -97,6 +98,7 @@ import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchProfile;
@ -635,12 +637,27 @@ public final class AnnotationBinder {
Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns(
clazzToProcess, mappings, inheritanceState, superEntity
);
Ejb3DiscriminatorColumn discriminatorColumn = null;
final Ejb3DiscriminatorColumn discriminatorColumn;
if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
discriminatorColumn = processDiscriminatorProperties(
clazzToProcess, mappings, inheritanceState, entityBinder
discriminatorColumn = processSingleTableDiscriminatorProperties(
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.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
@ -685,46 +702,71 @@ public final class AnnotationBinder {
OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class );
boolean onDeleteAppropriate = false;
if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) && inheritanceState.hasParents() ) {
onDeleteAppropriate = true;
final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass;
SimpleValue key = new DependantValue( mappings, jsc.getTable(), jsc.getIdentifier() );
jsc.setKey( key );
ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
if ( fk != null && !BinderHelper.isEmptyAnnotationValue( fk.name() ) ) {
key.setForeignKeyName( fk.name() );
}
if ( onDeleteAnn != null ) {
key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
}
else {
key.setCascadeDeleteEnabled( false );
}
//we are never in a second pass at that stage, so queue it
SecondPass sp = new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, mappings );
mappings.addSecondPass( sp );
mappings.addSecondPass( new CreateKeySecondPass( jsc ) );
// 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;
final JoinedSubclass jsc = ( JoinedSubclass ) persistentClass;
SimpleValue key = new DependantValue( mappings, jsc.getTable(), jsc.getIdentifier() );
jsc.setKey( key );
ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
if ( fk != null && !BinderHelper.isEmptyAnnotationValue( fk.name() ) ) {
key.setForeignKeyName( fk.name() );
}
if ( onDeleteAnn != null ) {
key.setCascadeDeleteEnabled( OnDeleteAction.CASCADE.equals( onDeleteAnn.action() ) );
}
else {
key.setCascadeDeleteEnabled( false );
}
//we are never in a second pass at that stage, so queue it
SecondPass sp = new JoinedSubclassFkSecondPass( jsc, inheritanceJoinedColumns, key, mappings );
mappings.addSecondPass( sp );
mappings.addSecondPass( new CreateKeySecondPass( jsc ) );
}
if ( isInheritanceRoot ) {
// the class we are processing is the root of the hierarchy, see if we had a discriminator column
// (it is perfectly valid for joined subclasses to not have discriminators).
if ( discriminatorColumn != null ) {
// we have a discriminator column
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn,
entityBinder.getSecondaryTables(),
propertyHolder,
mappings
);
//bind it again since the type might have changed
entityBinder.bindDiscriminatorValue();
}
}
}
}
else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
if ( ! inheritanceState.hasParents() ) {
if ( inheritanceState.hasSiblings() || !discriminatorColumn.isImplicit() ) {
//need a discriminator column
bindDiscriminatorToPersistentClass(
( RootClass ) persistentClass,
if ( isInheritanceRoot ) {
if ( hasSubclasses || !discriminatorColumn.isImplicit() ) {
bindDiscriminatorColumnToRootPersistentClass(
(RootClass) persistentClass,
discriminatorColumn,
entityBinder.getSecondaryTables(),
propertyHolder,
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());
}
if (onDeleteAnn != null && !onDeleteAppropriate) LOG.invalidOnDeleteAnnotation(propertyHolder.getEntityName());
// try to find class level generators
HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
@ -782,32 +824,43 @@ public final class AnnotationBinder {
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;
javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation(
javax.persistence.DiscriminatorColumn.class
);
DiscriminatorType discriminatorType = discAnn != null ?
discAnn.discriminatorType() :
DiscriminatorType.STRING;
DiscriminatorType discriminatorType = discAnn != null
? discAnn.discriminatorType()
: DiscriminatorType.STRING;
org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.DiscriminatorFormula.class
);
if ( !inheritanceState.hasParents() ) {
if ( isRoot ) {
discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType, discAnn, discFormulaAnn, mappings
discriminatorType,
discAnn,
discFormulaAnn,
mappings
);
}
if ( discAnn != null && inheritanceState.hasParents() ) {
if ( discAnn != null && !isRoot ) {
LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() );
}
String discrimValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class ) ?
clazzToProcess.getAnnotation( DiscriminatorValue.class ).value() :
null;
entityBinder.setDiscriminatorValue( discrimValue );
final String discriminatorValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class )
? clazzToProcess.getAnnotation( DiscriminatorValue.class ).value()
: null;
entityBinder.setDiscriminatorValue( discriminatorValue );
DiscriminatorOptions discriminatorOptions = clazzToProcess.getAnnotation( DiscriminatorOptions.class );
if ( discriminatorOptions != null) {
@ -818,6 +871,53 @@ public final class AnnotationBinder {
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(
Map<XClass, InheritanceState> inheritanceStatePerClass,
Mappings mappings,
@ -1375,7 +1475,7 @@ public final class AnnotationBinder {
}
private static void bindDiscriminatorToPersistentClass(
private static void bindDiscriminatorColumnToRootPersistentClass(
RootClass rootClass,
Ejb3DiscriminatorColumn discriminatorColumn,
Map<String, Join> secondaryTables,
@ -1387,10 +1487,10 @@ public final class AnnotationBinder {
}
discriminatorColumn.setJoins( secondaryTables );
discriminatorColumn.setPropertyHolder( propertyHolder );
SimpleValue discrim = new SimpleValue( mappings, rootClass.getTable() );
rootClass.setDiscriminator( discrim );
discriminatorColumn.linkWithValue( discrim );
discrim.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
SimpleValue discriminatorColumnBinding = new SimpleValue( mappings, rootClass.getTable() );
rootClass.setDiscriminator( discriminatorColumnBinding );
discriminatorColumn.linkWithValue( discriminatorColumnBinding );
discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
rootClass.setPolymorphic( true );
if ( LOG.isTraceEnabled() ) {
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";
/**
* 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 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 AUTO_SESSION_EVENTS_LISTENER = "hibernate.session.events.auto";
}

View File

@ -3375,6 +3375,7 @@ public class Configuration implements Serializable {
}
private Boolean useNewGeneratorMappings;
@Override
public boolean useNewGeneratorMappings() {
if ( useNewGeneratorMappings == null ) {
@ -3385,6 +3386,20 @@ public class Configuration implements Serializable {
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;
@Override

View File

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

View File

@ -797,6 +797,17 @@ public interface Mappings {
*/
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
* {@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.internal.DynamicFilterAliasGenerator;
import org.hibernate.internal.FilterAliasGenerator;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.MappedSuperclass;
@ -53,11 +55,16 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert;
import org.hibernate.sql.SelectFragment;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.hibernate.type.*;
import org.hibernate.type.DiscriminatorType;
import org.jboss.logging.Logger;
/**
* An <tt>EntityPersister</tt> implementing the normalized "table-per-subclass"
@ -66,6 +73,13 @@ import org.hibernate.type.Type;
* @author Gavin King
*/
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
private final int tableSpan;
@ -116,6 +130,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
private final Object discriminatorValue;
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
private final int coreTableSpan;
@ -136,15 +153,58 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// DISCRIMINATOR
if ( persistentClass.isPolymorphic() ) {
try {
discriminatorValue = persistentClass.getSubclassId();
discriminatorSQLString = discriminatorValue.toString();
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);
}
}
}
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 {
discriminatorValue = persistentClass.getSubclassId();
discriminatorSQLString = discriminatorValue.toString();
}
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;
discriminatorValue = null;
discriminatorSQLString = null;
}
@ -477,12 +537,35 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
subclassClosure[k] = sc.getEntityName();
try {
if ( persistentClass.isPolymorphic() ) {
// we now use subclass ids that are consistent across all
// persisters for a class hierarchy, so that the use of
// "foo.class = Bar" works in HQL
Integer subclassId = sc.getSubclassId();
subclassesByDiscriminatorValue.put( subclassId, sc.getEntityName() );
discriminatorValues[k] = subclassId.toString();
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
// persisters for a class hierarchy, so that the use of
// "foo.class = Bar" works in HQL
discriminatorValue = sc.getSubclassId();
}
subclassesByDiscriminatorValue.put( discriminatorValue, sc.getEntityName() );
discriminatorValues[k] = discriminatorValue.toString();
int id = getTableId(
sc.getTable().getQualifiedName(
factory.getDialect(),
@ -702,6 +785,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
constraintOrderedKeyColumnNames = null;
discriminatorValue = null;
discriminatorSQLString = null;
discriminatorType = StandardBasicTypes.INTEGER;
explicitDiscriminatorColumnName = null;
discriminatorAlias = IMPLICIT_DISCRIMINATOR_ALIAS;
coreTableSpan = -1;
isNullableTable = null;
subclassNamesBySubclassTable = null;
@ -729,21 +815,50 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
}
public Type getDiscriminatorType() {
return StandardBasicTypes.INTEGER;
return discriminatorType;
}
public Object getDiscriminatorValue() {
return discriminatorValue;
}
@Override
public String getDiscriminatorSQLValue() {
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) {
return (String) subclassesByDiscriminatorValue.get( value );
}
@Override
protected void addDiscriminatorToInsert(Insert insert) {
if ( explicitDiscriminatorColumnName != null ) {
insert.addColumn( explicitDiscriminatorColumnName, getDiscriminatorSQLValue() );
}
}
public Serializable[] getPropertySpaces() {
return spaces; // don't need subclass tables, because they can't appear in conditions
}
@ -858,7 +973,12 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
public void addDiscriminatorToSelect(SelectFragment select, String name, String suffix) {
if ( hasSubclasses() ) {
select.setExtraSelectList( discriminatorFragment( name ), getDiscriminatorAlias() );
if ( explicitDiscriminatorColumnName == null ) {
select.setExtraSelectList( discriminatorFragment( name ), getDiscriminatorAlias() );
}
else {
select.addColumn( name, explicitDiscriminatorColumnName, discriminatorAlias );
}
}
}

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();
}
}