HHH-17460 - Ongoing JPA 32 work

This commit is contained in:
Steve Ebersole 2024-03-19 08:32:45 -05:00
parent e086674878
commit 872b834047
6 changed files with 129 additions and 92 deletions

View File

@ -162,6 +162,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -176,6 +178,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -193,6 +197,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -208,6 +214,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -220,6 +228,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -232,6 +242,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -250,6 +262,8 @@ public class ManagedTypeProcessor {
attributeJavaType, attributeJavaType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
@ -257,94 +271,120 @@ public class ManagedTypeProcessor {
} }
final JaxbAttributesContainer attributes = jaxbManagedType.getAttributes(); final JaxbAttributesContainer attributes = jaxbManagedType.getAttributes();
// <basic/>
attributes.getBasicAttributes().forEach( (jaxbBasic) -> { attributes.getBasicAttributes().forEach( (jaxbBasic) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbBasic.getName(), jaxbBasic.getName(),
determineDynamicAttributeJavaType( jaxbBasic, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbBasic, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <embedded/>
attributes.getEmbeddedAttributes().forEach( (jaxbEmbedded) -> { attributes.getEmbeddedAttributes().forEach( (jaxbEmbedded) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbEmbedded.getName(), jaxbEmbedded.getName(),
determineDynamicAttributeJavaType( jaxbEmbedded, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbEmbedded, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <one-to-one/>
attributes.getOneToOneAttributes().forEach( (jaxbOneToOne) -> { attributes.getOneToOneAttributes().forEach( (jaxbOneToOne) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbOneToOne.getName(), jaxbOneToOne.getName(),
determineDynamicAttributeJavaType( jaxbOneToOne, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbOneToOne, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <many-to-one/>
attributes.getManyToOneAttributes().forEach( (jaxbManyToOne) -> { attributes.getManyToOneAttributes().forEach( (jaxbManyToOne) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbManyToOne.getName(), jaxbManyToOne.getName(),
determineDynamicAttributeJavaType( jaxbManyToOne, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbManyToOne, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <any/>
attributes.getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> { attributes.getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbAnyMapping.getName(), jaxbAnyMapping.getName(),
determineDynamicAttributeJavaType( jaxbAnyMapping, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbAnyMapping, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
false,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <element-collection/>
attributes.getElementCollectionAttributes().forEach( (jaxbElementCollection) -> { attributes.getElementCollectionAttributes().forEach( (jaxbElementCollection) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbElementCollection.getName(), jaxbElementCollection.getName(),
determineDynamicAttributeJavaType( jaxbElementCollection, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbElementCollection, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
true,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <one-to-many/>
attributes.getOneToManyAttributes().forEach( (jaxbOneToMany) -> { attributes.getOneToManyAttributes().forEach( (jaxbOneToMany) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbOneToMany.getName(), jaxbOneToMany.getName(),
determineDynamicAttributeJavaType( jaxbOneToMany, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbOneToMany, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
true,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <many-to-many/>
attributes.getManyToManyAttributes().forEach( (jaxbManyToMany) -> { attributes.getManyToManyAttributes().forEach( (jaxbManyToMany) -> {
final DynamicFieldDetails member = new DynamicFieldDetails( final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbManyToMany.getName(), jaxbManyToMany.getName(),
determineDynamicAttributeJavaType( jaxbManyToMany, xmlDocumentContext ), determineDynamicAttributeJavaType( jaxbManyToMany, xmlDocumentContext ),
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
true,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );
} ); } );
// <many-to-any/>
attributes.getPluralAnyMappingAttributes().forEach( (jaxbPluralAnyMapping) -> { attributes.getPluralAnyMappingAttributes().forEach( (jaxbPluralAnyMapping) -> {
final TypeDetails attributeType = determineDynamicAttributeJavaType( final TypeDetails attributeType = determineDynamicAttributeJavaType(
jaxbPluralAnyMapping, jaxbPluralAnyMapping,
@ -355,6 +395,8 @@ public class ManagedTypeProcessor {
attributeType, attributeType,
classDetails, classDetails,
MEMBER_MODIFIERS, MEMBER_MODIFIERS,
false,
true,
xmlDocumentContext.getModelBuildingContext() xmlDocumentContext.getModelBuildingContext()
); );
classDetails.addField( member ); classDetails.addField( member );

View File

@ -87,9 +87,9 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbUuidGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbCheckable; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbCheckable;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbColumnJoined; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbColumnJoined;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbTableMapping; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbTableMapping;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.categorize.spi.JpaEventListener; import org.hibernate.boot.models.categorize.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle; import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.internal.AnnotationUsageHelper;
import org.hibernate.boot.models.xml.internal.db.ColumnProcessing; import org.hibernate.boot.models.xml.internal.db.ColumnProcessing;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext; import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
@ -153,6 +153,7 @@ import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import jakarta.persistence.UniqueConstraint; import jakarta.persistence.UniqueConstraint;
import static java.lang.Boolean.FALSE;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hibernate.boot.models.xml.internal.XmlProcessingHelper.makeNestedAnnotation; import static org.hibernate.boot.models.xml.internal.XmlProcessingHelper.makeNestedAnnotation;
@ -1595,80 +1596,95 @@ public class XmlAnnotationHelper {
} }
static void applyDiscriminatorColumn( static void applyDiscriminatorColumn(
JaxbDiscriminatorColumnImpl discriminatorColumn, JaxbDiscriminatorColumnImpl jaxbDiscriminatorColumn,
MutableClassDetails target, MutableClassDetails target,
XmlDocumentContext xmlDocumentContext) { XmlDocumentContext xmlDocumentContext) {
if ( discriminatorColumn != null ) { if ( jaxbDiscriminatorColumn == null ) {
return;
}
final MutableAnnotationUsage<DiscriminatorColumn> discriminatorColumnAnn = XmlProcessingHelper final MutableAnnotationUsage<DiscriminatorColumn> discriminatorColumnAnn = XmlProcessingHelper
.getOrMakeAnnotation( DiscriminatorColumn.class, target, xmlDocumentContext ); .getOrMakeAnnotation( DiscriminatorColumn.class, target, xmlDocumentContext );
final AnnotationDescriptor<DiscriminatorColumn> discriminatorColumnDescriptor = xmlDocumentContext final AnnotationDescriptor<DiscriminatorColumn> discriminatorColumnDescriptor = xmlDocumentContext
.getModelBuildingContext() .getModelBuildingContext()
.getAnnotationDescriptorRegistry() .getAnnotationDescriptorRegistry()
.getDescriptor( DiscriminatorColumn.class ); .getDescriptor( DiscriminatorColumn.class );
applyOr( AnnotationUsageHelper.applyStringAttributeIfSpecified(
discriminatorColumn,
JaxbDiscriminatorColumnImpl::getName,
"name", "name",
discriminatorColumnAnn, jaxbDiscriminatorColumn.getName(),
discriminatorColumnDescriptor discriminatorColumnAnn
);
AnnotationUsageHelper.applyAttributeIfSpecified(
"discriminatorType",
jaxbDiscriminatorColumn.getDiscriminatorType(),
discriminatorColumnAnn
); );
applyOr( applyOr(
discriminatorColumn, jaxbDiscriminatorColumn,
JaxbDiscriminatorColumnImpl::getDiscriminatorType,
"discriminatorType",
discriminatorColumnAnn,
discriminatorColumnDescriptor
);
applyOr(
discriminatorColumn,
JaxbDiscriminatorColumnImpl::getColumnDefinition, JaxbDiscriminatorColumnImpl::getColumnDefinition,
"columnDefinition", "columnDefinition",
discriminatorColumnAnn, discriminatorColumnAnn,
discriminatorColumnDescriptor discriminatorColumnDescriptor
); );
applyOr( applyOr(
discriminatorColumn, jaxbDiscriminatorColumn,
JaxbDiscriminatorColumnImpl::getOptions, JaxbDiscriminatorColumnImpl::getOptions,
"options", "options",
discriminatorColumnAnn, discriminatorColumnAnn,
discriminatorColumnDescriptor discriminatorColumnDescriptor
); );
applyOr( applyOr(
discriminatorColumn, jaxbDiscriminatorColumn,
JaxbDiscriminatorColumnImpl::getLength, JaxbDiscriminatorColumnImpl::getLength,
"length", "length",
discriminatorColumnAnn, discriminatorColumnAnn,
discriminatorColumnDescriptor discriminatorColumnDescriptor
); );
// todo : add force-selection attribute to @DiscriminatorColumn if ( jaxbDiscriminatorColumn.isForceSelection() || jaxbDiscriminatorColumn.isInsertable() == FALSE ) {
} final MutableAnnotationUsage<DiscriminatorOptions> optionsAnn = XmlProcessingHelper.getOrMakeAnnotation(
DiscriminatorOptions.class,
target,
xmlDocumentContext
);
optionsAnn.setAttributeValue( "force", true );
AnnotationUsageHelper.applyAttributeIfSpecified(
"insert",
jaxbDiscriminatorColumn.isInsertable(),
discriminatorColumnAnn
);
}
} }
public static void applyDiscriminatorFormula( public static void applyDiscriminatorFormula(
JaxbDiscriminatorFormulaImpl jaxbDiscriminatorFormula, JaxbDiscriminatorFormulaImpl jaxbDiscriminatorFormula,
MutableClassDetails target, MutableClassDetails target,
XmlDocumentContext xmlDocumentContext) { XmlDocumentContext xmlDocumentContext) {
if ( jaxbDiscriminatorFormula == null || StringHelper.isEmpty( jaxbDiscriminatorFormula.getFragment() ) ) { if ( jaxbDiscriminatorFormula == null ) {
return;
}
if ( StringHelper.isEmpty( jaxbDiscriminatorFormula.getFragment() ) ) {
return; return;
} }
final MutableAnnotationUsage<DiscriminatorFormula> discriminatorFormulaAnn = HibernateAnnotations.DISCRIMINATOR_FORMULA.createUsage( final MutableAnnotationUsage<DiscriminatorFormula> discriminatorFormulaAnn = XmlProcessingHelper.getOrMakeAnnotation( DiscriminatorFormula.class, target, xmlDocumentContext );
target,
xmlDocumentContext.getModelBuildingContext()
);
discriminatorFormulaAnn.setAttributeValue( "value", jaxbDiscriminatorFormula.getFragment() ); discriminatorFormulaAnn.setAttributeValue( "value", jaxbDiscriminatorFormula.getFragment() );
XmlProcessingHelper.applyAttributeIfSpecified( "discriminatorType", jaxbDiscriminatorFormula.getDiscriminatorType(), discriminatorFormulaAnn ); XmlProcessingHelper.applyAttributeIfSpecified( "discriminatorType", jaxbDiscriminatorFormula.getDiscriminatorType(), discriminatorFormulaAnn );
if ( jaxbDiscriminatorFormula.isForceSelection() ) { if ( jaxbDiscriminatorFormula.isForceSelection() ) {
final MutableAnnotationUsage<DiscriminatorOptions> existingOptionsAnnotation = (MutableAnnotationUsage<DiscriminatorOptions>) target.getAnnotationUsage( DiscriminatorOptions.class ); final MutableAnnotationUsage<DiscriminatorOptions> optionsAnn = XmlProcessingHelper.getOrMakeAnnotation(
final MutableAnnotationUsage<DiscriminatorOptions> optionsAnnotation = existingOptionsAnnotation != null DiscriminatorOptions.class,
? existingOptionsAnnotation target,
: HibernateAnnotations.DISCRIMINATOR_OPTIONS.createUsage( target, xmlDocumentContext.getModelBuildingContext() ); xmlDocumentContext
optionsAnnotation.setAttributeValue( "force", true ); );
optionsAnn.setAttributeValue( "force", true );
} }
} }
} }

View File

@ -12,6 +12,7 @@ import java.lang.annotation.Annotation;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbManagedType; import org.hibernate.boot.jaxb.mapping.spi.JaxbManagedType;
import org.hibernate.boot.models.MemberResolutionException; import org.hibernate.boot.models.MemberResolutionException;
import org.hibernate.boot.models.internal.AnnotationUsageHelper;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext; import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.internal.dynamic.DynamicAnnotationUsage; import org.hibernate.models.internal.dynamic.DynamicAnnotationUsage;
@ -152,13 +153,7 @@ public class XmlProcessingHelper {
public static <A extends Annotation> MutableAnnotationUsage<A> makeNestedAnnotation( public static <A extends Annotation> MutableAnnotationUsage<A> makeNestedAnnotation(
Class<A> annotationType, Class<A> annotationType,
XmlDocumentContext xmlDocumentContext) { XmlDocumentContext xmlDocumentContext) {
return new DynamicAnnotationUsage<>( return makeNestedAnnotation( annotationType, null, xmlDocumentContext );
xmlDocumentContext.getModelBuildingContext()
.getAnnotationDescriptorRegistry()
.getDescriptor( annotationType ),
null,
xmlDocumentContext.getModelBuildingContext()
);
} }
/** /**
@ -178,8 +173,7 @@ public class XmlProcessingHelper {
public static <A extends Annotation> MutableAnnotationUsage<A> makeAnnotation( public static <A extends Annotation> MutableAnnotationUsage<A> makeAnnotation(
Class<A> annotationType, Class<A> annotationType,
XmlDocumentContext xmlDocumentContext) { XmlDocumentContext xmlDocumentContext) {
final MutableAnnotationUsage<A> created = makeNestedAnnotation( annotationType, xmlDocumentContext ); return makeNestedAnnotation( annotationType, xmlDocumentContext );
return created;
} }
/** /**
@ -233,36 +227,17 @@ public class XmlProcessingHelper {
return created; return created;
} }
public static <A extends Annotation> void setIf(
Object value,
String attributeName,
MutableAnnotationUsage<A> annotationUsage) {
if ( value == null ) {
return;
}
if ( value instanceof String && ( (String) value ).isBlank() ) {
return;
}
annotationUsage.setAttributeValue( attributeName, value );
}
public static <A extends Annotation> void applyAttributeIfSpecified( public static <A extends Annotation> void applyAttributeIfSpecified(
String attributeName, String attributeName,
String value, String value,
MutableAnnotationUsage<A> annotationUsage) { MutableAnnotationUsage<A> annotationUsage) {
if ( StringHelper.isNotEmpty( value ) ) { AnnotationUsageHelper.applyStringAttributeIfSpecified( attributeName, value, annotationUsage );
annotationUsage.setAttributeValue( attributeName, value );
}
} }
public static <A extends Annotation> void applyAttributeIfSpecified( public static <A extends Annotation> void applyAttributeIfSpecified(
String attributeName, String attributeName,
Object value, Object value,
MutableAnnotationUsage<A> tableAnn) { MutableAnnotationUsage<A> annotationUsage) {
if ( value != null ) { AnnotationUsageHelper.applyAttributeIfSpecified( attributeName, value, annotationUsage );
tableAnn.setAttributeValue( attributeName, value );
}
} }
} }

View File

@ -928,7 +928,9 @@
} }
</xsd:documentation> </xsd:documentation>
</xsd:annotation> </xsd:annotation>
<xsd:attribute name="fragment" type="xsd:string"/> <xsd:sequence>
<xsd:element name="fragment" type="xsd:string"/>
</xsd:sequence>
<xsd:attribute name="discriminator-type" type="orm:discriminator-type"/> <xsd:attribute name="discriminator-type" type="orm:discriminator-type"/>
<xsd:attribute name="force-selection" type="xsd:boolean" default="false"/> <xsd:attribute name="force-selection" type="xsd:boolean" default="false"/>
</xsd:complexType> </xsd:complexType>

View File

@ -47,7 +47,7 @@ public class NamedEntityGraphTest {
); );
final Set<EntityHierarchy> entityHierarchies = categorizedDomainModel.getEntityHierarchies(); final Set<EntityHierarchy> entityHierarchies = categorizedDomainModel.getEntityHierarchies();
assertThat( entityHierarchies ).hasSize( 1 ); assertThat( entityHierarchies ).hasSize( 2 );
entityHierarchies.forEach( entityHierarchies.forEach(
entityHierarchy -> { entityHierarchy -> {

View File

@ -35,7 +35,9 @@
</entity> </entity>
<entity class="org.hibernate.orm.test.boot.models.xml.SimpleEntity" metadata-complete="true" access="FIELD"> <entity class="org.hibernate.orm.test.boot.models.xml.SimpleEntity" metadata-complete="true" access="FIELD">
<discriminator-formula>CASE WHEN VALUE1 IS NOT NULL THEN 1 WHEN VALUE2 IS NOT NULL THEN 2 END</discriminator-formula> <discriminator-formula>
<fragment>CASE WHEN VALUE1 IS NOT NULL THEN 1 WHEN VALUE2 IS NOT NULL THEN 2 END</fragment>
</discriminator-formula>
<attributes> <attributes>
<id name="id"/> <id name="id"/>
</attributes> </attributes>