diff --git a/documentation/src/test/java/org/hibernate/userguide/collections/BasicTypeCollectionTest.java b/documentation/src/test/java/org/hibernate/userguide/collections/BasicTypeCollectionTest.java index 8def4aedf7..edf53fcb33 100644 --- a/documentation/src/test/java/org/hibernate/userguide/collections/BasicTypeCollectionTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/collections/BasicTypeCollectionTest.java @@ -51,12 +51,11 @@ public class BasicTypeCollectionTest extends BaseCoreFunctionalTestCase { } @Override - protected Configuration constructAndConfigureConfiguration() { - Configuration configuration = super.constructAndConfigureConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.registerTypeContributor( (typeContributions, serviceRegistry) -> { typeContributions.contributeType( new CommaDelimitedStringsType() ); } ); - return configuration; } //tag::collections-comma-delimited-collection-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetTypeTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetTypeTest.java index d7e4264633..baa87e38aa 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetTypeTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetTypeTest.java @@ -32,14 +32,13 @@ public class BitSetTypeTest extends BaseCoreFunctionalTestCase { } @Override - protected Configuration constructAndConfigureConfiguration() { - Configuration configuration = super.constructAndConfigureConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); //tag::basic-custom-type-register-BasicType-example[] configuration.registerTypeContributor( (typeContributions, serviceRegistry) -> { typeContributions.contributeType( BitSetType.INSTANCE ); } ); //end::basic-custom-type-register-BasicType-example[] - return configuration; } @Test diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java index a0c7fd7293..6a7a7ffb52 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BitSetUserTypeTest.java @@ -36,14 +36,13 @@ public class BitSetUserTypeTest extends BaseCoreFunctionalTestCase { } @Override - protected Configuration constructAndConfigureConfiguration() { - Configuration configuration = super.constructAndConfigureConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); //tag::basic-custom-type-register-UserType-example[] configuration.registerTypeContributor( (typeContributions, serviceRegistry) -> { typeContributions.contributeType( BitSetUserType.INSTANCE, "bitset"); } ); //end::basic-custom-type-register-UserType-example[] - return configuration; } @Test diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 0db0d6a2c7..0ce940a676 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -202,6 +202,11 @@ xjc { xjcBinding = file( 'src/main/xjb/hbm-mapping-bindings.xjb' ) xjcExtensions = ['inheritance', 'simplify'] } + mapping { + xsd = file( 'src/main/resources/org/hibernate/jpa/orm_2_2.xsd' ) + xjcBinding = file( 'src/main/xjb/mapping-bindings.xjb' ) + xjcExtensions = ['inheritance'] + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java index 731ef6b296..801f735a34 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java @@ -388,6 +388,18 @@ public class MetadataSources implements Serializable { return this; } + /** + * Add XML mapping bindings created from an arbitrary source by the {@link #getXmlMappingBinderAccess() binder}. + * + * @param binding The binding. + * + * @return this (for method chaining purposes) + */ + public MetadataSources addXmlBinding(Binding binding) { + getXmlBindingsForWrite().add( binding ); + return this; + } + /** * See {@link #addCacheableFile(java.io.File)} for description * diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java index 71c76ed191..169583db9f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java @@ -32,6 +32,7 @@ import org.hibernate.boot.spi.ClassLoaderAccess; import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenMetadataProvider; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.jpa.internal.MutableJpaComplianceImpl; import org.hibernate.jpa.spi.MutableJpaCompliance; @@ -319,7 +320,13 @@ public class BootstrapContextImpl implements BootstrapContext { private JavaReflectionManager generateHcannReflectionManager() { final JavaReflectionManager reflectionManager = new JavaReflectionManager(); - reflectionManager.setMetadataProvider( new JPAMetadataProvider( this ) ); + if ( metadataBuildingOptions.getXmlMappingOptions().isPreferJaxb() ) { + reflectionManager.setMetadataProvider( new JPAXMLOverriddenMetadataProvider( this ) ); + } + else { + // Legacy implementation based on DOM4J, for backwards compatibility. + reflectionManager.setMetadataProvider( new JPAMetadataProvider( this ) ); + } return reflectionManager; } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java index 92045e888d..a5de31a8ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java @@ -28,6 +28,7 @@ import org.hibernate.boot.archive.spi.ArchiveDescriptorFactory; import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService; import org.hibernate.boot.cfgxml.spi.LoadedConfig; import org.hibernate.boot.cfgxml.spi.MappingReference; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.IdGeneratorStrategyInterpreter; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributor; @@ -554,7 +555,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl(); private String schemaCharset; - private boolean xmlMappingEnabled; + private final XmlMappingOptions xmlMappingOptions; public MetadataBuildingOptionsImpl(StandardServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; @@ -566,11 +567,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont this.multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configService.getSettings() ); - this.xmlMappingEnabled = configService.getSetting( - AvailableSettings.XML_MAPPING_ENABLED, - StandardConverters.BOOLEAN, - true - ); + xmlMappingOptions = XmlMappingOptions.get( serviceRegistry ); this.implicitDiscriminatorsForJoinedInheritanceSupported = configService.getSetting( AvailableSettings.IMPLICIT_DISCRIMINATOR_COLUMNS_FOR_JOINED_SUBCLASS, @@ -843,8 +840,8 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont } @Override - public boolean isXmlMappingEnabled() { - return xmlMappingEnabled; + public XmlMappingOptions getXmlMappingOptions() { + return xmlMappingOptions; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/DefaultXmlMappingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/DefaultXmlMappingOptions.java new file mode 100644 index 0000000000..9812b21fc9 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/DefaultXmlMappingOptions.java @@ -0,0 +1,12 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.jaxb.internal; + +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; + +public class DefaultXmlMappingOptions implements XmlMappingOptions { +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/MappingBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/MappingBinder.java index 2129768643..ee566e98e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/MappingBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/MappingBinder.java @@ -20,7 +20,9 @@ import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping; import org.hibernate.boot.jaxb.internal.stax.HbmEventReader; import org.hibernate.boot.jaxb.internal.stax.JpaOrmXmlEventReader; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.xsd.MappingXsdSupport; import org.hibernate.internal.util.config.ConfigurationException; @@ -39,15 +41,14 @@ public class MappingBinder extends AbstractBinder { private final XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance(); + private final XmlMappingOptions options; + private JAXBContext hbmJaxbContext; + private JAXBContext entityMappingsJaxbContext; - @SuppressWarnings("unused") - public MappingBinder(ClassLoaderService classLoaderService) { - this( classLoaderService, true ); - } - - public MappingBinder(ClassLoaderService classLoaderService, boolean validateXml) { + public MappingBinder(ClassLoaderService classLoaderService, boolean validateXml, XmlMappingOptions options) { super( classLoaderService, validateXml ); + this.options = options; } @Override @@ -66,12 +67,20 @@ public class MappingBinder extends AbstractBinder { return new Binding<>( hbmBindings, origin ); } else { -// final XMLEventReader reader = new JpaOrmXmlEventReader( staxEventReader ); -// return jaxb( reader, LocalSchema.MAPPING.getSchema(), JaxbEntityMappings.class, origin ); - try { - final XMLEventReader reader = new JpaOrmXmlEventReader( staxEventReader, xmlEventFactory ); - return new Binding<>( toDom4jDocument( reader, origin ), origin ); + if ( options.isPreferJaxb() ) { + log.debugf( "Performing JAXB binding of orm.xml document : %s", origin.toString() ); + + XMLEventReader reader = new JpaOrmXmlEventReader( staxEventReader, xmlEventFactory ); + JaxbEntityMappings bindingRoot = jaxb( reader, MappingXsdSupport.INSTANCE.latestJpaDescriptor().getSchema(), entityMappingsJaxbContext(), origin ); + return new Binding<>( bindingRoot, origin ); + } + else { + log.debugf( "Performing DOM4J binding of orm.xml document : %s", origin.toString() ); + + final XMLEventReader reader = new JpaOrmXmlEventReader( staxEventReader, xmlEventFactory ); + return new Binding<>( toDom4jDocument( reader, origin ), origin ); + } } catch (JpaOrmXmlEventReader.BadVersionException e) { throw new UnsupportedOrmXsdVersionException( e.getRequestedVersion(), origin ); @@ -91,6 +100,18 @@ public class MappingBinder extends AbstractBinder { return hbmJaxbContext; } + private JAXBContext entityMappingsJaxbContext() { + if ( entityMappingsJaxbContext == null ) { + try { + entityMappingsJaxbContext = JAXBContext.newInstance( JaxbEntityMappings.class ); + } + catch ( JAXBException e ) { + throw new ConfigurationException( "Unable to build orm.xml JAXBContext", e ); + } + } + return entityMappingsJaxbContext; + } + private Document toDom4jDocument(XMLEventReader jpaOrmXmlEventReader, Origin origin) { // todo : do we need to build a DocumentFactory instance for use here? // historically we did that to set TCCL since, iirc, dom4j uses TCCL diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/AccessTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/AccessTypeMarshalling.java new file mode 100644 index 0000000000..6d413c8024 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/AccessTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.AccessType; + +/** + * Marshalling support for dealing with JPA AccessType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class AccessTypeMarshalling { + public static AccessType fromXml(String name) { + return AccessType.valueOf( name ); + } + + public static String toXml(AccessType accessType) { + return accessType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ConstraintModeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ConstraintModeMarshalling.java new file mode 100644 index 0000000000..48049d56a7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ConstraintModeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.ConstraintMode; + +/** + * Marshalling support for dealing with JPA ConstraintMode enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class ConstraintModeMarshalling { + public static ConstraintMode fromXml(String name) { + return ConstraintMode.valueOf( name ); + } + + public static String toXml(ConstraintMode accessType) { + return accessType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/DiscriminatorTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/DiscriminatorTypeMarshalling.java new file mode 100644 index 0000000000..06ece7dff1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/DiscriminatorTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.DiscriminatorType; + +/** + * Marshalling support for dealing with JPA DiscriminatorType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class DiscriminatorTypeMarshalling { + public static DiscriminatorType fromXml(String name) { + return DiscriminatorType.valueOf( name ); + } + + public static String toXml(DiscriminatorType discriminatorType) { + return discriminatorType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/EnumTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/EnumTypeMarshalling.java new file mode 100644 index 0000000000..9164ad6e6f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/EnumTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.EnumType; + +/** + * Marshalling support for dealing with JPA EnumType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class EnumTypeMarshalling { + public static EnumType fromXml(String name) { + return EnumType.valueOf( name ); + } + + public static String toXml(EnumType enumType) { + return enumType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/FetchTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/FetchTypeMarshalling.java new file mode 100644 index 0000000000..537c76377d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/FetchTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.FetchType; + +/** + * Marshalling support for dealing with JPA FetchType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class FetchTypeMarshalling { + public static FetchType fromXml(String name) { + return FetchType.valueOf( name ); + } + + public static String toXml(FetchType fetchType) { + return fetchType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/GenerationTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/GenerationTypeMarshalling.java new file mode 100644 index 0000000000..04e815d939 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/GenerationTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.GenerationType; + +/** + * Marshalling support for dealing with JPA GenerationType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class GenerationTypeMarshalling { + public static GenerationType fromXml(String name) { + return GenerationType.valueOf( name ); + } + + public static String toXml(GenerationType accessType) { + return accessType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/InheritanceTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/InheritanceTypeMarshalling.java new file mode 100644 index 0000000000..93668921b7 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/InheritanceTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.InheritanceType; + +/** + * Marshalling support for dealing with JPA InheritanceType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class InheritanceTypeMarshalling { + public static InheritanceType fromXml(String name) { + return InheritanceType.valueOf( name ); + } + + public static String toXml(InheritanceType accessType) { + return accessType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/LockModeTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/LockModeTypeMarshalling.java new file mode 100644 index 0000000000..fb975cc756 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/LockModeTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.LockModeType; + +/** + * Marshalling support for dealing with JPA LockModeType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class LockModeTypeMarshalling { + public static LockModeType fromXml(String name) { + return LockModeType.valueOf( name ); + } + + public static String toXml(LockModeType lockModeType) { + return lockModeType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ParameterModeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ParameterModeMarshalling.java new file mode 100644 index 0000000000..a481933400 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ParameterModeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.ParameterMode; + +/** + * Marshalling support for dealing with JPA ParameterMode enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class ParameterModeMarshalling { + public static ParameterMode fromXml(String name) { + return ParameterMode.valueOf( name ); + } + + public static String toXml(ParameterMode parameterMode) { + return parameterMode.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/TemporalTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/TemporalTypeMarshalling.java new file mode 100644 index 0000000000..09ace499db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/TemporalTypeMarshalling.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.internal; + +import javax.persistence.TemporalType; + +/** + * Marshalling support for dealing with JPA TemporalType enums. Plugged into JAXB for binding + * + * @author Steve Ebersole + */ +public class TemporalTypeMarshalling { + public static TemporalType fromXml(String name) { + return TemporalType.valueOf( name ); + } + + public static String toXml(TemporalType temporalType) { + return temporalType.name(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/package-info.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/package-info.java new file mode 100644 index 0000000000..0d61b3224e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/package-info.java @@ -0,0 +1,11 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +/** + * JAXB for JPA's {@code orm.xml} mapping schema. + */ +package org.hibernate.boot.jaxb.mapping; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AssociationAttribute.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AssociationAttribute.java new file mode 100644 index 0000000000..f9784577ab --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AssociationAttribute.java @@ -0,0 +1,23 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.jaxb.mapping.spi; + +public interface AssociationAttribute extends PersistentAttribute, FetchableAttribute { + + JaxbJoinTable getJoinTable(); + + void setJoinTable(JaxbJoinTable value); + + JaxbCascadeType getCascade(); + + void setCascade(JaxbCascadeType value); + + String getTargetEntity(); + + void setTargetEntity(String value); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AttributesContainer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AttributesContainer.java new file mode 100644 index 0000000000..18b20d6592 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AttributesContainer.java @@ -0,0 +1,35 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import java.util.List; + +/** + * Common interface for JAXB bindings which are containers of attributes. + * + * @author Strong Liu + * @author Steve Ebersole + */ +public interface AttributesContainer { + + List getTransient(); + + List getBasic(); + + List getElementCollection(); + + List getEmbedded(); + + List getManyToMany(); + + List getManyToOne(); + + List getOneToMany(); + + List getOneToOne(); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/CollectionAttribute.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/CollectionAttribute.java new file mode 100644 index 0000000000..dc81a6f224 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/CollectionAttribute.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import java.util.List; +import javax.persistence.EnumType; +import javax.persistence.TemporalType; + +/** + * Common interface for Jaxb bindings that represent persistent collection attributes. + * + * @author Brett Meyer + */ +public interface CollectionAttribute extends FetchableAttribute { + + String getOrderBy(); + + void setOrderBy(String value); + + JaxbOrderColumn getOrderColumn(); + + void setOrderColumn(JaxbOrderColumn value); + + JaxbMapKey getMapKey(); + + void setMapKey(JaxbMapKey value); + + JaxbMapKeyClass getMapKeyClass(); + + void setMapKeyClass(JaxbMapKeyClass value); + + TemporalType getMapKeyTemporal(); + + void setMapKeyTemporal(TemporalType value); + + EnumType getMapKeyEnumerated(); + + void setMapKeyEnumerated(EnumType value); + + List getMapKeyAttributeOverride(); + + List getMapKeyConvert(); + + JaxbMapKeyColumn getMapKeyColumn(); + + void setMapKeyColumn(JaxbMapKeyColumn value); + + List getMapKeyJoinColumn(); + + JaxbForeignKey getMapKeyForeignKey(); + + void setMapKeyForeignKey(JaxbForeignKey value); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/EntityOrMappedSuperclass.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/EntityOrMappedSuperclass.java new file mode 100644 index 0000000000..0c33cec1ae --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/EntityOrMappedSuperclass.java @@ -0,0 +1,62 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +/** + * Common interface for JAXB bindings representing entities and mapped-superclasses. + */ +public interface EntityOrMappedSuperclass extends ManagedType, LifecycleCallbackContainer { + + JaxbIdClass getIdClass(); + + void setIdClass(JaxbIdClass value); + + JaxbEmptyType getExcludeDefaultListeners(); + + void setExcludeDefaultListeners(JaxbEmptyType value); + + JaxbEmptyType getExcludeSuperclassListeners(); + + void setExcludeSuperclassListeners(JaxbEmptyType value); + + JaxbEntityListeners getEntityListeners(); + + void setEntityListeners(JaxbEntityListeners value); + + JaxbPrePersist getPrePersist(); + + void setPrePersist(JaxbPrePersist value); + + JaxbPostPersist getPostPersist(); + + void setPostPersist(JaxbPostPersist value); + + JaxbPreRemove getPreRemove(); + + void setPreRemove(JaxbPreRemove value); + + JaxbPostRemove getPostRemove(); + + void setPostRemove(JaxbPostRemove value); + + JaxbPreUpdate getPreUpdate(); + + void setPreUpdate(JaxbPreUpdate value); + + JaxbPostUpdate getPostUpdate(); + + void setPostUpdate(JaxbPostUpdate value); + + JaxbPostLoad getPostLoad(); + + void setPostLoad(JaxbPostLoad value); + + JaxbAttributes getAttributes(); + + void setAttributes(JaxbAttributes value); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/FetchableAttribute.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/FetchableAttribute.java new file mode 100644 index 0000000000..edce1ae655 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/FetchableAttribute.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import javax.persistence.FetchType; + +/** + * Common interface for JAXB bindings that represent attributes with laziness and fetch style. + * + * @author Brett Meyer + */ +public interface FetchableAttribute { + + FetchType getFetch(); + + void setFetch(FetchType value); + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallback.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallback.java new file mode 100644 index 0000000000..223207dd81 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallback.java @@ -0,0 +1,17 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +/** + * Common interface for all the JAXB bindings representing lifecycle callbacks. + * + * @author Strong Liu + * @author Steve Ebersole + */ +public interface LifecycleCallback { + String getMethodName(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallbackContainer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallbackContainer.java new file mode 100644 index 0000000000..7c0550491e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallbackContainer.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.jaxb.mapping.spi; + +public interface LifecycleCallbackContainer { + String getDescription(); + + void setDescription(String value); + + JaxbPrePersist getPrePersist(); + + void setPrePersist(JaxbPrePersist value); + + JaxbPostPersist getPostPersist(); + + void setPostPersist(JaxbPostPersist value); + + JaxbPreRemove getPreRemove(); + + void setPreRemove(JaxbPreRemove value); + + JaxbPostRemove getPostRemove(); + + void setPostRemove(JaxbPostRemove value); + + JaxbPreUpdate getPreUpdate(); + + void setPreUpdate(JaxbPreUpdate value); + + JaxbPostUpdate getPostUpdate(); + + void setPostUpdate(JaxbPostUpdate value); + + JaxbPostLoad getPostLoad(); + + void setPostLoad(JaxbPostLoad value); + + String getClazz(); + + void setClazz(String value); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java new file mode 100644 index 0000000000..7d8da934bd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import javax.persistence.AccessType; + +/** + * Common interface for JAXB bindings representing entities, mapped-superclasses and embeddables (JPA collective + * calls these "managed types" in terms of its Metamodel api). + * + * @author Strong Liu + * @author Steve Ebersole + */ +public interface ManagedType { + + String getDescription(); + + void setDescription(String value); + + String getClazz(); + + void setClazz(String className); + + Boolean isMetadataComplete(); + + void setMetadataComplete(Boolean isMetadataComplete); + + AccessType getAccess(); + + void setAccess(AccessType value); + + AttributesContainer getAttributes(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/NamedQuery.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/NamedQuery.java new file mode 100644 index 0000000000..79ded21dc1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/NamedQuery.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import java.io.Serializable; +import java.util.List; +import javax.persistence.LockModeType; + +public interface NamedQuery extends Serializable { + String getDescription(); + + void setDescription(String value); + + String getQuery(); + + void setQuery(String value); + + LockModeType getLockMode(); + + void setLockMode(LockModeType value); + + List getHint(); + + String getName(); + + void setName(String value); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/PersistentAttribute.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/PersistentAttribute.java new file mode 100644 index 0000000000..f1ee13b1ed --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/PersistentAttribute.java @@ -0,0 +1,23 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +import javax.persistence.AccessType; + +/** + * Common interface for JAXB bindings that represent persistent attributes. + * + * @author Strong Liu + * @author Steve Ebersole + */ +public interface PersistentAttribute { + String getName(); + + AccessType getAccess(); + + void setAccess(AccessType accessType); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/SchemaAware.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/SchemaAware.java new file mode 100644 index 0000000000..3f726a4b51 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/SchemaAware.java @@ -0,0 +1,23 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +/** + * Common interface for JAXB bindings that understand database schema (tables, sequences, etc). + * + * @author Strong Liu + * @author Steve Ebersole + */ +public interface SchemaAware { + String getSchema(); + + void setSchema(String schema); + + String getCatalog(); + + void setCatalog(String catalog); +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlMappingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlMappingOptions.java new file mode 100644 index 0000000000..5247ef82cc --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlMappingOptions.java @@ -0,0 +1,86 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.boot.jaxb.spi; + +import org.hibernate.boot.jaxb.internal.DefaultXmlMappingOptions; +import org.hibernate.boot.registry.selector.spi.StrategySelector; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.config.spi.StandardConverters; +import org.hibernate.service.ServiceRegistry; + +/** + * The options of XML mapping. + *

+ * We're using an interface instead of simply configuration properties, + * so that we can override the options easily in integrations (Quarkus) + * and tests (to run the tests multiple times with different options). + */ +public interface XmlMappingOptions { + + String DEFAULT_NAME = "default"; + + static XmlMappingOptions get(ServiceRegistry serviceRegistry) { + final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class ); + + // The config service may be null if we're using a BootstrapServiceRegistry, + // since configuration properties are unknown at that point. + // That can happen with MetadataSources in particular, + // because for some reason we allow MetadataSources to be built before the StandardServiceRegistry + // (and Quarkus relies on that). + // That's why we prefer to rely on strategies (see below): + // they can be customized without relying on configuration properties + // through the service loader. + boolean xmlMappingEnabled = configService == null || configService.getSetting( + AvailableSettings.XML_MAPPING_ENABLED, + StandardConverters.BOOLEAN, + true + ); + + XmlMappingOptions result; + if ( !xmlMappingEnabled ) { + result = new XmlMappingOptions() { + @Override + public boolean isEnabled() { + return false; + } + }; + } + else { + StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class ); + result = strategySelector.resolveDefaultableStrategy( + XmlMappingOptions.class, + XmlMappingOptions.DEFAULT_NAME, + new DefaultXmlMappingOptions() + ); + } + + return result; + } + + /** + * Allows to skip processing of XML Mapping. + * This is for people using exclusively annotations to define their model, and might + * be able to improve efficiency of booting Hibernate ORM. + * By default, the XML mapping is taken into account. + */ + default boolean isEnabled() { + return true; + } + + /** + * Whether to prefer JAXB implementations for XML parsing, + * or to rely on legacy behavior (JAXB for hbm.xml, DOM4J for orm.xml and Envers). + *

+ * This option will be removed in a future major version (probably ORM 6.0) + * where JAXB will always be used. + */ + default boolean isPreferJaxb() { + return false; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index a63a191652..90e9edce02 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -15,6 +15,7 @@ import org.hibernate.boot.MetadataSources; import org.hibernate.boot.internal.InFlightMetadataCollectorImpl; import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; import org.hibernate.boot.jaxb.internal.MappingBinder; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributor; import org.hibernate.boot.model.process.internal.ManagedResourcesImpl; @@ -32,11 +33,8 @@ import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.boot.spi.MetadataContributor; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.MetadataSourceType; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; @@ -97,16 +95,11 @@ public class MetadataBuildingProcess { final MetadataSources sources, final BootstrapContext bootstrapContext) { final ManagedResourcesImpl managedResources = ManagedResourcesImpl.baseline( sources, bootstrapContext ); - final ConfigurationService configService = bootstrapContext.getServiceRegistry().getService( ConfigurationService.class ); - final boolean xmlMappingEnabled = configService.getSetting( - AvailableSettings.XML_MAPPING_ENABLED, - StandardConverters.BOOLEAN, - true - ); + XmlMappingOptions xmlMappingOptions = XmlMappingOptions.get( bootstrapContext.getServiceRegistry() ); ScanningCoordinator.INSTANCE.coordinateScan( managedResources, bootstrapContext, - xmlMappingEnabled ? sources.getXmlMappingBinderAccess() : null + xmlMappingOptions.isEnabled() ? sources.getXmlMappingBinderAccess() : null ); return managedResources; } @@ -153,7 +146,7 @@ public class MetadataBuildingProcess { final MetadataSourceProcessor processor = new MetadataSourceProcessor() { private final MetadataSourceProcessor hbmProcessor = - options.isXmlMappingEnabled() + options.getXmlMappingOptions().isEnabled() ? new HbmMetadataSourceProcessorImpl( managedResources, rootMetadataBuildingContext ) : new NoOpMetadataSourceProcessorImpl(); @@ -290,13 +283,14 @@ public class MetadataBuildingProcess { metadataCollector.processSecondPasses( rootMetadataBuildingContext ); - if ( options.isXmlMappingEnabled() ) { + final XmlMappingOptions xmlMappingOptions = options.getXmlMappingOptions(); + if ( xmlMappingOptions.isEnabled() ) { final Iterable producers = classLoaderService.loadJavaServices( AdditionalJaxbMappingProducer.class ); if ( producers != null ) { final EntityHierarchyBuilder hierarchyBuilder = new EntityHierarchyBuilder(); // final MappingBinder mappingBinder = new MappingBinder( true ); // We need to disable validation here. It seems Envers is not producing valid (according to schema) XML - final MappingBinder mappingBinder = new MappingBinder( classLoaderService, false ); + final MappingBinder mappingBinder = new MappingBinder( classLoaderService, false, xmlMappingOptions ); for ( AdditionalJaxbMappingProducer producer : producers ) { log.tracef( "Calling AdditionalJaxbMappingProducer : %s", producer ); Collection additionalMappings = producer.produceAdditionalMappings( diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java index bae154c829..8f67981a1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/annotations/AnnotationMetadataSourceProcessorImpl.java @@ -22,17 +22,21 @@ import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.boot.AttributeConverterInfo; import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.convert.spi.ConverterDescriptor; import org.hibernate.boot.model.process.spi.ManagedResources; import org.hibernate.boot.model.source.spi.MetadataSourceProcessor; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware; import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware.JpaOrmXmlPersistenceUnitDefaults; +import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.cfg.AnnotationBinder; import org.hibernate.cfg.InheritanceState; import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector; import org.hibernate.cfg.annotations.reflection.JPAMetadataProvider; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenMetadataProvider; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; @@ -75,21 +79,32 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc final AttributeConverterManager attributeConverterManager = new AttributeConverterManager( rootMetadataBuildingContext ); this.classLoaderService = rootMetadataBuildingContext.getBuildingOptions().getServiceRegistry().getService( ClassLoaderService.class ); - if ( rootMetadataBuildingContext.getBuildingOptions().isXmlMappingEnabled() ) { - + MetadataBuildingOptions metadataBuildingOptions = rootMetadataBuildingContext.getBuildingOptions(); + XmlMappingOptions xmlMappingOptions = metadataBuildingOptions.getXmlMappingOptions(); + if ( xmlMappingOptions.isEnabled() ) { // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Ewww. This is temporary until we migrate to Jandex + StAX for annotation binding + if ( xmlMappingOptions.isPreferJaxb() ) { + final JPAXMLOverriddenMetadataProvider jpaMetadataProvider = (JPAXMLOverriddenMetadataProvider) ( (MetadataProviderInjector) reflectionManager ) + .getMetadataProvider(); + for ( Binding xmlBinding : managedResources.getXmlMappingBindings() ) { + Object root = xmlBinding.getRoot(); + if ( !(root instanceof JaxbEntityMappings) ) { + continue; + } + JaxbEntityMappings entityMappings = (JaxbEntityMappings) xmlBinding.getRoot(); + final List classNames = jpaMetadataProvider.getXMLContext().addDocument( entityMappings ); + for ( String className : classNames ) { + xClasses.add( toXClass( className, reflectionManager, classLoaderService ) ); + } + } + jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager ); + } + else { final JPAMetadataProvider jpaMetadataProvider = (JPAMetadataProvider) ( (MetadataProviderInjector) reflectionManager ) .getMetadataProvider(); for ( Binding xmlBinding : managedResources.getXmlMappingBindings() ) { - // if ( !MappingBinder.DelayedOrmXmlData.class.isInstance( xmlBinding.getRoot() ) ) { - // continue; - // } - // - // // convert the StAX representation in delayedOrmXmlData to DOM because that's what commons-annotations needs - // final MappingBinder.DelayedOrmXmlData delayedOrmXmlData = (MappingBinder.DelayedOrmXmlData) xmlBinding.getRoot(); - // org.dom4j.Document dom4jDocument = toDom4jDocument( delayedOrmXmlData ); if ( !org.dom4j.Document.class.isInstance( xmlBinding.getRoot() ) ) { continue; } @@ -101,6 +116,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc } } jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager ); + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } @@ -138,22 +154,6 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc return reflectionManager.toXClass( cls.classForName( className ) ); } -// private Document toDom4jDocument(MappingBinder.DelayedOrmXmlData delayedOrmXmlData) { -// // todo : do we need to build a DocumentFactory instance for use here? -// // historically we did that to set TCCL since, iirc, dom4j uses TCCL -// org.dom4j.io.STAXEventReader staxToDom4jReader = new STAXEventReader(); -// try { -// return staxToDom4jReader.readDocument( delayedOrmXmlData.getStaxEventReader() ); -// } -// catch (XMLStreamException e) { -// throw new MappingException( -// "An error occurred transforming orm.xml document from StAX to dom4j representation ", -// e, -// delayedOrmXmlData.getOrigin() -// ); -// } -// } - @Override public void prepare() { // use any persistence-unit-defaults defined in orm.xml diff --git a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java index d9943c52cc..560070bbf3 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/registry/selector/internal/StrategySelectorBuilder.java @@ -9,6 +9,8 @@ package org.hibernate.boot.registry.selector.internal; import java.util.ArrayList; import java.util.List; +import org.hibernate.boot.jaxb.internal.DefaultXmlMappingOptions; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.ImplicitNamingStrategyComponentPathImpl; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; @@ -103,6 +105,8 @@ public class StrategySelectorBuilder { addSqmMultiTableMutationStrategies( strategySelector ); addImplicitNamingStrategies( strategySelector ); addCacheKeysFactories( strategySelector ); + strategySelector.registerStrategyImplementor( XmlMappingOptions.class, XmlMappingOptions.DEFAULT_NAME, + DefaultXmlMappingOptions.class ); // apply auto-discovered registrations for ( StrategyRegistrationProvider provider : classLoaderService.loadJavaServices( StrategyRegistrationProvider.class ) ) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java index bf959a7f62..3a194c4152 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java @@ -16,6 +16,7 @@ import org.hibernate.boot.CacheRegionDefinition; import org.hibernate.boot.archive.scan.spi.ScanEnvironment; import org.hibernate.boot.archive.scan.spi.ScanOptions; import org.hibernate.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.IdGeneratorStrategyInterpreter; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; @@ -184,8 +185,8 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad } @Override - public boolean isXmlMappingEnabled() { - return delegate.isXmlMappingEnabled(); + public XmlMappingOptions getXmlMappingOptions() { + return delegate.getXmlMappingOptions(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java index b10b5ee567..7577778764 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java @@ -15,6 +15,7 @@ import org.hibernate.boot.CacheRegionDefinition; import org.hibernate.boot.archive.scan.spi.ScanEnvironment; import org.hibernate.boot.archive.scan.spi.ScanOptions; import org.hibernate.boot.archive.spi.ArchiveDescriptorFactory; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.IdGeneratorStrategyInterpreter; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.PhysicalNamingStrategy; @@ -260,7 +261,7 @@ public interface MetadataBuildingOptions { return null; } - default boolean isXmlMappingEnabled() { - return true; + default XmlMappingOptions getXmlMappingOptions() { + return XmlMappingOptions.get( getServiceRegistry() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java index 8102865f2a..fa7014840b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/XmlMappingBinderAccess.java @@ -20,7 +20,11 @@ import org.hibernate.boot.jaxb.internal.InputStreamXmlSource; import org.hibernate.boot.jaxb.internal.MappingBinder; import org.hibernate.boot.jaxb.internal.UrlXmlSource; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.config.spi.ConfigurationService; +import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.service.ServiceRegistry; import org.jboss.logging.Logger; @@ -37,10 +41,12 @@ public class XmlMappingBinderAccess { public XmlMappingBinderAccess(ServiceRegistry serviceRegistry) { this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); + XmlMappingOptions xmlMappingOptions = XmlMappingOptions.get( serviceRegistry ); + // NOTE : The boolean here indicates whether or not to perform validation as we load XML documents. // Should we expose this setting? Disabling would speed up JAXP and JAXB at runtime, but potentially // at the cost of less obvious errors when a document is not valid. - this.mappingBinder = new MappingBinder( serviceRegistry.getService( ClassLoaderService.class ), true ); + this.mappingBinder = new MappingBinder( classLoaderService, true, xmlMappingOptions ); } public MappingBinder getMappingBinder() { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/xsd/MappingXsdSupport.java b/hibernate-core/src/main/java/org/hibernate/boot/xsd/MappingXsdSupport.java index 33a3719270..0ba5ee2ab8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/xsd/MappingXsdSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/xsd/MappingXsdSupport.java @@ -35,13 +35,13 @@ public class MappingXsdSupport { private final XsdDescriptor jpa21 = LocalXsdResolver.buildXsdDescriptor( "org/hibernate/jpa/orm_2_1.xsd", "2.1", - "http://xmlns.jcp.org/xml/ns/persistence" + "http://xmlns.jcp.org/xml/ns/persistence/orm" ); private final XsdDescriptor jpa22 = LocalXsdResolver.buildXsdDescriptor( "org/hibernate/jpa/orm_2_2.xsd", "2.2", - "http://xmlns.jcp.org/xml/ns/persistence" + "http://xmlns.jcp.org/xml/ns/persistence/orm" ); private final XsdDescriptor jpa30 = LocalXsdResolver.buildXsdDescriptor( diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 1a1c0d89b4..c3e4a43d57 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -741,7 +741,6 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings { */ String XML_MAPPING_ENABLED = "hibernate.xml_mapping_enabled"; - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // SessionFactoryBuilder level settings // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index dacd24b3ab..0ee7fc79ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -30,6 +30,7 @@ import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.internal.ClassmateContext; +import org.hibernate.boot.jaxb.spi.Binding; import org.hibernate.boot.model.TypeContributor; import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor; import org.hibernate.boot.model.convert.internal.InstanceBasedConverterDescriptor; @@ -47,6 +48,7 @@ import org.hibernate.boot.query.NamedHqlQueryDefinition; import org.hibernate.boot.query.NamedNativeQueryDefinition; import org.hibernate.boot.query.NamedProcedureCallDefinition; import org.hibernate.boot.query.NamedResultSetMappingDescriptor; +import org.hibernate.boot.spi.XmlMappingBinderAccess; import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.internal.CoreLogging; @@ -372,6 +374,13 @@ public class Configuration { return this; } + /** + * @return An object capable of parsing XML mapping files that can then be passed to {@link #addXmlMapping(Binding)}. + */ + public XmlMappingBinderAccess getXmlMappingBinderAccess() { + return metadataSources.getXmlMappingBinderAccess(); + } + /** * @deprecated No longer supported. */ @@ -379,6 +388,17 @@ public class Configuration { public void add(XmlDocument metadataXml) { } + /** + * Read mappings that were parsed using {@link #getXmlMappingBinderAccess()}. + * + * @param binding the parsed mapping + * @return this (for method chaining purposes) + */ + public Configuration addXmlMapping(Binding binding) { + metadataSources.addXmlBinding( binding ); + return this; + } + /** * Add a cached mapping file. A cached file is a serialized representation * of the DOM structure of a particular mapping. It is saved from a previous diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java index 6147c5a23b..0db00e64be 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java @@ -31,6 +31,7 @@ import org.hibernate.boot.spi.ClassLoaderAccess; import org.hibernate.boot.spi.ClassLoaderAccessDelegateImpl; import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenMetadataProvider; import org.dom4j.Element; @@ -38,7 +39,13 @@ import org.dom4j.Element; * MetadataProvider aware of the JPA Deployment descriptor * * @author Emmanuel Bernard + * + * @deprecated This class is not API: do not use it from application code. + * This class will be removed in Hibernate ORM 6.0. + * For implementation code, use {@link JPAXMLOverriddenMetadataProvider} + * instead. */ +@Deprecated @SuppressWarnings("unchecked") public final class JPAMetadataProvider implements MetadataProvider { @@ -75,12 +82,12 @@ public final class JPAMetadataProvider implements MetadataProvider { return delegate; } }, - metadataBuildingOptions.isXmlMappingEnabled() ); + metadataBuildingOptions.getXmlMappingOptions().isEnabled() ); } public JPAMetadataProvider(BootstrapContext bootstrapContext) { this( bootstrapContext.getClassLoaderAccess(), - bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled() ); + bootstrapContext.getMetadataBuildingOptions().getXmlMappingOptions().isEnabled() ); } JPAMetadataProvider(ClassLoaderAccess classLoaderAccess, boolean xmlMetadataEnabled) { @@ -148,7 +155,8 @@ public final class JPAMetadataProvider implements MetadataProvider { defaults.put( SequenceGenerator.class, sequenceGenerators ); } for ( Element subelement : elements ) { - sequenceGenerators.add( JPAOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) ); + sequenceGenerators.add( JPAOverriddenAnnotationReader + .buildSequenceGeneratorAnnotation( subelement ) ); } elements = element.elements( "table-generator" ); @@ -170,7 +178,8 @@ public final class JPAMetadataProvider implements MetadataProvider { namedQueries = new ArrayList<>(); defaults.put( NamedQuery.class, namedQueries ); } - List currentNamedQueries = JPAOverriddenAnnotationReader.buildNamedQueries( + List currentNamedQueries = JPAOverriddenAnnotationReader + .buildNamedQueries( element, false, xmlDefaults, @@ -183,7 +192,8 @@ public final class JPAMetadataProvider implements MetadataProvider { namedNativeQueries = new ArrayList<>(); defaults.put( NamedNativeQuery.class, namedNativeQueries ); } - List currentNamedNativeQueries = JPAOverriddenAnnotationReader.buildNamedQueries( + List currentNamedNativeQueries = JPAOverriddenAnnotationReader + .buildNamedQueries( element, true, xmlDefaults, @@ -198,7 +208,8 @@ public final class JPAMetadataProvider implements MetadataProvider { sqlResultSetMappings = new ArrayList<>(); defaults.put( SqlResultSetMapping.class, sqlResultSetMappings ); } - List currentSqlResultSetMappings = JPAOverriddenAnnotationReader.buildSqlResultsetMappings( + List currentSqlResultSetMappings = JPAOverriddenAnnotationReader + .buildSqlResultsetMappings( element, xmlDefaults, classLoaderAccess @@ -210,7 +221,8 @@ public final class JPAMetadataProvider implements MetadataProvider { namedStoredProcedureQueries = new ArrayList<>( ); defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries ); } - List currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader.buildNamedStoreProcedureQueries( + List currentNamedStoredProcedureQueries = JPAOverriddenAnnotationReader + .buildNamedStoreProcedureQueries( element, xmlDefaults, classLoaderAccess diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java index 5c5bad65ca..f69e19971d 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAOverriddenAnnotationReader.java @@ -126,6 +126,7 @@ import org.hibernate.annotations.common.reflection.ReflectionUtil; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.ClassLoaderAccess; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; @@ -141,7 +142,13 @@ import org.dom4j.Element; * @author Davide Marchignoli * @author Emmanuel Bernard * @author Hardy Ferentschik + * + * @deprecated This class is not API: do not use it from application code. + * This class will be removed in Hibernate ORM 6.0. + * For implementation code, use {@link JPAXMLOverriddenAnnotationReader} + * instead. */ +@Deprecated @SuppressWarnings("unchecked") public class JPAOverriddenAnnotationReader implements AnnotationReader { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JPAOverriddenAnnotationReader.class ); @@ -239,12 +246,12 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { annotationToXml.put( ConstructorResult.class, "constructor-result" ); } - private XMLContext xmlContext; + private final XMLContext xmlContext; private final ClassLoaderAccess classLoaderAccess; private final AnnotatedElement element; - private String className; - private String propertyName; - private PropertyType propertyType; + private final String className; + private final String propertyName; + private final PropertyType propertyType; private transient Annotation[] annotations; private transient Map annotationsMap; private transient List elementsForProperty; @@ -264,6 +271,8 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { if ( el instanceof Class ) { Class clazz = (Class) el; className = clazz.getName(); + propertyName = null; + propertyType = null; } else if ( el instanceof Field ) { Field field = (Field) el; @@ -283,18 +292,18 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { else if ( el instanceof Method ) { Method method = (Method) el; className = method.getDeclaringClass().getName(); - propertyName = method.getName(); + String methodName = method.getName(); // YUCK! The null here is the 'boundType', we'd rather get the TypeEnvironment() if ( ReflectionUtil.isProperty( method, null, PersistentAttributeFilter.INSTANCE ) ) { - if ( propertyName.startsWith( "get" ) ) { - propertyName = Introspector.decapitalize( propertyName.substring( "get".length() ) ); + if ( methodName.startsWith( "get" ) ) { + propertyName = Introspector.decapitalize( methodName.substring( "get".length() ) ); } - else if ( propertyName.startsWith( "is" ) ) { - propertyName = Introspector.decapitalize( propertyName.substring( "is".length() ) ); + else if ( methodName.startsWith( "is" ) ) { + propertyName = Introspector.decapitalize( methodName.substring( "is".length() ) ); } else { - throw new RuntimeException( "Method " + propertyName + " is not a property getter" ); + throw new RuntimeException( "Method " + methodName + " is not a property getter" ); } propertyType = PropertyType.PROPERTY; try { @@ -305,12 +314,14 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { } } else { + propertyName = methodName; propertyType = PropertyType.METHOD; } } else { className = null; propertyName = null; + propertyType = null; } } @@ -881,7 +892,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { * field or property. Thus, any methods which might in some contexts merge * with annotations must not do so in this context. * - * @see #getElementCollection(List, org.hibernate.cfg.annotations.reflection.XMLContext.Default) + * @see #getElementCollection(List, XMLContext.Default) */ private void getAssociation( Class annotationType, List annotationList, XMLContext.Default defaults @@ -2690,7 +2701,8 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader { AnnotationDescriptor ad = new AnnotationDescriptor( IdClass.class ); Class clazz; try { - clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( attr.getValue(), defaults ) + clazz = classLoaderAccess.classForName( XMLContext + .buildSafeClassName( attr.getValue(), defaults ) ); } catch ( ClassLoadingException e ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/XMLContext.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/XMLContext.java index 2a77b1c6c3..21db9bb2b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/XMLContext.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/XMLContext.java @@ -32,7 +32,12 @@ import org.dom4j.Element; * * @author Emmanuel Bernard * @author Brett Meyer + * + * @deprecated This class is not API: do not use it from application code. + * This class will be removed in Hibernate ORM 6.0. + * For implementation code, use {@link org.hibernate.cfg.annotations.reflection.internal.XMLContext} instead. */ +@Deprecated public class XMLContext implements Serializable { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( XMLContext.class ); @@ -222,7 +227,7 @@ public class XMLContext implements Serializable { return className; } - public static String buildSafeClassName(String className, XMLContext.Default defaults) { + public static String buildSafeClassName(String className, Default defaults) { return buildSafeClassName( className, defaults.getPackageName() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java new file mode 100644 index 0000000000..c91855d43e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java @@ -0,0 +1,3077 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.cfg.annotations.reflection.internal; + +import java.beans.Introspector; +import java.lang.annotation.Annotation; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.AssociationOverride; +import javax.persistence.AssociationOverrides; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.Basic; +import javax.persistence.Cacheable; +import javax.persistence.CascadeType; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ColumnResult; +import javax.persistence.ConstructorResult; +import javax.persistence.Convert; +import javax.persistence.Converts; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorType; +import javax.persistence.DiscriminatorValue; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EntityResult; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.ExcludeDefaultListeners; +import javax.persistence.ExcludeSuperclassListeners; +import javax.persistence.FetchType; +import javax.persistence.FieldResult; +import javax.persistence.ForeignKey; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Index; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.Lob; +import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; +import javax.persistence.MapKey; +import javax.persistence.MapKeyClass; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyEnumerated; +import javax.persistence.MapKeyJoinColumn; +import javax.persistence.MapKeyJoinColumns; +import javax.persistence.MapKeyTemporal; +import javax.persistence.MappedSuperclass; +import javax.persistence.MapsId; +import javax.persistence.NamedAttributeNode; +import javax.persistence.NamedEntityGraph; +import javax.persistence.NamedEntityGraphs; +import javax.persistence.NamedNativeQueries; +import javax.persistence.NamedNativeQuery; +import javax.persistence.NamedQueries; +import javax.persistence.NamedQuery; +import javax.persistence.NamedStoredProcedureQueries; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.NamedSubgraph; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.OrderBy; +import javax.persistence.OrderColumn; +import javax.persistence.ParameterMode; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.PrimaryKeyJoinColumns; +import javax.persistence.QueryHint; +import javax.persistence.SecondaryTable; +import javax.persistence.SecondaryTables; +import javax.persistence.SequenceGenerator; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.SqlResultSetMappings; +import javax.persistence.StoredProcedureParameter; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; +import javax.persistence.UniqueConstraint; +import javax.persistence.Version; + +import org.hibernate.AnnotationException; +import org.hibernate.annotations.Any; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.Columns; +import org.hibernate.annotations.ManyToAny; +import org.hibernate.annotations.common.annotationfactory.AnnotationDescriptor; +import org.hibernate.annotations.common.annotationfactory.AnnotationFactory; +import org.hibernate.annotations.common.reflection.AnnotationReader; +import org.hibernate.annotations.common.reflection.ReflectionUtil; +import org.hibernate.boot.jaxb.mapping.spi.AssociationAttribute; +import org.hibernate.boot.jaxb.mapping.spi.AttributesContainer; +import org.hibernate.boot.jaxb.mapping.spi.EntityOrMappedSuperclass; +import org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationOverride; +import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributeOverride; +import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributes; +import org.hibernate.boot.jaxb.mapping.spi.JaxbBasic; +import org.hibernate.boot.jaxb.mapping.spi.JaxbCascadeType; +import org.hibernate.boot.jaxb.mapping.spi.JaxbCollectionTable; +import org.hibernate.boot.jaxb.mapping.spi.JaxbColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbColumnResult; +import org.hibernate.boot.jaxb.mapping.spi.JaxbConstructorResult; +import org.hibernate.boot.jaxb.mapping.spi.JaxbConvert; +import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollection; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddable; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbedded; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedId; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmptyType; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListener; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListeners; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityResult; +import org.hibernate.boot.jaxb.mapping.spi.JaxbFieldResult; +import org.hibernate.boot.jaxb.mapping.spi.JaxbGeneratedValue; +import org.hibernate.boot.jaxb.mapping.spi.JaxbId; +import org.hibernate.boot.jaxb.mapping.spi.JaxbIdClass; +import org.hibernate.boot.jaxb.mapping.spi.JaxbIndex; +import org.hibernate.boot.jaxb.mapping.spi.JaxbInheritance; +import org.hibernate.boot.jaxb.mapping.spi.JaxbJoinColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbJoinTable; +import org.hibernate.boot.jaxb.mapping.spi.JaxbLob; +import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToMany; +import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToOne; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMapKey; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMapKeyClass; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMapKeyColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMapKeyJoinColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMappedSuperclass; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedAttributeNode; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedEntityGraph; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedNativeQuery; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedQuery; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedStoredProcedureQuery; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedSubgraph; +import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToMany; +import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToOne; +import org.hibernate.boot.jaxb.mapping.spi.JaxbOrderColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPrimaryKeyJoinColumn; +import org.hibernate.boot.jaxb.mapping.spi.JaxbQueryHint; +import org.hibernate.boot.jaxb.mapping.spi.JaxbSecondaryTable; +import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGenerator; +import org.hibernate.boot.jaxb.mapping.spi.JaxbSqlResultSetMapping; +import org.hibernate.boot.jaxb.mapping.spi.JaxbStoredProcedureParameter; +import org.hibernate.boot.jaxb.mapping.spi.JaxbTable; +import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGenerator; +import org.hibernate.boot.jaxb.mapping.spi.JaxbTransient; +import org.hibernate.boot.jaxb.mapping.spi.JaxbUniqueConstraint; +import org.hibernate.boot.jaxb.mapping.spi.JaxbVersion; +import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer; +import org.hibernate.boot.jaxb.mapping.spi.ManagedType; +import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.ClassLoaderAccess; +import org.hibernate.cfg.annotations.reflection.PersistentAttributeFilter; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.StringHelper; + +import static org.hibernate.cfg.annotations.reflection.internal.PropertyMappingElementCollector.JAXB_TRANSIENT_NAME; +import static org.hibernate.cfg.annotations.reflection.internal.PropertyMappingElementCollector.PERSISTENT_ATTRIBUTE_NAME; + +/** + * Encapsulates the overriding of Java annotations from an EJB 3.0 descriptor (orm.xml, ...). + * + * @author Paolo Perrotta + * @author Davide Marchignoli + * @author Emmanuel Bernard + * @author Hardy Ferentschik + */ +// FIXME HHH-14529 Change this class to use JaxbEntityMappings instead of Document. +// I'm delaying this change in order to keep the commits simpler and easier to review. +@SuppressWarnings("unchecked") +public class JPAXMLOverriddenAnnotationReader implements AnnotationReader { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JPAXMLOverriddenAnnotationReader.class ); + + private static final String SCHEMA_VALIDATION = "Activate schema validation for more information"; + private static final String WORD_SEPARATOR = "-"; + + private enum PropertyType { + PROPERTY, + FIELD, + METHOD + } + + private static final Map annotationToXml; + + static { + annotationToXml = new HashMap<>(); + annotationToXml.put( Entity.class, "entity" ); + annotationToXml.put( MappedSuperclass.class, "mapped-superclass" ); + annotationToXml.put( Embeddable.class, "embeddable" ); + annotationToXml.put( Table.class, "table" ); + annotationToXml.put( SecondaryTable.class, "secondary-table" ); + annotationToXml.put( SecondaryTables.class, "secondary-table" ); + annotationToXml.put( PrimaryKeyJoinColumn.class, "primary-key-join-column" ); + annotationToXml.put( PrimaryKeyJoinColumns.class, "primary-key-join-column" ); + annotationToXml.put( IdClass.class, "id-class" ); + annotationToXml.put( Inheritance.class, "inheritance" ); + annotationToXml.put( DiscriminatorValue.class, "discriminator-value" ); + annotationToXml.put( DiscriminatorColumn.class, "discriminator-column" ); + annotationToXml.put( SequenceGenerator.class, "sequence-generator" ); + annotationToXml.put( TableGenerator.class, "table-generator" ); + annotationToXml.put( NamedEntityGraph.class, "named-entity-graph" ); + annotationToXml.put( NamedEntityGraphs.class, "named-entity-graph" ); + annotationToXml.put( NamedQuery.class, "named-query" ); + annotationToXml.put( NamedQueries.class, "named-query" ); + annotationToXml.put( NamedNativeQuery.class, "named-native-query" ); + annotationToXml.put( NamedNativeQueries.class, "named-native-query" ); + annotationToXml.put( NamedStoredProcedureQuery.class, "named-stored-procedure-query" ); + annotationToXml.put( NamedStoredProcedureQueries.class, "named-stored-procedure-query" ); + annotationToXml.put( SqlResultSetMapping.class, "sql-result-set-mapping" ); + annotationToXml.put( SqlResultSetMappings.class, "sql-result-set-mapping" ); + annotationToXml.put( ExcludeDefaultListeners.class, "exclude-default-listeners" ); + annotationToXml.put( ExcludeSuperclassListeners.class, "exclude-superclass-listeners" ); + annotationToXml.put( AccessType.class, "access" ); + annotationToXml.put( AttributeOverride.class, "attribute-override" ); + annotationToXml.put( AttributeOverrides.class, "attribute-override" ); + annotationToXml.put( AttributeOverride.class, "association-override" ); + annotationToXml.put( AttributeOverrides.class, "association-override" ); + annotationToXml.put( AttributeOverride.class, "map-key-attribute-override" ); + annotationToXml.put( AttributeOverrides.class, "map-key-attribute-override" ); + annotationToXml.put( Id.class, "id" ); + annotationToXml.put( EmbeddedId.class, "embedded-id" ); + annotationToXml.put( GeneratedValue.class, "generated-value" ); + annotationToXml.put( Column.class, "column" ); + annotationToXml.put( Columns.class, "column" ); + annotationToXml.put( Temporal.class, "temporal" ); + annotationToXml.put( Lob.class, "lob" ); + annotationToXml.put( Enumerated.class, "enumerated" ); + annotationToXml.put( Version.class, "version" ); + annotationToXml.put( Transient.class, "transient" ); + annotationToXml.put( Basic.class, "basic" ); + annotationToXml.put( Embedded.class, "embedded" ); + annotationToXml.put( ManyToOne.class, "many-to-one" ); + annotationToXml.put( OneToOne.class, "one-to-one" ); + annotationToXml.put( OneToMany.class, "one-to-many" ); + annotationToXml.put( ManyToMany.class, "many-to-many" ); + annotationToXml.put( Any.class, "any" ); + annotationToXml.put( ManyToAny.class, "many-to-any" ); + annotationToXml.put( JoinTable.class, "join-table" ); + annotationToXml.put( JoinColumn.class, "join-column" ); + annotationToXml.put( JoinColumns.class, "join-column" ); + annotationToXml.put( MapKey.class, "map-key" ); + annotationToXml.put( OrderBy.class, "order-by" ); + annotationToXml.put( EntityListeners.class, "entity-listeners" ); + annotationToXml.put( PrePersist.class, "pre-persist" ); + annotationToXml.put( PreRemove.class, "pre-remove" ); + annotationToXml.put( PreUpdate.class, "pre-update" ); + annotationToXml.put( PostPersist.class, "post-persist" ); + annotationToXml.put( PostRemove.class, "post-remove" ); + annotationToXml.put( PostUpdate.class, "post-update" ); + annotationToXml.put( PostLoad.class, "post-load" ); + annotationToXml.put( CollectionTable.class, "collection-table" ); + annotationToXml.put( MapKeyClass.class, "map-key-class" ); + annotationToXml.put( MapKeyTemporal.class, "map-key-temporal" ); + annotationToXml.put( MapKeyEnumerated.class, "map-key-enumerated" ); + annotationToXml.put( MapKeyColumn.class, "map-key-column" ); + annotationToXml.put( MapKeyJoinColumn.class, "map-key-join-column" ); + annotationToXml.put( MapKeyJoinColumns.class, "map-key-join-column" ); + annotationToXml.put( OrderColumn.class, "order-column" ); + annotationToXml.put( Cacheable.class, "cacheable" ); + annotationToXml.put( Index.class, "index" ); + annotationToXml.put( ForeignKey.class, "foreign-key" ); + annotationToXml.put( Convert.class, "convert" ); + annotationToXml.put( Converts.class, "convert" ); + annotationToXml.put( ConstructorResult.class, "constructor-result" ); + } + + private final XMLContext xmlContext; + private final ClassLoaderAccess classLoaderAccess; + private final AnnotatedElement element; + private final String className; + private final String propertyName; + private final PropertyType propertyType; + private transient Annotation[] annotations; + private transient Map annotationsMap; + private transient PropertyMappingElementCollector elementsForProperty; + private AccessibleObject mirroredAttribute; + + JPAXMLOverriddenAnnotationReader( + AnnotatedElement el, + XMLContext xmlContext, + ClassLoaderAccess classLoaderAccess) { + this.element = el; + this.xmlContext = xmlContext; + this.classLoaderAccess = classLoaderAccess; + + if ( el instanceof Class ) { + Class clazz = (Class) el; + className = clazz.getName(); + propertyName = null; + propertyType = null; + } + else if ( el instanceof Field ) { + Field field = (Field) el; + className = field.getDeclaringClass().getName(); + propertyName = field.getName(); + propertyType = PropertyType.FIELD; + String expectedGetter = "get" + Character.toUpperCase( propertyName.charAt( 0 ) ) + propertyName.substring( + 1 + ); + try { + mirroredAttribute = field.getDeclaringClass().getDeclaredMethod( expectedGetter ); + } + catch ( NoSuchMethodException e ) { + //no method + } + } + else if ( el instanceof Method ) { + Method method = (Method) el; + className = method.getDeclaringClass().getName(); + String methodName = method.getName(); + + // YUCK! The null here is the 'boundType', we'd rather get the TypeEnvironment() + if ( ReflectionUtil.isProperty( method, null, PersistentAttributeFilter.INSTANCE ) ) { + if ( methodName.startsWith( "get" ) ) { + propertyName = Introspector.decapitalize( methodName.substring( "get".length() ) ); + } + else if ( methodName.startsWith( "is" ) ) { + propertyName = Introspector.decapitalize( methodName.substring( "is".length() ) ); + } + else { + throw new RuntimeException( "Method " + methodName + " is not a property getter" ); + } + propertyType = PropertyType.PROPERTY; + try { + mirroredAttribute = method.getDeclaringClass().getDeclaredField( propertyName ); + } + catch ( NoSuchFieldException e ) { + //no method + } + } + else { + propertyName = methodName; + propertyType = PropertyType.METHOD; + } + } + else { + className = null; + propertyName = null; + propertyType = null; + } + } + + // For tests only + public JPAXMLOverriddenAnnotationReader( + AnnotatedElement el, + XMLContext xmlContext, + BootstrapContext bootstrapContext) { + this( el, xmlContext, bootstrapContext.getClassLoaderAccess() ); + } + + public T getAnnotation(Class annotationType) { + initAnnotations(); + return (T) annotationsMap.get( annotationType ); + } + + public boolean isAnnotationPresent(Class annotationType) { + initAnnotations(); + return annotationsMap.containsKey( annotationType ); + } + + public Annotation[] getAnnotations() { + initAnnotations(); + return annotations; + } + + /* + * The idea is to create annotation proxies for the xml configuration elements. Using this proxy annotations together + * with the {@link JPAMetadataProvider} allows to handle xml configuration the same way as annotation configuration. + */ + private void initAnnotations() { + if ( annotations == null ) { + XMLContext.Default defaults = xmlContext.getDefault( className ); + if ( className != null && propertyName == null ) { + //is a class + ManagedType managedTypeOverride = xmlContext.getManagedTypeOverride( className ); + Annotation[] annotations = getPhysicalAnnotations(); + List annotationList = new ArrayList<>( annotations.length + 5 ); + annotationsMap = new HashMap<>( annotations.length + 5 ); + for ( Annotation annotation : annotations ) { + if ( !annotationToXml.containsKey( annotation.annotationType() ) ) { + //unknown annotations are left over + annotationList.add( annotation ); + } + } + addIfNotNull( annotationList, getEntity( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getMappedSuperclass( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getEmbeddable( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getTable( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getSecondaryTables( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getPrimaryKeyJoinColumns( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getIdClass( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getCacheable( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getInheritance( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getDiscriminatorValue( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getDiscriminatorColumn( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getSequenceGenerator( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getTableGenerator( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getNamedQueries( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getNamedNativeQueries( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getNamedStoredProcedureQueries( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getNamedEntityGraphs( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getSqlResultSetMappings( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getExcludeDefaultListeners( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getExcludeSuperclassListeners( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getAccessType( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getAttributeOverrides( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getAssociationOverrides( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getEntityListeners( managedTypeOverride, defaults ) ); + addIfNotNull( annotationList, getConverts( managedTypeOverride, defaults ) ); + + this.annotations = annotationList.toArray( new Annotation[annotationList.size()] ); + for ( Annotation ann : this.annotations ) { + annotationsMap.put( ann.annotationType(), ann ); + } + checkForOrphanProperties( managedTypeOverride ); + } + else if ( className != null ) { //&& propertyName != null ) { //always true but less confusing + ManagedType managedTypeOverride = xmlContext.getManagedTypeOverride( className ); + JaxbEntityListener entityListenerOverride = xmlContext.getEntityListenerOverride( className ); + Annotation[] annotations = getPhysicalAnnotations(); + List annotationList = new ArrayList<>( annotations.length + 5 ); + annotationsMap = new HashMap<>( annotations.length + 5 ); + for ( Annotation annotation : annotations ) { + if ( !annotationToXml.containsKey( annotation.annotationType() ) ) { + //unknown annotations are left over + annotationList.add( annotation ); + } + } + preCalculateElementsForProperty( managedTypeOverride, entityListenerOverride ); + Transient transientAnn = getTransient( defaults ); + if ( transientAnn != null ) { + annotationList.add( transientAnn ); + } + else { + if ( defaults.canUseJavaAnnotations() ) { + Annotation annotation = getPhysicalAnnotation( Access.class ); + addIfNotNull( annotationList, annotation ); + } + getId( annotationList, defaults ); + getEmbeddedId( annotationList, defaults ); + getEmbedded( annotationList, defaults ); + getBasic( annotationList, defaults ); + getVersion( annotationList, defaults ); + getManyToOne( annotationList, defaults ); + getOneToOne( annotationList, defaults ); + getOneToMany( annotationList, defaults ); + getManyToMany( annotationList, defaults ); + getAny( annotationList, defaults ); + getManyToAny( annotationList, defaults ); + getElementCollection( annotationList, defaults ); + addIfNotNull( annotationList, getSequenceGenerator( elementsForProperty, defaults ) ); + addIfNotNull( annotationList, getTableGenerator( elementsForProperty, defaults ) ); + addIfNotNull( annotationList, getConvertsForAttribute( elementsForProperty, defaults ) ); + } + processEventAnnotations( annotationList, defaults ); + //FIXME use annotationsMap rather than annotationList this will be faster since the annotation type is usually known at put() time + this.annotations = annotationList.toArray( new Annotation[annotationList.size()] ); + for ( Annotation ann : this.annotations ) { + annotationsMap.put( ann.annotationType(), ann ); + } + } + else { + this.annotations = getPhysicalAnnotations(); + annotationsMap = new HashMap<>( annotations.length + 5 ); + for ( Annotation ann : this.annotations ) { + annotationsMap.put( ann.annotationType(), ann ); + } + } + } + } + + private Annotation getConvertsForAttribute(PropertyMappingElementCollector elementsForProperty, XMLContext.Default defaults) { + // NOTE : we use a map here to make sure that an xml and annotation referring to the same attribute + // properly overrides. Very sparse map, yes, but easy setup. + // todo : revisit this + // although bear in mind that this code is no longer used in 5.0... + + final Map convertAnnotationsMap = new HashMap<>(); + + for ( JaxbBasic element : elementsForProperty.getBasic() ) { + JaxbConvert convert = element.getConvert(); + if ( convert != null ) { + applyXmlDefinedConverts( Collections.singletonList( convert ), defaults, null, + convertAnnotationsMap ); + } + } + for ( JaxbEmbedded element : elementsForProperty.getEmbedded() ) { + applyXmlDefinedConverts( element.getConvert(), defaults, propertyName, convertAnnotationsMap ); + } + for ( JaxbElementCollection element : elementsForProperty.getElementCollection() ) { + applyXmlDefinedConverts( element.getConvert(), defaults, propertyName, convertAnnotationsMap ); + } + + // NOTE : per section 12.2.3.16 of the spec is additive, although only if "metadata-complete" is not + // specified in the XML + + if ( defaults.canUseJavaAnnotations() ) { + // todo : note sure how to best handle attributeNamePrefix here + applyPhysicalConvertAnnotations( propertyName, convertAnnotationsMap ); + } + + if ( !convertAnnotationsMap.isEmpty() ) { + final AnnotationDescriptor groupingDescriptor = new AnnotationDescriptor( Converts.class ); + groupingDescriptor.setValue( "value", convertAnnotationsMap.values().toArray( new Convert[convertAnnotationsMap.size()]) ); + return AnnotationFactory.create( groupingDescriptor ); + } + + return null; + } + + private Converts getConverts(ManagedType root, XMLContext.Default defaults) { + // NOTE : we use a map here to make sure that an xml and annotation referring to the same attribute + // properly overrides. Bit sparse, but easy... + final Map convertAnnotationsMap = new HashMap<>(); + + if ( root instanceof JaxbEntity ) { + applyXmlDefinedConverts( ( (JaxbEntity) root ).getConvert(), defaults, null, convertAnnotationsMap ); + } + + // NOTE : per section 12.2.3.16 of the spec is additive, although only if "metadata-complete" is not + // specified in the XML + + if ( defaults.canUseJavaAnnotations() ) { + applyPhysicalConvertAnnotations( null, convertAnnotationsMap ); + } + + if ( !convertAnnotationsMap.isEmpty() ) { + final AnnotationDescriptor groupingDescriptor = new AnnotationDescriptor( Converts.class ); + groupingDescriptor.setValue( "value", convertAnnotationsMap.values().toArray( new Convert[convertAnnotationsMap.size()]) ); + return AnnotationFactory.create( groupingDescriptor ); + } + + return null; + } + + private void applyXmlDefinedConverts( + List elements, + XMLContext.Default defaults, + String attributeNamePrefix, + Map convertAnnotationsMap) { + for ( JaxbConvert convertElement : elements ) { + final AnnotationDescriptor convertAnnotationDescriptor = new AnnotationDescriptor( Convert.class ); + copyAttribute( convertAnnotationDescriptor, "attribute-name", convertElement.getAttributeName(), false ); + copyAttribute( convertAnnotationDescriptor, "disable-conversion", convertElement.isDisableConversion(), false ); + + final String converter = convertElement.getConverter(); + if ( converter != null ) { + final String converterClassName = XMLContext.buildSafeClassName( + converter, + defaults + ); + try { + final Class converterClass = classLoaderAccess.classForName( converterClassName ); + convertAnnotationDescriptor.setValue( "converter", converterClass ); + } + catch (ClassLoadingException e) { + throw new AnnotationException( "Unable to find specified converter class id-class: " + converterClassName, e ); + } + } + final Convert convertAnnotation = AnnotationFactory.create( convertAnnotationDescriptor ); + final String qualifiedAttributeName = qualifyConverterAttributeName( + attributeNamePrefix, + convertAnnotation.attributeName() + ); + convertAnnotationsMap.put( qualifiedAttributeName, convertAnnotation ); + } + + } + + private String qualifyConverterAttributeName(String attributeNamePrefix, String specifiedAttributeName) { + String qualifiedAttributeName; + if ( StringHelper.isNotEmpty( specifiedAttributeName ) ) { + if ( StringHelper.isNotEmpty( attributeNamePrefix ) ) { + qualifiedAttributeName = attributeNamePrefix + '.' + specifiedAttributeName; + } + else { + qualifiedAttributeName = specifiedAttributeName; + } + } + else { + qualifiedAttributeName = ""; + } + return qualifiedAttributeName; + } + + private void applyPhysicalConvertAnnotations( + String attributeNamePrefix, + Map convertAnnotationsMap) { + final Convert physicalAnnotation = getPhysicalAnnotation( Convert.class ); + if ( physicalAnnotation != null ) { + // only add if no XML element named a converter for this attribute + final String qualifiedAttributeName = qualifyConverterAttributeName( attributeNamePrefix, physicalAnnotation.attributeName() ); + if ( ! convertAnnotationsMap.containsKey( qualifiedAttributeName ) ) { + convertAnnotationsMap.put( qualifiedAttributeName, physicalAnnotation ); + } + } + final Converts physicalGroupingAnnotation = getPhysicalAnnotation( Converts.class ); + if ( physicalGroupingAnnotation != null ) { + for ( Convert convertAnnotation : physicalGroupingAnnotation.value() ) { + // again, only add if no XML element named a converter for this attribute + final String qualifiedAttributeName = qualifyConverterAttributeName( attributeNamePrefix, convertAnnotation.attributeName() ); + if ( ! convertAnnotationsMap.containsKey( qualifiedAttributeName ) ) { + convertAnnotationsMap.put( qualifiedAttributeName, convertAnnotation ); + } + } + } + } + + private void checkForOrphanProperties(ManagedType root) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( className ); + } + catch ( ClassLoadingException e ) { + return; //a primitive type most likely + } + AttributesContainer container = root != null ? root.getAttributes() : null; + //put entity.attributes elements + if ( container != null ) { + //precompute the list of properties + //TODO is it really useful... + Set properties = new HashSet<>(); + for ( Field field : clazz.getFields() ) { + properties.add( field.getName() ); + } + for ( Method method : clazz.getMethods() ) { + String name = method.getName(); + if ( name.startsWith( "get" ) ) { + properties.add( Introspector.decapitalize( name.substring( "get".length() ) ) ); + } + else if ( name.startsWith( "is" ) ) { + properties.add( Introspector.decapitalize( name.substring( "is".length() ) ) ); + } + } + if ( container instanceof JaxbAttributes ) { + JaxbAttributes jaxbAttributes = (JaxbAttributes) container; + checkForOrphanProperties( jaxbAttributes.getId(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( jaxbAttributes.getEmbeddedId(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( jaxbAttributes.getVersion(), properties, PERSISTENT_ATTRIBUTE_NAME ); + } + checkForOrphanProperties( container.getBasic(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getManyToOne(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getOneToMany(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getOneToOne(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getManyToMany(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getElementCollection(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getEmbedded(), properties, PERSISTENT_ATTRIBUTE_NAME ); + checkForOrphanProperties( container.getTransient(), properties, JAXB_TRANSIENT_NAME ); + } + } + + private void checkForOrphanProperties(List elements, Set properties, + Function nameGetter) { + for ( T element : elements ) { + checkForOrphanProperties( element, properties, nameGetter ); + } + } + + private void checkForOrphanProperties(T element, Set properties, + Function nameGetter) { + if ( element == null ) { + return; + } + String propertyName = nameGetter.apply( element ); + if ( !properties.contains( propertyName ) ) { + LOG.propertyNotFound( StringHelper.qualify( className, propertyName ) ); + } + } + + /** + * Adds {@code annotation} to the list (only if it's not null) and then returns it. + * + * @param annotationList The list of annotations. + * @param annotation The annotation to add to the list. + * + * @return The annotation which was added to the list or {@code null}. + */ + private Annotation addIfNotNull(List annotationList, Annotation annotation) { + if ( annotation != null ) { + annotationList.add( annotation ); + } + return annotation; + } + + //TODO mutualize the next 2 methods + private Annotation getTableGenerator(PropertyMappingElementCollector elementsForProperty, XMLContext.Default defaults) { + for ( JaxbId element : elementsForProperty.getId() ) { + JaxbTableGenerator subelement = element.getTableGenerator(); + if ( subelement != null ) { + return buildTableGeneratorAnnotation( subelement, defaults ); + } + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( TableGenerator.class ); + } + else { + return null; + } + } + + private Annotation getSequenceGenerator(PropertyMappingElementCollector elementsForProperty, XMLContext.Default defaults) { + for ( JaxbId element : elementsForProperty.getId() ) { + JaxbSequenceGenerator subelement = element.getSequenceGenerator(); + if ( subelement != null ) { + return buildSequenceGeneratorAnnotation( subelement ); + } + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( SequenceGenerator.class ); + } + else { + return null; + } + } + + private void processEventAnnotations(List annotationList, XMLContext.Default defaults) { + boolean eventElement = false; + if ( !elementsForProperty.getPrePersist().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PrePersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPreRemove().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPreUpdate().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPostPersist().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostPersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPostRemove().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPostUpdate().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( !elementsForProperty.getPostLoad().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostLoad.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + if ( !eventElement && defaults.canUseJavaAnnotations() ) { + Annotation ann = getPhysicalAnnotation( PrePersist.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PreRemove.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PreUpdate.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PostPersist.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PostRemove.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PostUpdate.class ); + addIfNotNull( annotationList, ann ); + ann = getPhysicalAnnotation( PostLoad.class ); + addIfNotNull( annotationList, ann ); + } + } + + private EntityListeners getEntityListeners(ManagedType root, XMLContext.Default defaults) { + JaxbEntityListeners element = root instanceof EntityOrMappedSuperclass ? ( (EntityOrMappedSuperclass) root ).getEntityListeners() : null; + if ( element != null ) { + List entityListenerClasses = new ArrayList<>(); + for ( JaxbEntityListener subelement : element.getEntityListener() ) { + String className = subelement.getClazz(); + try { + entityListenerClasses.add( + classLoaderAccess.classForName( + XMLContext.buildSafeClassName( className, defaults ) + ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( + "Unable to find class: " + className, e + ); + } + } + AnnotationDescriptor ad = new AnnotationDescriptor( EntityListeners.class ); + ad.setValue( "value", entityListenerClasses.toArray( new Class[entityListenerClasses.size()] ) ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( EntityListeners.class ); + } + else { + return null; + } + } + + private JoinTable overridesDefaultsInJoinTable(Annotation annotation, XMLContext.Default defaults) { + //no element but might have some default or some annotation + boolean defaultToJoinTable = !( isPhysicalAnnotationPresent( JoinColumn.class ) + || isPhysicalAnnotationPresent( JoinColumns.class ) ); + final Class annotationClass = annotation.annotationType(); + defaultToJoinTable = defaultToJoinTable && + ( ( annotationClass == ManyToMany.class && StringHelper.isEmpty( ( (ManyToMany) annotation ).mappedBy() ) ) + || ( annotationClass == OneToMany.class && StringHelper.isEmpty( ( (OneToMany) annotation ).mappedBy() ) ) + || ( annotationClass == ElementCollection.class ) + ); + final Class annotationType = JoinTable.class; + if ( defaultToJoinTable + && ( StringHelper.isNotEmpty( defaults.getCatalog() ) + || StringHelper.isNotEmpty( defaults.getSchema() ) ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + if ( defaults.canUseJavaAnnotations() ) { + JoinTable table = getPhysicalAnnotation( annotationType ); + if ( table != null ) { + ad.setValue( "name", table.name() ); + ad.setValue( "schema", table.schema() ); + ad.setValue( "catalog", table.catalog() ); + ad.setValue( "uniqueConstraints", table.uniqueConstraints() ); + ad.setValue( "joinColumns", table.joinColumns() ); + ad.setValue( "inverseJoinColumns", table.inverseJoinColumns() ); + } + } + if ( StringHelper.isEmpty( (String) ad.valueOf( "schema" ) ) + && StringHelper.isNotEmpty( defaults.getSchema() ) ) { + ad.setValue( "schema", defaults.getSchema() ); + } + if ( StringHelper.isEmpty( (String) ad.valueOf( "catalog" ) ) + && StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + ad.setValue( "catalog", defaults.getCatalog() ); + } + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( annotationType ); + } + else { + return null; + } + } + + private Annotation overridesDefaultCascadePersist(Annotation annotation, XMLContext.Default defaults) { + if ( Boolean.TRUE.equals( defaults.getCascadePersist() ) ) { + final Class annotationType = annotation.annotationType(); + + if ( annotationType == ManyToOne.class ) { + ManyToOne manyToOne = (ManyToOne) annotation; + List cascades = new ArrayList<>( Arrays.asList( manyToOne.cascade() ) ); + if ( !cascades.contains( CascadeType.ALL ) && !cascades.contains( CascadeType.PERSIST ) ) { + cascades.add( CascadeType.PERSIST ); + } + else { + return annotation; + } + + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + ad.setValue( "cascade", cascades.toArray( new CascadeType[] {} ) ); + ad.setValue( "targetEntity", manyToOne.targetEntity() ); + ad.setValue( "fetch", manyToOne.fetch() ); + ad.setValue( "optional", manyToOne.optional() ); + + return AnnotationFactory.create( ad ); + } + else if ( annotationType == OneToOne.class ) { + OneToOne oneToOne = (OneToOne) annotation; + List cascades = new ArrayList<>( Arrays.asList( oneToOne.cascade() ) ); + if ( !cascades.contains( CascadeType.ALL ) && !cascades.contains( CascadeType.PERSIST ) ) { + cascades.add( CascadeType.PERSIST ); + } + else { + return annotation; + } + + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + ad.setValue( "cascade", cascades.toArray( new CascadeType[] {} ) ); + ad.setValue( "targetEntity", oneToOne.targetEntity() ); + ad.setValue( "fetch", oneToOne.fetch() ); + ad.setValue( "optional", oneToOne.optional() ); + ad.setValue( "mappedBy", oneToOne.mappedBy() ); + ad.setValue( "orphanRemoval", oneToOne.orphanRemoval() ); + + return AnnotationFactory.create( ad ); + } + } + return annotation; + } + + private void getJoinTable(List annotationList, AssociationAttribute associationAttribute, + XMLContext.Default defaults) { + addIfNotNull( annotationList, buildJoinTable( associationAttribute.getJoinTable(), defaults ) ); + } + + /* + * no partial overriding possible + */ + private JoinTable buildJoinTable(JaxbJoinTable subelement, XMLContext.Default defaults) { + final Class annotationType = JoinTable.class; + if ( subelement == null ) { + return null; + } + //ignore java annotation, an element is defined + AnnotationDescriptor annotation = new AnnotationDescriptor( annotationType ); + copyAttribute( annotation, "name", subelement.getName(), false ); + copyAttribute( annotation, "catalog", subelement.getCatalog(), false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyAttribute( annotation, "schema", subelement.getSchema(), false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, subelement.getUniqueConstraint() ); + buildIndex( annotation, subelement.getIndex() ); + annotation.setValue( "joinColumns", getJoinColumns( subelement.getJoinColumn(), false ) ); + annotation.setValue( "inverseJoinColumns", getJoinColumns( subelement.getInverseJoinColumn(), true ) ); + return AnnotationFactory.create( annotation ); + } + + private void getOneToMany(List annotationList, XMLContext.Default defaults) { + Class annotationType = OneToMany.class; + List elements = elementsForProperty.getOneToMany(); + for ( JaxbOneToMany element : elements ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + addTargetClass( element.getTargetEntity(), ad, "target-entity", defaults ); + getFetchType( ad, element.getFetch() ); + getCascades( ad, element.getCascade(), defaults ); + getJoinTable( annotationList, element, defaults ); + buildJoinColumns( annotationList, element.getJoinColumn() ); + copyAttribute( ad, "orphan-removal", element.isOrphanRemoval(), false ); + copyAttribute( ad, "mapped-by", element.getMappedBy(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + + getOrderBy( annotationList, element.getOrderBy() ); + getMapKey( annotationList, element.getMapKey() ); + getMapKeyClass( annotationList, element.getMapKeyClass(), defaults ); + getMapKeyColumn( annotationList, element.getMapKeyColumn() ); + getOrderColumn( annotationList, element.getOrderColumn() ); + getMapKeyTemporal( annotationList, element.getMapKeyTemporal() ); + getMapKeyEnumerated( annotationList, element.getMapKeyEnumerated() ); + Annotation annotation = getMapKeyAttributeOverrides( element.getMapKeyAttributeOverride(), defaults ); + addIfNotNull( annotationList, annotation ); + getMapKeyJoinColumns( annotationList, element.getMapKeyJoinColumn() ); + getAccessType( annotationList, element.getAccess() ); + } + afterGetAssociation( annotationType, annotationList, defaults ); + } + + /** + * As per section 12.2 of the JPA 2.0 specification, the association + * subelements (many-to-one, one-to-many, one-to-one, many-to-many, + * element-collection) completely override the mapping for the specified + * field or property. Thus, any methods which might in some contexts merge + * with annotations must not do so in this context. + * + * @see #getElementCollection(List, XMLContext.Default) + */ + private void getOneToOne(List annotationList, XMLContext.Default defaults) { + Class annotationType = OneToOne.class; + List elements = elementsForProperty.getOneToOne(); + for ( JaxbOneToOne element : elements ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + addTargetClass( element.getTargetEntity(), ad, "target-entity", defaults ); + getFetchType( ad, element.getFetch() ); + getCascades( ad, element.getCascade(), defaults ); + getJoinTable( annotationList, element, defaults ); + buildJoinColumns( annotationList, element.getJoinColumn() ); + Annotation annotation = getPrimaryKeyJoinColumns( element.getPrimaryKeyJoinColumn(), defaults, false ); + addIfNotNull( annotationList, annotation ); + copyAttribute( ad, "optional", element.isOptional(), false ); + copyAttribute( ad, "orphan-removal", element.isOrphanRemoval(), false ); + copyAttribute( ad, "mapped-by", element.getMappedBy(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + + getAssociationId( annotationList, element.isId() ); + getMapsId( annotationList, element.getMapsId() ); + getAccessType( annotationList, element.getAccess() ); + } + afterGetAssociation( annotationType, annotationList, defaults ); + } + + /** + * @see #getOneToOne(List, XMLContext.Default) + * @see #getElementCollection(List, XMLContext.Default) + */ + private void getManyToOne(List annotationList, XMLContext.Default defaults) { + Class annotationType = ManyToOne.class; + List elements = elementsForProperty.getManyToOne(); + for ( JaxbManyToOne element : elements ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + addTargetClass( element.getTargetEntity(), ad, "target-entity", defaults ); + getFetchType( ad, element.getFetch() ); + getCascades( ad, element.getCascade(), defaults ); + getJoinTable( annotationList, element, defaults ); + buildJoinColumns( annotationList, element.getJoinColumn() ); + copyAttribute( ad, "optional", element.isOptional(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + + getAssociationId( annotationList, element.isId() ); + getMapsId( annotationList, element.getMapsId() ); + getAccessType( annotationList, element.getAccess() ); + } + afterGetAssociation( annotationType, annotationList, defaults ); + } + + /** + * @see #getOneToOne(List, XMLContext.Default) + * @see #getElementCollection(List, XMLContext.Default) + */ + private void getManyToMany(List annotationList, XMLContext.Default defaults) { + Class annotationType = ManyToMany.class; + List elements = elementsForProperty.getManyToMany(); + for ( JaxbManyToMany element : elements ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + addTargetClass( element.getTargetEntity(), ad, "target-entity", defaults ); + getFetchType( ad, element.getFetch() ); + getCascades( ad, element.getCascade(), defaults ); + getJoinTable( annotationList, element, defaults ); + copyAttribute( ad, "mapped-by", element.getMappedBy(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + + getOrderBy( annotationList, element.getOrderBy() ); + getMapKey( annotationList, element.getMapKey() ); + getMapKeyClass( annotationList, element.getMapKeyClass(), defaults ); + getMapKeyColumn( annotationList, element.getMapKeyColumn() ); + getOrderColumn( annotationList, element.getOrderColumn() ); + getMapKeyTemporal( annotationList, element.getMapKeyTemporal() ); + getMapKeyEnumerated( annotationList, element.getMapKeyEnumerated() ); + Annotation annotation = getMapKeyAttributeOverrides( element.getMapKeyAttributeOverride(), defaults ); + addIfNotNull( annotationList, annotation ); + getMapKeyJoinColumns( annotationList, element.getMapKeyJoinColumn() ); + getAccessType( annotationList, element.getAccess() ); + } + afterGetAssociation( annotationType, annotationList, defaults ); + } + + private void getAny(List annotationList, XMLContext.Default defaults) { + // No support for "any" in JPA's orm.xml; we will just use the "physical" annotations. + // TODO HHH-10176 We should allow "any" associations, but the JPA XSD doesn't allow that. We would need our own XSD. + afterGetAssociation( Any.class, annotationList, defaults ); + } + + private void getManyToAny(List annotationList, XMLContext.Default defaults) { + // No support for "many-to-any" in JPA's orm.xml; we will just use the annotations. + // TODO HHH-10176 We should allow "many-to-any" associations, but the JPA XSD doesn't allow that. We would need our own XSD. + afterGetAssociation( ManyToAny.class, annotationList, defaults ); + } + + private void afterGetAssociation(Class annotationType, List annotationList, + XMLContext.Default defaults) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + Annotation annotation = getPhysicalAnnotation( annotationType ); + if ( annotation != null ) { + annotation = overridesDefaultCascadePersist( annotation, defaults ); + annotationList.add( annotation ); + annotation = overridesDefaultsInJoinTable( annotation, defaults ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( JoinColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( JoinColumns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( PrimaryKeyJoinColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( PrimaryKeyJoinColumns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKey.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( OrderBy.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Lob.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Enumerated.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Columns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyClass.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyTemporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyEnumerated.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyJoinColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyJoinColumns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( OrderColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Cascade.class ); + addIfNotNull( annotationList, annotation ); + } + else if ( isPhysicalAnnotationPresent( ElementCollection.class ) ) { //JPA2 + annotation = overridesDefaultsInJoinTable( getPhysicalAnnotation( ElementCollection.class ), defaults ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKey.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( OrderBy.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Lob.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Enumerated.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( OrderColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyClass.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyTemporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyEnumerated.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyJoinColumn.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( MapKeyJoinColumns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( CollectionTable.class ); + addIfNotNull( annotationList, annotation ); + } + } + } + + private void getMapKeyJoinColumns(List annotationList, List elements) { + MapKeyJoinColumn[] joinColumns = buildMapKeyJoinColumns( elements ); + if ( joinColumns.length > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyJoinColumns.class ); + ad.setValue( "value", joinColumns ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private MapKeyJoinColumn[] buildMapKeyJoinColumns(List elements) { + List joinColumns = new ArrayList<>(); + if ( elements != null ) { + for ( JaxbMapKeyJoinColumn element : elements ) { + AnnotationDescriptor column = new AnnotationDescriptor( MapKeyJoinColumn.class ); + copyAttribute( column, "name", element.getName(), false ); + copyAttribute( column, "referenced-column-name", element.getReferencedColumnName(), false ); + copyAttribute( column, "unique", element.isUnique(), false ); + copyAttribute( column, "nullable", element.isNullable(), false ); + copyAttribute( column, "insertable", element.isInsertable(), false ); + copyAttribute( column, "updatable", element.isUpdatable(), false ); + copyAttribute( column, "column-definition", element.getColumnDefinition(), false ); + copyAttribute( column, "table", element.getTable(), false ); + joinColumns.add( AnnotationFactory.create( column ) ); + } + } + return joinColumns.toArray( new MapKeyJoinColumn[joinColumns.size()] ); + } + + private AttributeOverrides getMapKeyAttributeOverrides(List elements, XMLContext.Default defaults) { + List attributes = buildAttributeOverrides( elements, "map-key-attribute-override" ); + return mergeAttributeOverrides( defaults, attributes, false ); + } + + private Cacheable getCacheable(ManagedType root, XMLContext.Default defaults){ + if ( root instanceof JaxbEntity ) { + Boolean attValue = ( (JaxbEntity) root ).isCacheable(); + if ( attValue != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Cacheable.class ); + ad.setValue( "value", attValue ); + return AnnotationFactory.create( ad ); + } + } + if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Cacheable.class ); + } + else { + return null; + } + } + /** + * Adds a @MapKeyEnumerated annotation to the specified annotationList if the specified element + * contains a map-key-enumerated sub-element. This should only be the case for + * element-collection, many-to-many, or one-to-many associations. + */ + private void getMapKeyEnumerated(List annotationList, EnumType enumType) { + if ( enumType != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyEnumerated.class ); + ad.setValue( "value", enumType ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + /** + * Adds a @MapKeyTemporal annotation to the specified annotationList if the specified element + * contains a map-key-temporal sub-element. This should only be the case for element-collection, + * many-to-many, or one-to-many associations. + */ + private void getMapKeyTemporal(List annotationList, TemporalType temporalType) { + if ( temporalType != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyTemporal.class ); + ad.setValue( "value", temporalType ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + /** + * Adds an @OrderColumn annotation to the specified annotationList if the specified element + * contains an order-column sub-element. This should only be the case for element-collection, + * many-to-many, or one-to-many associations. + */ + private void getOrderColumn(List annotationList, JaxbOrderColumn element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( OrderColumn.class ); + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "nullable", element.isNullable(), false ); + copyAttribute( ad, "insertable", element.isInsertable(), false ); + copyAttribute( ad, "updatable", element.isUpdatable(), false ); + copyAttribute( ad, "column-definition", element.getColumnDefinition(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + /** + * Adds a @MapsId annotation to the specified annotationList if the specified element has the + * maps-id attribute set. This should only be the case for many-to-one or one-to-one + * associations. + */ + private void getMapsId(List annotationList, String mapsId) { + if ( mapsId != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapsId.class ); + ad.setValue( "value", mapsId ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + /** + * Adds an @Id annotation to the specified annotationList if the specified element has the id + * attribute set to true. This should only be the case for many-to-one or one-to-one + * associations. + */ + private void getAssociationId(List annotationList, Boolean isId) { + if ( Boolean.TRUE.equals( isId ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Id.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void addTargetClass(String className, AnnotationDescriptor ad, String nodeName, XMLContext.Default defaults) { + if ( className != null ) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( className, defaults ) ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( + "Unable to find " + nodeName + ": " + className, e + ); + } + ad.setValue( getJavaAttributeNameFromXMLOne( nodeName ), clazz ); + } + } + + /** + * As per sections 12.2.3.23.9, 12.2.4.8.9 and 12.2.5.3.6 of the JPA 2.0 + * specification, the element-collection subelement completely overrides the + * mapping for the specified field or property. Thus, any methods which + * might in some contexts merge with annotations must not do so in this + * context. + */ + private void getElementCollection(List annotationList, XMLContext.Default defaults) { + for ( JaxbElementCollection element : elementsForProperty.getElementCollection() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( ElementCollection.class ); + addTargetClass( element.getTargetClass(), ad, "target-class", defaults ); + getFetchType( ad, element.getFetch() ); + getOrderBy( annotationList, element.getOrderBy() ); + getOrderColumn( annotationList, element.getOrderColumn() ); + getMapKey( annotationList, element.getMapKey() ); + getMapKeyClass( annotationList, element.getMapKeyClass(), defaults ); + getMapKeyTemporal( annotationList, element.getMapKeyTemporal() ); + getMapKeyEnumerated( annotationList, element.getMapKeyEnumerated() ); + getMapKeyColumn( annotationList, element.getMapKeyColumn() ); + getMapKeyJoinColumns( annotationList, element.getMapKeyJoinColumn() ); + Annotation annotation = getColumn( element.getColumn(), false, "element-collection" ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element.getTemporal() ); + getEnumerated( annotationList, element.getEnumerated() ); + getLob( annotationList, element.getLob() ); + //Both map-key-attribute-overrides and attribute-overrides + //translate into AttributeOverride annotations, which need + //need to be wrapped in the same AttributeOverrides annotation. + List attributes = new ArrayList<>(); + attributes.addAll( buildAttributeOverrides( element.getMapKeyAttributeOverride(), "map-key-attribute-override" ) ); + attributes.addAll( buildAttributeOverrides( element.getAttributeOverride(), "attribute-override" ) ); + annotation = mergeAttributeOverrides( defaults, attributes, false ); + addIfNotNull( annotationList, annotation ); + annotation = getAssociationOverrides( element.getAssociationOverride(), defaults, false ); + addIfNotNull( annotationList, annotation ); + getCollectionTable( annotationList, element.getCollectionTable(), defaults ); + annotationList.add( AnnotationFactory.create( ad ) ); + getAccessType( annotationList, element.getAccess() ); + } + } + + private void getOrderBy(List annotationList, String orderBy) { + if ( orderBy != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( OrderBy.class ); + ad.setValue( "value", orderBy ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKey(List annotationList, JaxbMapKey element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKey.class ); + copyAttribute( ad, "name", element.getName(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKeyColumn(List annotationList, JaxbMapKeyColumn element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyColumn.class ); + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "unique", element.isUnique(), false ); + copyAttribute( ad, "nullable", element.isNullable(), false ); + copyAttribute( ad, "insertable", element.isInsertable(), false ); + copyAttribute( ad, "updatable", element.isUpdatable(), false ); + copyAttribute( ad, "column-definition", element.getColumnDefinition(), false ); + copyAttribute( ad, "table", element.getTable(), false ); + copyAttribute( ad, "length", element.getLength(), false ); + copyAttribute( ad, "precision", element.getPrecision(), false ); + copyAttribute( ad, "scale", element.getScale(), false ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKeyClass(List annotationList, JaxbMapKeyClass element, XMLContext.Default defaults) { + String nodeName = "map-key-class"; + if ( element != null ) { + String mapKeyClassName = element.getClazz(); + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyClass.class ); + if ( StringHelper.isNotEmpty( mapKeyClassName ) ) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( + XMLContext.buildSafeClassName( mapKeyClassName, defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( + "Unable to find " + nodeName + ": " + mapKeyClassName, e + ); + } + ad.setValue( "value", clazz ); + } + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getCollectionTable(List annotationList, JaxbCollectionTable element, XMLContext.Default defaults) { + if ( element != null ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( CollectionTable.class ); + copyAttribute( annotation, "name", element.getName(), false ); + copyAttribute( annotation, "catalog", element.getCatalog(), false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyAttribute( annotation, "schema", element.getSchema(), false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + JoinColumn[] joinColumns = getJoinColumns( element.getJoinColumn(), false ); + if ( joinColumns.length > 0 ) { + annotation.setValue( "joinColumns", joinColumns ); + } + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); + annotationList.add( AnnotationFactory.create( annotation ) ); + } + } + + private void buildJoinColumns(List annotationList, List elements) { + JoinColumn[] joinColumns = getJoinColumns( elements, false ); + if ( joinColumns.length > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( JoinColumns.class ); + ad.setValue( "value", joinColumns ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getCascades(AnnotationDescriptor ad, JaxbCascadeType element, XMLContext.Default defaults) { + List cascades = new ArrayList<>(); + if ( element != null ) { + if ( element.getCascadeAll() != null ) { + cascades.add( CascadeType.ALL ); + } + if ( element.getCascadePersist() != null ) { + cascades.add( CascadeType.PERSIST ); + } + if ( element.getCascadeMerge() != null ) { + cascades.add( CascadeType.MERGE ); + } + if ( element.getCascadeRemove() != null ) { + cascades.add( CascadeType.REMOVE ); + } + if ( element.getCascadeRefresh() != null ) { + cascades.add( CascadeType.REFRESH ); + } + if ( element.getCascadeDetach() != null ) { + cascades.add( CascadeType.DETACH ); + } + } + if ( Boolean.TRUE.equals( defaults.getCascadePersist() ) + && !cascades.contains( CascadeType.ALL ) && !cascades.contains( CascadeType.PERSIST ) ) { + cascades.add( CascadeType.PERSIST ); + } + if ( cascades.size() > 0 ) { + ad.setValue( "cascade", cascades.toArray( new CascadeType[cascades.size()] ) ); + } + } + + private void getEmbedded(List annotationList, XMLContext.Default defaults) { + for ( JaxbEmbedded element : elementsForProperty.getEmbedded() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Embedded.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + Annotation annotation = getAttributeOverrides( element.getAttributeOverride(), defaults, false ); + addIfNotNull( annotationList, annotation ); + annotation = getAssociationOverrides( element.getAssociationOverride(), defaults, false ); + addIfNotNull( annotationList, annotation ); + getAccessType( annotationList, element.getAccess() ); + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + Annotation annotation = getPhysicalAnnotation( Embedded.class ); + if ( annotation != null ) { + annotationList.add( annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + } + } + } + + private Transient getTransient(XMLContext.Default defaults) { + if ( !elementsForProperty.getTransient().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Transient.class ); + return AnnotationFactory.create( ad ); + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Transient.class ); + } + else { + return null; + } + } + + private void getVersion(List annotationList, XMLContext.Default defaults) { + for ( JaxbVersion element : elementsForProperty.getVersion() ) { + Annotation annotation = buildColumns( element.getColumn(), "version" ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element.getTemporal() ); + AnnotationDescriptor basic = new AnnotationDescriptor( Version.class ); + annotationList.add( AnnotationFactory.create( basic ) ); + getAccessType( annotationList, element.getAccess() ); + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + //we have nothing, so Java annotations might occur + Annotation annotation = getPhysicalAnnotation( Version.class ); + if ( annotation != null ) { + annotationList.add( annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Columns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + } + } + } + + private void getBasic(List annotationList, XMLContext.Default defaults) { + for ( JaxbBasic element : elementsForProperty.getBasic() ) { + Annotation annotation = buildColumns( element.getColumn(), "basic" ); + addIfNotNull( annotationList, annotation ); + getAccessType( annotationList, element.getAccess() ); + getTemporal( annotationList, element.getTemporal() ); + getLob( annotationList, element.getLob() ); + getEnumerated( annotationList, element.getEnumerated() ); + AnnotationDescriptor basic = new AnnotationDescriptor( Basic.class ); + getFetchType( basic, element.getFetch() ); + copyAttribute( basic, "optional", element.isOptional(), false ); + annotationList.add( AnnotationFactory.create( basic ) ); + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + //no annotation presence constraint, basic is the default + Annotation annotation = getPhysicalAnnotation( Basic.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Lob.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Enumerated.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Columns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + } + } + + private void getEnumerated(List annotationList, EnumType enumType) { + if ( enumType != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Enumerated.class ); + ad.setValue( "value", enumType ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getLob(List annotationList, JaxbLob element) { + if ( element != null ) { + annotationList.add( AnnotationFactory.create( new AnnotationDescriptor( Lob.class ) ) ); + } + } + + private void getFetchType(AnnotationDescriptor descriptor, FetchType type) { + if ( type != null ) { + descriptor.setValue( "fetch", type ); + } + } + + private void getEmbeddedId(List annotationList, XMLContext.Default defaults) { + for ( JaxbEmbeddedId element : elementsForProperty.getEmbeddedId() ) { + if ( isProcessingId( defaults ) ) { + Annotation annotation = getAttributeOverrides( element.getAttributeOverride(), defaults, false ); + addIfNotNull( annotationList, annotation ); + // TODO HHH-10176 We should allow association overrides here, but the JPA XSD doesn't allow that. We would need our own XSD. + AnnotationDescriptor ad = new AnnotationDescriptor( EmbeddedId.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + getAccessType( annotationList, element.getAccess() ); + } + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + Annotation annotation = getPhysicalAnnotation( EmbeddedId.class ); + if ( annotation != null ) { + annotationList.add( annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Columns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( GeneratedValue.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( TableGenerator.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( SequenceGenerator.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + } + } + } + + private void preCalculateElementsForProperty(ManagedType managedType, JaxbEntityListener entityListener) { + elementsForProperty = new PropertyMappingElementCollector( propertyName ); + AttributesContainer attributes = managedType == null ? null : managedType.getAttributes(); + //put entity.attributes elements + if ( attributes != null ) { + elementsForProperty.collectPersistentAttributesIfMatching( attributes ); + } + //add pre-* etc from entity and pure entity listener classes + if ( managedType instanceof LifecycleCallbackContainer ) { + elementsForProperty.collectLifecycleCallbacksIfMatching( (LifecycleCallbackContainer) managedType ); + } + if ( entityListener != null ) { + elementsForProperty.collectLifecycleCallbacksIfMatching( entityListener ); + } + } + + private void getId(List annotationList, XMLContext.Default defaults) { + for ( JaxbId element : elementsForProperty.getId() ) { + boolean processId = isProcessingId( defaults ); + if ( processId ) { + Annotation annotation = buildColumns( element.getColumn(), "id" ); + addIfNotNull( annotationList, annotation ); + annotation = buildGeneratedValue( element.getGeneratedValue() ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element.getTemporal() ); + //FIXME: fix the priority of xml over java for generator names + annotation = getTableGenerator( element.getTableGenerator(), defaults ); + addIfNotNull( annotationList, annotation ); + annotation = getSequenceGenerator( element.getSequenceGenerator(), defaults ); + addIfNotNull( annotationList, annotation ); + AnnotationDescriptor id = new AnnotationDescriptor( Id.class ); + annotationList.add( AnnotationFactory.create( id ) ); + getAccessType( annotationList, element.getAccess() ); + } + } + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { + Annotation annotation = getPhysicalAnnotation( Id.class ); + if ( annotation != null ) { + annotationList.add( annotation ); + annotation = getPhysicalAnnotation( Column.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Columns.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( GeneratedValue.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( Temporal.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( TableGenerator.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( SequenceGenerator.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AttributeOverrides.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverride.class ); + addIfNotNull( annotationList, annotation ); + annotation = getPhysicalAnnotation( AssociationOverrides.class ); + addIfNotNull( annotationList, annotation ); + } + } + } + + private boolean isProcessingId(XMLContext.Default defaults) { + boolean isExplicit = defaults.getAccess() != null; + boolean correctAccess = + ( PropertyType.PROPERTY.equals( propertyType ) && AccessType.PROPERTY.equals( defaults.getAccess() ) ) + || ( PropertyType.FIELD.equals( propertyType ) && AccessType.FIELD + .equals( defaults.getAccess() ) ); + boolean hasId = defaults.canUseJavaAnnotations() + && ( isPhysicalAnnotationPresent( Id.class ) || isPhysicalAnnotationPresent( EmbeddedId.class ) ); + //if ( properAccessOnMetadataComplete || properOverridingOnMetadataNonComplete ) { + boolean mirrorAttributeIsId = defaults.canUseJavaAnnotations() && + ( mirroredAttribute != null && + ( mirroredAttribute.isAnnotationPresent( Id.class ) + || mirroredAttribute.isAnnotationPresent( EmbeddedId.class ) ) ); + boolean propertyIsDefault = PropertyType.PROPERTY.equals( propertyType ) + && !mirrorAttributeIsId; + return correctAccess || ( !isExplicit && hasId ) || ( !isExplicit && propertyIsDefault ); + } + + private Columns buildColumns(JaxbColumn element, String nodeName) { + if ( element == null ) { + return null; + } + List columns = new ArrayList<>( 1 ); + columns.add( getColumn( element, false, nodeName ) ); + if ( columns.size() > 0 ) { + AnnotationDescriptor columnsDescr = new AnnotationDescriptor( Columns.class ); + columnsDescr.setValue( "columns", columns.toArray( new Column[columns.size()] ) ); + return AnnotationFactory.create( columnsDescr ); + } + else { + return null; + } + } + + private Columns buildColumns(List elements, String nodeName) { + List columns = new ArrayList<>( elements.size() ); + for ( JaxbColumn element : elements ) { + columns.add( getColumn( element, false, nodeName ) ); + } + if ( columns.size() > 0 ) { + AnnotationDescriptor columnsDescr = new AnnotationDescriptor( Columns.class ); + columnsDescr.setValue( "columns", columns.toArray( new Column[columns.size()] ) ); + return AnnotationFactory.create( columnsDescr ); + } + else { + return null; + } + } + + private GeneratedValue buildGeneratedValue(JaxbGeneratedValue element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( GeneratedValue.class ); + GenerationType strategy = element.getStrategy(); + if ( strategy != null ) { + ad.setValue( "strategy", strategy ); + } + copyAttribute( ad, "generator", element.getGenerator(), false ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void getTemporal(List annotationList, TemporalType type) { + if ( type != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Temporal.class ); + ad.setValue( "value", type ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getAccessType(List annotationList, AccessType type) { + if ( element == null ) { + return; + } + if ( type != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); + + if ( ( AccessType.PROPERTY.equals( type ) && this.element instanceof Method ) || + ( AccessType.FIELD.equals( type ) && this.element instanceof Field ) ) { + return; + } + + ad.setValue( "value", type ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private AssociationOverrides getAssociationOverrides(ManagedType root, XMLContext.Default defaults) { + return root instanceof JaxbEntity + ? getAssociationOverrides( ( (JaxbEntity) root ).getAssociationOverride(), defaults, true ) + : null; + } + + /** + * @param mergeWithAnnotations Whether to use Java annotations for this + * element, if present and not disabled by the XMLContext defaults. + * In some contexts (such as an element-collection mapping) merging + */ + private AssociationOverrides getAssociationOverrides(List elements, XMLContext.Default defaults, + boolean mergeWithAnnotations) { + List attributes = buildAssociationOverrides( elements, defaults ); + if ( mergeWithAnnotations && defaults.canUseJavaAnnotations() ) { + AssociationOverride annotation = getPhysicalAnnotation( AssociationOverride.class ); + addAssociationOverrideIfNeeded( annotation, attributes ); + AssociationOverrides annotations = getPhysicalAnnotation( AssociationOverrides.class ); + if ( annotations != null ) { + for ( AssociationOverride current : annotations.value() ) { + addAssociationOverrideIfNeeded( current, attributes ); + } + } + } + if ( attributes.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( AssociationOverrides.class ); + ad.setValue( "value", attributes.toArray( new AssociationOverride[attributes.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private List buildAssociationOverrides(List elements, XMLContext.Default defaults) { + List overrides = new ArrayList<>(); + if ( elements != null && elements.size() > 0 ) { + for ( JaxbAssociationOverride current : elements ) { + AnnotationDescriptor override = new AnnotationDescriptor( AssociationOverride.class ); + copyAttribute( override, "name", current.getName(), true ); + override.setValue( "joinColumns", getJoinColumns( current.getJoinColumn(), false ) ); + JoinTable joinTable = buildJoinTable( current.getJoinTable(), defaults ); + if ( joinTable != null ) { + override.setValue( "joinTable", joinTable ); + } + overrides.add( AnnotationFactory.create( override ) ); + } + } + return overrides; + } + + private JoinColumn[] getJoinColumns(List subelements, boolean isInverse) { + List joinColumns = new ArrayList<>(); + if ( subelements != null ) { + for ( JaxbJoinColumn subelement : subelements ) { + AnnotationDescriptor column = new AnnotationDescriptor( JoinColumn.class ); + copyAttribute( column, "name", subelement.getName(), false ); + copyAttribute( column, "referenced-column-name", subelement.getReferencedColumnName(), false ); + copyAttribute( column, "unique", subelement.isUnique(), false ); + copyAttribute( column, "nullable", subelement.isNullable(), false ); + copyAttribute( column, "insertable", subelement.isInsertable(), false ); + copyAttribute( column, "updatable", subelement.isUpdatable(), false ); + copyAttribute( column, "column-definition", subelement.getColumnDefinition(), false ); + copyAttribute( column, "table", subelement.getTable(), false ); + joinColumns.add( AnnotationFactory.create( column ) ); + } + } + return joinColumns.toArray( new JoinColumn[joinColumns.size()] ); + } + + private void addAssociationOverrideIfNeeded(AssociationOverride annotation, List overrides) { + if ( annotation != null ) { + String overrideName = annotation.name(); + boolean present = false; + for ( AssociationOverride current : overrides ) { + if ( current.name().equals( overrideName ) ) { + present = true; + break; + } + } + if ( !present ) { + overrides.add( annotation ); + } + } + } + + private AttributeOverrides getAttributeOverrides(ManagedType root, XMLContext.Default defaults) { + return root instanceof JaxbEntity + ? getAttributeOverrides( ( (JaxbEntity) root ).getAttributeOverride(), defaults, true ) + : null; + } + + /** + * @param mergeWithAnnotations Whether to use Java annotations for this + * element, if present and not disabled by the XMLContext defaults. + * In some contexts (such as an association mapping) merging with + * annotations is never allowed. + */ + private AttributeOverrides getAttributeOverrides(List elements, XMLContext.Default defaults, + boolean mergeWithAnnotations) { + List attributes = buildAttributeOverrides( elements, "attribute-override" ); + return mergeAttributeOverrides( defaults, attributes, mergeWithAnnotations ); + } + + /** + * @param mergeWithAnnotations Whether to use Java annotations for this + * element, if present and not disabled by the XMLContext defaults. + * In some contexts (such as an association mapping) merging with + * annotations is never allowed. + */ + private AttributeOverrides mergeAttributeOverrides(XMLContext.Default defaults, List attributes, boolean mergeWithAnnotations) { + if ( mergeWithAnnotations && defaults.canUseJavaAnnotations() ) { + AttributeOverride annotation = getPhysicalAnnotation( AttributeOverride.class ); + addAttributeOverrideIfNeeded( annotation, attributes ); + AttributeOverrides annotations = getPhysicalAnnotation( AttributeOverrides.class ); + if ( annotations != null ) { + for ( AttributeOverride current : annotations.value() ) { + addAttributeOverrideIfNeeded( current, attributes ); + } + } + } + if ( attributes.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( AttributeOverrides.class ); + ad.setValue( "value", attributes.toArray( new AttributeOverride[attributes.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private List buildAttributeOverrides(List subelements, String nodeName) { + List overrides = new ArrayList<>(); + if ( subelements != null && subelements.size() > 0 ) { + for ( JaxbAttributeOverride current : subelements ) { + AnnotationDescriptor override = new AnnotationDescriptor( AttributeOverride.class ); + copyAttribute( override, "name", current.getName(), true ); + JaxbColumn column = current.getColumn(); + override.setValue( "column", getColumn( column, true, nodeName ) ); + overrides.add( AnnotationFactory.create( override ) ); + } + } + return overrides; + } + + private Column getColumn(JaxbColumn element, boolean isMandatory, String nodeName) { + if ( element != null ) { + AnnotationDescriptor column = new AnnotationDescriptor( Column.class ); + copyAttribute( column, "name", element.getName(), false ); + copyAttribute( column, "unique", element.isUnique(), false ); + copyAttribute( column, "nullable", element.isNullable(), false ); + copyAttribute( column, "insertable", element.isInsertable(), false ); + copyAttribute( column, "updatable", element.isUpdatable(), false ); + copyAttribute( column, "column-definition", element.getColumnDefinition(), false ); + copyAttribute( column, "table", element.getTable(), false ); + copyAttribute( column, "length", element.getLength(), false ); + copyAttribute( column, "precision", element.getPrecision(), false ); + copyAttribute( column, "scale", element.getScale(), false ); + return (Column) AnnotationFactory.create( column ); + } + else { + if ( isMandatory ) { + throw new AnnotationException( nodeName + ".column is mandatory. " + SCHEMA_VALIDATION ); + } + return null; + } + } + + private void addAttributeOverrideIfNeeded(AttributeOverride annotation, List overrides) { + if ( annotation != null ) { + String overrideName = annotation.name(); + boolean present = false; + for ( AttributeOverride current : overrides ) { + if ( current.name().equals( overrideName ) ) { + present = true; + break; + } + } + if ( !present ) { + overrides.add( annotation ); + } + } + } + + private Access getAccessType(ManagedType root, XMLContext.Default defaults) { + AccessType access = root == null ? null : root.getAccess(); + if ( access != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); + ad.setValue( "value", access ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() && isPhysicalAnnotationPresent( Access.class ) ) { + return getPhysicalAnnotation( Access.class ); + } + else if ( defaults.getAccess() != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); + ad.setValue( "value", defaults.getAccess() ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private ExcludeSuperclassListeners getExcludeSuperclassListeners(ManagedType root, XMLContext.Default defaults) { + return (ExcludeSuperclassListeners) getMarkerAnnotation( ExcludeSuperclassListeners.class, + root instanceof EntityOrMappedSuperclass + ? ( (EntityOrMappedSuperclass) root ).getExcludeSuperclassListeners() + : null, + defaults ); + } + + private ExcludeDefaultListeners getExcludeDefaultListeners(ManagedType root, XMLContext.Default defaults) { + return (ExcludeDefaultListeners) getMarkerAnnotation( ExcludeDefaultListeners.class, + root instanceof EntityOrMappedSuperclass + ? ( (EntityOrMappedSuperclass) root ).getExcludeDefaultListeners() + : null, + defaults ); + } + + private Annotation getMarkerAnnotation(Class clazz, JaxbEmptyType element, + XMLContext.Default defaults) { + if ( element != null ) { + return AnnotationFactory.create( new AnnotationDescriptor( clazz ) ); + } + else if ( defaults.canUseJavaAnnotations() ) { + //TODO wonder whether it should be excluded so that user can undone it + return getPhysicalAnnotation( clazz ); + } + else { + return null; + } + } + + private SqlResultSetMappings getSqlResultSetMappings(ManagedType root, XMLContext.Default defaults) { + List results = root instanceof JaxbEntity + ? buildSqlResultsetMappings( ( (JaxbEntity) root ).getSqlResultSetMapping(), defaults, classLoaderAccess ) + : new ArrayList<>(); + if ( defaults.canUseJavaAnnotations() ) { + SqlResultSetMapping annotation = getPhysicalAnnotation( SqlResultSetMapping.class ); + addSqlResultsetMappingIfNeeded( annotation, results ); + SqlResultSetMappings annotations = getPhysicalAnnotation( SqlResultSetMappings.class ); + if ( annotations != null ) { + for ( SqlResultSetMapping current : annotations.value() ) { + addSqlResultsetMappingIfNeeded( current, results ); + } + } + } + if ( results.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( SqlResultSetMappings.class ); + ad.setValue( "value", results.toArray( new SqlResultSetMapping[results.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + public static List buildNamedEntityGraph( + List elements, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + List namedEntityGraphList = new ArrayList<>(); + for ( JaxbNamedEntityGraph element : elements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedEntityGraph.class ); + copyAttribute( ann, "name", element.getName(), false ); + copyAttribute( ann, "include-all-attributes", element.isIncludeAllAttributes(), false ); + bindNamedAttributeNodes( element.getNamedAttributeNode(), ann ); + + bindNamedSubgraph( defaults, ann, "subgraphs", element.getSubgraph(), classLoaderAccess ); + bindNamedSubgraph( defaults, ann, "subclass-subgraph", element.getSubclassSubgraph(), classLoaderAccess ); + namedEntityGraphList.add( AnnotationFactory.create( ann ) ); + } + //TODO + return namedEntityGraphList; + } + + private static void bindNamedSubgraph( + XMLContext.Default defaults, + AnnotationDescriptor ann, + String annotationAttributeName, + List subgraphNodes, + ClassLoaderAccess classLoaderAccess) { + List annSubgraphNodes = new ArrayList<>( ); + for(JaxbNamedSubgraph subgraphNode : subgraphNodes){ + AnnotationDescriptor annSubgraphNode = new AnnotationDescriptor( NamedSubgraph.class ); + copyAttribute( annSubgraphNode, "name", subgraphNode.getName(), true ); + String clazzName = subgraphNode.getClazz(); + Class clazz; + try { + clazz = classLoaderAccess.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + annSubgraphNode.setValue( "type", clazz ); + bindNamedAttributeNodes(subgraphNode.getNamedAttributeNode(), annSubgraphNode); + annSubgraphNodes.add( AnnotationFactory.create( annSubgraphNode ) ); + } + + ann.setValue( annotationAttributeName, annSubgraphNodes.toArray( new NamedSubgraph[annSubgraphNodes.size()] ) ); + } + + private static void bindNamedAttributeNodes(List elements, AnnotationDescriptor ann) { + List annNamedAttributeNodes = new ArrayList<>( ); + for( JaxbNamedAttributeNode element : elements){ + AnnotationDescriptor annNamedAttributeNode = new AnnotationDescriptor( NamedAttributeNode.class ); + copyAttribute( annNamedAttributeNode, "value", "name", element.getName(),true ); + copyAttribute( annNamedAttributeNode, "subgraph", element.getSubgraph(), false ); + copyAttribute( annNamedAttributeNode, "key-subgraph", element.getKeySubgraph(), false ); + annNamedAttributeNodes.add( AnnotationFactory.create( annNamedAttributeNode ) ); + } + ann.setValue( "attributeNodes", annNamedAttributeNodes.toArray( new NamedAttributeNode[annNamedAttributeNodes.size()] ) ); + } + + public static List buildNamedStoreProcedureQueries( + List elements, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + List namedStoredProcedureQueries = new ArrayList<>(); + for ( JaxbNamedStoredProcedureQuery element : elements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedStoredProcedureQuery.class ); + copyAttribute( ann, "name", element.getName(), true ); + copyAttribute( ann, "procedure-name", element.getProcedureName(), true ); + + List storedProcedureParameters = new ArrayList<>(); + + for ( JaxbStoredProcedureParameter parameterElement : element.getParameter() ) { + AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class ); + copyAttribute( parameterDescriptor, "name", parameterElement.getName(), false ); + ParameterMode modeValue = parameterElement.getMode(); + if ( modeValue == null ) { + parameterDescriptor.setValue( "mode", ParameterMode.IN ); + } + else { + parameterDescriptor.setValue( "mode", modeValue ); + } + String clazzName = parameterElement.getClazz(); + Class clazz; + try { + clazz = classLoaderAccess.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + parameterDescriptor.setValue( "type", clazz ); + storedProcedureParameters.add( AnnotationFactory.create( parameterDescriptor ) ); + } + + ann.setValue( + "parameters", + storedProcedureParameters.toArray( new StoredProcedureParameter[storedProcedureParameters.size()] ) + ); + + List> returnClasses = new ArrayList<>(); + for ( String clazzName : element.getResultClass() ) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + returnClasses.add( clazz ); + } + ann.setValue( "resultClasses", returnClasses.toArray( new Class[returnClasses.size()] ) ); + + + ann.setValue( "resultSetMappings", element.getResultSetMapping().toArray( new String[0] ) ); + buildQueryHints( element.getHint(), ann ); + namedStoredProcedureQueries.add( AnnotationFactory.create( ann ) ); + } + return namedStoredProcedureQueries; + + } + + public static List buildSqlResultsetMappings( + List elements, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + final List builtResultSetMappings = new ArrayList<>(); + + // iterate over each element + for ( JaxbSqlResultSetMapping resultSetMappingElement : elements ) { + final AnnotationDescriptor resultSetMappingAnnotation = new AnnotationDescriptor( SqlResultSetMapping.class ); + copyAttribute( resultSetMappingAnnotation, "name", resultSetMappingElement.getName(), true ); + + // iterate over the sub-elements, which should include: + // * + // * + // * + + List entityResultAnnotations = null; + List columnResultAnnotations = null; + List constructorResultAnnotations = null; + + for ( JaxbEntityResult resultElement : resultSetMappingElement.getEntityResult() ) { + if ( entityResultAnnotations == null ) { + entityResultAnnotations = new ArrayList<>(); + } + // process the + entityResultAnnotations.add( buildEntityResult( resultElement, defaults, classLoaderAccess ) ); + } + for ( JaxbColumnResult resultElement : resultSetMappingElement.getColumnResult() ) { + if ( columnResultAnnotations == null ) { + columnResultAnnotations = new ArrayList<>(); + } + columnResultAnnotations.add( buildColumnResult( resultElement, defaults, classLoaderAccess ) ); + } + for ( JaxbConstructorResult resultElement : resultSetMappingElement.getConstructorResult() ) { + if ( constructorResultAnnotations == null ) { + constructorResultAnnotations = new ArrayList<>(); + } + constructorResultAnnotations.add( buildConstructorResult( resultElement, defaults, classLoaderAccess ) ); + } + + if ( entityResultAnnotations != null && !entityResultAnnotations.isEmpty() ) { + resultSetMappingAnnotation.setValue( + "entities", + entityResultAnnotations.toArray( new EntityResult[entityResultAnnotations.size()] ) + ); + } + if ( columnResultAnnotations != null && !columnResultAnnotations.isEmpty() ) { + resultSetMappingAnnotation.setValue( + "columns", + columnResultAnnotations.toArray( new ColumnResult[columnResultAnnotations.size()] ) + ); + } + if ( constructorResultAnnotations != null && !constructorResultAnnotations.isEmpty() ) { + resultSetMappingAnnotation.setValue( + "classes", + constructorResultAnnotations.toArray( new ConstructorResult[constructorResultAnnotations.size()] ) + ); + } + + builtResultSetMappings.add( AnnotationFactory.create( resultSetMappingAnnotation ) ); + } + + return builtResultSetMappings; + } + + private static EntityResult buildEntityResult( + JaxbEntityResult entityResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + final AnnotationDescriptor entityResultDescriptor = new AnnotationDescriptor( EntityResult.class ); + + final Class entityClass = resolveClassReference( entityResultElement.getEntityClass(), defaults, classLoaderAccess ); + entityResultDescriptor.setValue( "entityClass", entityClass ); + + copyAttribute( entityResultDescriptor, "discriminator-column", entityResultElement.getDiscriminatorColumn(), false ); + + // process the sub-elements + List fieldResultAnnotations = new ArrayList<>(); + for ( JaxbFieldResult fieldResult : entityResultElement.getFieldResult() ) { + AnnotationDescriptor fieldResultDescriptor = new AnnotationDescriptor( FieldResult.class ); + copyAttribute( fieldResultDescriptor, "name", fieldResult.getName(), true ); + copyAttribute( fieldResultDescriptor, "column", fieldResult.getColumn(), true ); + fieldResultAnnotations.add( AnnotationFactory.create( fieldResultDescriptor ) ); + } + entityResultDescriptor.setValue( + "fields", fieldResultAnnotations.toArray( new FieldResult[fieldResultAnnotations.size()] ) + ); + return AnnotationFactory.create( entityResultDescriptor ); + } + + private static Class resolveClassReference( + String className, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + if ( className == null ) { + throw new AnnotationException( " without entity-class. " + SCHEMA_VALIDATION ); + } + try { + return classLoaderAccess.classForName( + XMLContext.buildSafeClassName( className, defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find specified class: " + className, e ); + } + } + + private static ColumnResult buildColumnResult( + JaxbColumnResult columnResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor( ColumnResult.class ); + copyAttribute( columnResultDescriptor, "name", columnResultElement.getName(), true ); + final String columnTypeName = columnResultElement.getClazz(); + if ( StringHelper.isNotEmpty( columnTypeName ) ) { + columnResultDescriptor.setValue( "type", resolveClassReference( columnTypeName, defaults, classLoaderAccess ) ); + } + return AnnotationFactory.create( columnResultDescriptor ); + } + + private static ConstructorResult buildConstructorResult( + JaxbConstructorResult constructorResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + AnnotationDescriptor constructorResultDescriptor = new AnnotationDescriptor( ConstructorResult.class ); + + final Class entityClass = resolveClassReference( constructorResultElement.getTargetClass(), defaults, classLoaderAccess ); + constructorResultDescriptor.setValue( "targetClass", entityClass ); + + List columnResultAnnotations = new ArrayList<>(); + for ( JaxbColumnResult columnResultElement : constructorResultElement.getColumn() ) { + columnResultAnnotations.add( buildColumnResult( columnResultElement, defaults, classLoaderAccess ) ); + } + constructorResultDescriptor.setValue( + "columns", + columnResultAnnotations.toArray( new ColumnResult[ columnResultAnnotations.size() ] ) + ); + + return AnnotationFactory.create( constructorResultDescriptor ); + } + + private void addSqlResultsetMappingIfNeeded(SqlResultSetMapping annotation, List resultsets) { + if ( annotation != null ) { + String resultsetName = annotation.name(); + boolean present = false; + for ( SqlResultSetMapping current : resultsets ) { + if ( current.name().equals( resultsetName ) ) { + present = true; + break; + } + } + if ( !present ) { + resultsets.add( annotation ); + } + } + } + + private NamedQueries getNamedQueries(ManagedType root, XMLContext.Default defaults) { + //TODO avoid the Proxy Creation (@NamedQueries) when possible + List queries = root instanceof JaxbEntity + ? buildNamedQueries( ( (JaxbEntity) root ).getNamedQuery(), defaults, classLoaderAccess ) + : new ArrayList<>(); + if ( defaults.canUseJavaAnnotations() ) { + NamedQuery annotation = getPhysicalAnnotation( NamedQuery.class ); + addNamedQueryIfNeeded( annotation, queries ); + NamedQueries annotations = getPhysicalAnnotation( NamedQueries.class ); + if ( annotations != null ) { + for ( NamedQuery current : annotations.value() ) { + addNamedQueryIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedQueries.class ); + ad.setValue( "value", queries.toArray( new NamedQuery[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedQueryIfNeeded(NamedQuery annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedQuery current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + } + + private NamedEntityGraphs getNamedEntityGraphs(ManagedType root, XMLContext.Default defaults) { + List queries = root instanceof JaxbEntity + ? buildNamedEntityGraph( ( (JaxbEntity) root ).getNamedEntityGraph(), defaults, classLoaderAccess ) + : new ArrayList<>(); + if ( defaults.canUseJavaAnnotations() ) { + NamedEntityGraph annotation = getPhysicalAnnotation( NamedEntityGraph.class ); + addNamedEntityGraphIfNeeded( annotation, queries ); + NamedEntityGraphs annotations = getPhysicalAnnotation( NamedEntityGraphs.class ); + if ( annotations != null ) { + for ( NamedEntityGraph current : annotations.value() ) { + addNamedEntityGraphIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedEntityGraphs.class ); + ad.setValue( "value", queries.toArray( new NamedEntityGraph[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedEntityGraphIfNeeded(NamedEntityGraph annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedEntityGraph current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + + } + + private NamedStoredProcedureQueries getNamedStoredProcedureQueries(ManagedType root, XMLContext.Default defaults) { + List queries = root instanceof JaxbEntity + ? buildNamedStoreProcedureQueries( ( (JaxbEntity) root ).getNamedStoredProcedureQuery(), defaults, classLoaderAccess ) + : new ArrayList<>(); + if ( defaults.canUseJavaAnnotations() ) { + NamedStoredProcedureQuery annotation = getPhysicalAnnotation( NamedStoredProcedureQuery.class ); + addNamedStoredProcedureQueryIfNeeded( annotation, queries ); + NamedStoredProcedureQueries annotations = getPhysicalAnnotation( NamedStoredProcedureQueries.class ); + if ( annotations != null ) { + for ( NamedStoredProcedureQuery current : annotations.value() ) { + addNamedStoredProcedureQueryIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedStoredProcedureQueries.class ); + ad.setValue( "value", queries.toArray( new NamedStoredProcedureQuery[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedStoredProcedureQueryIfNeeded(NamedStoredProcedureQuery annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedStoredProcedureQuery current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + } + + + private NamedNativeQueries getNamedNativeQueries( + ManagedType root, + XMLContext.Default defaults) { + List queries = root instanceof JaxbEntity + ? buildNamedNativeQueries( ( (JaxbEntity) root ).getNamedNativeQuery(), defaults, classLoaderAccess ) + : new ArrayList<>(); + if ( defaults.canUseJavaAnnotations() ) { + NamedNativeQuery annotation = getPhysicalAnnotation( NamedNativeQuery.class ); + addNamedNativeQueryIfNeeded( annotation, queries ); + NamedNativeQueries annotations = getPhysicalAnnotation( NamedNativeQueries.class ); + if ( annotations != null ) { + for ( NamedNativeQuery current : annotations.value() ) { + addNamedNativeQueryIfNeeded( current, queries ); + } + } + } + if ( queries.size() > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( NamedNativeQueries.class ); + ad.setValue( "value", queries.toArray( new NamedNativeQuery[queries.size()] ) ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void addNamedNativeQueryIfNeeded(NamedNativeQuery annotation, List queries) { + if ( annotation != null ) { + String queryName = annotation.name(); + boolean present = false; + for ( NamedNativeQuery current : queries ) { + if ( current.name().equals( queryName ) ) { + present = true; + break; + } + } + if ( !present ) { + queries.add( annotation ); + } + } + } + + private static void buildQueryHints(List elements, AnnotationDescriptor ann){ + List queryHints = new ArrayList<>( elements.size() ); + for ( JaxbQueryHint hint : elements ) { + AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); + String value = hint.getName(); + if ( value == null ) { + throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); + } + hintDescriptor.setValue( "name", value ); + value = hint.getValue(); + if ( value == null ) { + throw new AnnotationException( " without value. " + SCHEMA_VALIDATION ); + } + hintDescriptor.setValue( "value", value ); + queryHints.add( AnnotationFactory.create( hintDescriptor ) ); + } + ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) ); + } + + public static List buildNamedQueries( + List elements, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + List namedQueries = new ArrayList<>(); + for ( JaxbNamedQuery element : elements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedQuery.class ); + copyAttribute( ann, "name", element.getName(), false ); + copyAttribute( ann, "query", element.getQuery(), true ); + buildQueryHints( element.getHint(), ann ); + copyAttribute( ann, "lock-mode", element.getLockMode(), false ); + namedQueries.add( AnnotationFactory.create( ann ) ); + } + return namedQueries; + } + + public static List buildNamedNativeQueries( + List elements, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + List namedQueries = new ArrayList<>(); + for ( JaxbNamedNativeQuery element : elements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedNativeQuery.class ); + copyAttribute( ann, "name", element.getName(), false ); + copyAttribute( ann, "query", element.getQuery(), true ); + buildQueryHints( element.getHint(), ann ); + String clazzName = element.getResultClass(); + if ( StringHelper.isNotEmpty( clazzName ) ) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( + XMLContext.buildSafeClassName( clazzName, defaults ) + ); + } + catch (ClassLoadingException e) { + throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); + } + ann.setValue( "resultClass", clazz ); + } + copyAttribute( ann, "result-set-mapping", element.getResultSetMapping(), false ); + namedQueries.add( AnnotationFactory.create( ann ) ); + } + return namedQueries; + } + + private TableGenerator getTableGenerator(ManagedType root, XMLContext.Default defaults) { + return getTableGenerator( root instanceof JaxbEntity ? ( (JaxbEntity) root ).getTableGenerator() : null, defaults ); + } + + private TableGenerator getTableGenerator(JaxbTableGenerator element, XMLContext.Default defaults) { + if ( element != null ) { + return buildTableGeneratorAnnotation( element, defaults ); + } + else if ( defaults.canUseJavaAnnotations() && isPhysicalAnnotationPresent( TableGenerator.class ) ) { + TableGenerator tableAnn = getPhysicalAnnotation( TableGenerator.class ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + || StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( TableGenerator.class ); + annotation.setValue( "name", tableAnn.name() ); + annotation.setValue( "table", tableAnn.table() ); + annotation.setValue( "catalog", tableAnn.table() ); + if ( StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) + && StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + annotation.setValue( "schema", tableAnn.table() ); + if ( StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) + && StringHelper.isNotEmpty( defaults.getSchema() ) ) { + annotation.setValue( "catalog", defaults.getSchema() ); + } + annotation.setValue( "pkColumnName", tableAnn.pkColumnName() ); + annotation.setValue( "valueColumnName", tableAnn.valueColumnName() ); + annotation.setValue( "pkColumnValue", tableAnn.pkColumnValue() ); + annotation.setValue( "initialValue", tableAnn.initialValue() ); + annotation.setValue( "allocationSize", tableAnn.allocationSize() ); + annotation.setValue( "uniqueConstraints", tableAnn.uniqueConstraints() ); + return AnnotationFactory.create( annotation ); + } + else { + return tableAnn; + } + } + else { + return null; + } + } + + public static TableGenerator buildTableGeneratorAnnotation(JaxbTableGenerator element, XMLContext.Default defaults) { + AnnotationDescriptor ad = new AnnotationDescriptor( TableGenerator.class ); + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "table", element.getTable(), false ); + copyAttribute( ad, "catalog", element.getCatalog(), false ); + copyAttribute( ad, "schema", element.getSchema(), false ); + copyAttribute( ad, "pk-column-name", element.getPkColumnName(), false ); + copyAttribute( ad, "value-column-name", element.getValueColumnName(), false ); + copyAttribute( ad, "pk-column-value", element.getPkColumnValue(), false ); + copyAttribute( ad, "initial-value", element.getInitialValue(), false ); + copyAttribute( ad, "allocation-size", element.getAllocationSize(), false ); + buildUniqueConstraints( ad, element.getUniqueConstraint() ); + if ( StringHelper.isEmpty( (String) ad.valueOf( "schema" ) ) + && StringHelper.isNotEmpty( defaults.getSchema() ) ) { + ad.setValue( "schema", defaults.getSchema() ); + } + if ( StringHelper.isEmpty( (String) ad.valueOf( "catalog" ) ) + && StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + ad.setValue( "catalog", defaults.getCatalog() ); + } + return AnnotationFactory.create( ad ); + } + + private SequenceGenerator getSequenceGenerator(ManagedType root, XMLContext.Default defaults) { + return getSequenceGenerator( root instanceof JaxbEntity ? ( (JaxbEntity) root ).getSequenceGenerator() : null, + defaults ); + } + + private SequenceGenerator getSequenceGenerator(JaxbSequenceGenerator element, XMLContext.Default defaults) { + if ( element != null ) { + return buildSequenceGeneratorAnnotation( element ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( SequenceGenerator.class ); + } + else { + return null; + } + } + + public static SequenceGenerator buildSequenceGeneratorAnnotation(JaxbSequenceGenerator element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( SequenceGenerator.class ); + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "sequence-name", element.getSequenceName(), false ); + copyAttribute( ad, "initial-value", element.getInitialValue(), false ); + copyAttribute( ad, "allocation-size", element.getAllocationSize(), false ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private DiscriminatorColumn getDiscriminatorColumn(ManagedType root, XMLContext.Default defaults) { + JaxbDiscriminatorColumn element = root instanceof JaxbEntity ? ( (JaxbEntity) root ).getDiscriminatorColumn() : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( DiscriminatorColumn.class ); + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "column-definition", element.getColumnDefinition(), false ); + DiscriminatorType type = element.getDiscriminatorType(); + if ( type != null ) { + ad.setValue( "discriminatorType", type ); + } + copyAttribute( ad, "length", element.getLength(), false ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( DiscriminatorColumn.class ); + } + else { + return null; + } + } + + private DiscriminatorValue getDiscriminatorValue(ManagedType root, XMLContext.Default defaults) { + String element = root instanceof JaxbEntity ? ( (JaxbEntity) root ).getDiscriminatorValue() : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( DiscriminatorValue.class ); + ad.setValue( "value", element ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( DiscriminatorValue.class ); + } + else { + return null; + } + } + + private Inheritance getInheritance(ManagedType root, XMLContext.Default defaults) { + JaxbInheritance element = root instanceof JaxbEntity ? ( (JaxbEntity) root ).getInheritance() : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Inheritance.class ); + InheritanceType strategy = element.getStrategy(); + if ( strategy != null ) { + ad.setValue( "strategy", strategy ); + } + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Inheritance.class ); + } + else { + return null; + } + } + + private IdClass getIdClass(ManagedType root, XMLContext.Default defaults) { + JaxbIdClass element = root instanceof EntityOrMappedSuperclass ? + ( (EntityOrMappedSuperclass) root ).getIdClass() : null; + if ( element != null ) { + String className = element.getClazz(); + if ( className != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( IdClass.class ); + Class clazz; + try { + clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( className, defaults ) ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find id-class: " + className, e ); + } + ad.setValue( "value", clazz ); + return AnnotationFactory.create( ad ); + } + else { + throw new AnnotationException( "id-class without class. " + SCHEMA_VALIDATION ); + } + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( IdClass.class ); + } + else { + return null; + } + } + + private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(ManagedType root, XMLContext.Default defaults) { + return root instanceof JaxbEntity + ? getPrimaryKeyJoinColumns( ( (JaxbEntity) root ).getPrimaryKeyJoinColumn(), defaults, true ) + : null; + } + + /** + * @param mergeWithAnnotations Whether to use Java annotations for this + * element, if present and not disabled by the XMLContext defaults. + * In some contexts (such as an association mapping) merging with + */ + private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(List elements, + XMLContext.Default defaults, boolean mergeWithAnnotations) { + PrimaryKeyJoinColumn[] columns = buildPrimaryKeyJoinColumns( elements ); + if ( mergeWithAnnotations ) { + if ( columns.length == 0 && defaults.canUseJavaAnnotations() ) { + PrimaryKeyJoinColumn annotation = getPhysicalAnnotation( PrimaryKeyJoinColumn.class ); + if ( annotation != null ) { + columns = new PrimaryKeyJoinColumn[] { annotation }; + } + else { + PrimaryKeyJoinColumns annotations = getPhysicalAnnotation( PrimaryKeyJoinColumns.class ); + columns = annotations != null ? annotations.value() : columns; + } + } + } + if ( columns.length > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PrimaryKeyJoinColumns.class ); + ad.setValue( "value", columns ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private Entity getEntity(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Entity.class ) : null; + } + else { + if ( element instanceof JaxbEntity ) { + JaxbEntity entityElement = (JaxbEntity) element; + AnnotationDescriptor entity = new AnnotationDescriptor( Entity.class ); + copyAttribute( entity, "name", entityElement.getName(), false ); + if ( defaults.canUseJavaAnnotations() + && StringHelper.isEmpty( (String) entity.valueOf( "name" ) ) ) { + Entity javaAnn = getPhysicalAnnotation( Entity.class ); + if ( javaAnn != null ) { + entity.setValue( "name", javaAnn.name() ); + } + } + return AnnotationFactory.create( entity ); + } + else { + return null; //this is not an entity + } + } + } + + private MappedSuperclass getMappedSuperclass(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( MappedSuperclass.class ) : null; + } + else { + if ( element instanceof JaxbMappedSuperclass ) { + AnnotationDescriptor entity = new AnnotationDescriptor( MappedSuperclass.class ); + return AnnotationFactory.create( entity ); + } + else { + return null; //this is not an entity + } + } + } + + private Embeddable getEmbeddable(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Embeddable.class ) : null; + } + else { + if ( element instanceof JaxbEmbeddable ) { + AnnotationDescriptor entity = new AnnotationDescriptor( Embeddable.class ); + return AnnotationFactory.create( entity ); + } + else { + return null; //this is not an entity + } + } + } + + private Table getTable(ManagedType root, XMLContext.Default defaults) { + JaxbTable element = root instanceof JaxbEntity ? ( (JaxbEntity) root ).getTable() : null; + if ( element == null ) { + //no element but might have some default or some annotation + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + || StringHelper.isNotEmpty( defaults.getSchema() ) ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( Table.class ); + if ( defaults.canUseJavaAnnotations() ) { + Table table = getPhysicalAnnotation( Table.class ); + if ( table != null ) { + annotation.setValue( "name", table.name() ); + annotation.setValue( "schema", table.schema() ); + annotation.setValue( "catalog", table.catalog() ); + annotation.setValue( "uniqueConstraints", table.uniqueConstraints() ); + annotation.setValue( "indexes", table.indexes() ); + } + } + if ( StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) + && StringHelper.isNotEmpty( defaults.getSchema() ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + if ( StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) + && StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + return AnnotationFactory.create( annotation ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Table.class ); + } + else { + return null; + } + } + else { + //ignore java annotation, an element is defined + AnnotationDescriptor annotation = new AnnotationDescriptor( Table.class ); + copyAttribute( annotation, "name", element.getName(), false ); + copyAttribute( annotation, "catalog", element.getCatalog(), false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyAttribute( annotation, "schema", element.getSchema(), false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); + return AnnotationFactory.create( annotation ); + } + } + + private SecondaryTables getSecondaryTables(ManagedType root, XMLContext.Default defaults) { + List elements = root instanceof JaxbEntity ? + ( (JaxbEntity) root ).getSecondaryTable() : Collections.emptyList(); + List secondaryTables = new ArrayList<>( 3 ); + for ( JaxbSecondaryTable element : elements ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( SecondaryTable.class ); + copyAttribute( annotation, "name", element.getName(), false ); + copyAttribute( annotation, "catalog", element.getCatalog(), false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyAttribute( annotation, "schema", element.getSchema(), false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); + annotation.setValue( "pkJoinColumns", + buildPrimaryKeyJoinColumns( element.getPrimaryKeyJoinColumn() ) ); + secondaryTables.add( AnnotationFactory.create( annotation ) ); + } + /* + * You can't have both secondary tables in XML and Java, + * since there would be no way to "remove" a secondary table + */ + if ( secondaryTables.size() == 0 && defaults.canUseJavaAnnotations() ) { + SecondaryTable secTableAnn = getPhysicalAnnotation( SecondaryTable.class ); + overridesDefaultInSecondaryTable( secTableAnn, defaults, secondaryTables ); + SecondaryTables secTablesAnn = getPhysicalAnnotation( SecondaryTables.class ); + if ( secTablesAnn != null ) { + for ( SecondaryTable table : secTablesAnn.value() ) { + overridesDefaultInSecondaryTable( table, defaults, secondaryTables ); + } + } + } + if ( secondaryTables.size() > 0 ) { + AnnotationDescriptor descriptor = new AnnotationDescriptor( SecondaryTables.class ); + descriptor.setValue( "value", secondaryTables.toArray( new SecondaryTable[secondaryTables.size()] ) ); + return AnnotationFactory.create( descriptor ); + } + else { + return null; + } + } + + private void overridesDefaultInSecondaryTable( + SecondaryTable secTableAnn, XMLContext.Default defaults, List secondaryTables + ) { + if ( secTableAnn != null ) { + //handle default values + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + || StringHelper.isNotEmpty( defaults.getSchema() ) ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( SecondaryTable.class ); + annotation.setValue( "name", secTableAnn.name() ); + annotation.setValue( "schema", secTableAnn.schema() ); + annotation.setValue( "catalog", secTableAnn.catalog() ); + annotation.setValue( "uniqueConstraints", secTableAnn.uniqueConstraints() ); + annotation.setValue( "pkJoinColumns", secTableAnn.pkJoinColumns() ); + if ( StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) + && StringHelper.isNotEmpty( defaults.getSchema() ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + if ( StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) + && StringHelper.isNotEmpty( defaults.getCatalog() ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + secondaryTables.add( AnnotationFactory.create( annotation ) ); + } + else { + secondaryTables.add( secTableAnn ); + } + } + } + private static void buildIndex(AnnotationDescriptor annotation, List elements) { + Index[] indexes = new Index[elements.size()]; + int i = 0; + for ( JaxbIndex element : elements ) { + AnnotationDescriptor indexAnn = new AnnotationDescriptor( Index.class ); + copyAttribute( indexAnn, "name", element.getName(), false ); + copyAttribute( indexAnn, "column-list", element.getColumnList(), true ); + copyAttribute( indexAnn, "unique", element.isUnique(), false ); + indexes[i++] = AnnotationFactory.create( indexAnn ); + } + annotation.setValue( "indexes", indexes ); + } + + private static void buildUniqueConstraints(AnnotationDescriptor annotation, + List elements) { + UniqueConstraint[] uniqueConstraints = new UniqueConstraint[elements.size()]; + int i = 0; + for ( JaxbUniqueConstraint element : elements ) { + String[] columnNames = element.getColumnName().toArray( new String[0] ); + AnnotationDescriptor ucAnn = new AnnotationDescriptor( UniqueConstraint.class ); + copyAttribute( ucAnn, "name", element.getName(), false ); + ucAnn.setValue( "columnNames", columnNames ); + uniqueConstraints[i++] = AnnotationFactory.create( ucAnn ); + } + annotation.setValue( "uniqueConstraints", uniqueConstraints ); + } + + private PrimaryKeyJoinColumn[] buildPrimaryKeyJoinColumns(List elements) { + PrimaryKeyJoinColumn[] pkJoinColumns = new PrimaryKeyJoinColumn[elements.size()]; + int i = 0; + for ( JaxbPrimaryKeyJoinColumn element : elements ) { + AnnotationDescriptor pkAnn = new AnnotationDescriptor( PrimaryKeyJoinColumn.class ); + copyAttribute( pkAnn, "name", element.getName(), false ); + copyAttribute( pkAnn, "referenced-column-name", element.getReferencedColumnName(), false ); + copyAttribute( pkAnn, "column-definition", element.getColumnDefinition(), false ); + pkJoinColumns[i++] = AnnotationFactory.create( pkAnn ); + } + return pkJoinColumns; + } + + /** + * Copy an attribute from an XML element to an annotation descriptor. The name of the annotation attribute is + * computed from the name of the XML attribute by {@link #getJavaAttributeNameFromXMLOne(String)}. + * + * @param annotation annotation descriptor where to copy to the attribute. + * @param attributeName name of the XML attribute to copy. + * @param attributeValue value of the XML attribute to copy. + * @param mandatory whether the attribute is mandatory. + */ + private static void copyAttribute( + final AnnotationDescriptor annotation, + final String attributeName, final Object attributeValue, + final boolean mandatory) { + copyAttribute( + annotation, + getJavaAttributeNameFromXMLOne( attributeName ), + attributeName, + attributeValue, + mandatory + ); + } + + /** + * Copy an attribute from an XML element to an annotation descriptor. The name of the annotation attribute is + * explicitly given. + * + * @param annotation annotation where to copy to the attribute. + * @param annotationAttributeName name of the annotation attribute where to copy. + * @param attributeValue value of the XML attribute to copy. + * @param mandatory whether the attribute is mandatory. + */ + private static void copyAttribute( + final AnnotationDescriptor annotation, + final String annotationAttributeName, final Object attributeName, + final Object attributeValue, + boolean mandatory) { + if ( attributeValue != null ) { + annotation.setValue( annotationAttributeName, attributeValue ); + } + else { + if ( mandatory ) { + throw new AnnotationException( + annotationToXml.getOrDefault( annotation.type(), annotation.type().getName() ) + + "." + attributeName + + " is mandatory in XML overriding. " + SCHEMA_VALIDATION + ); + } + } + } + + private static String getJavaAttributeNameFromXMLOne(String attributeName) { + StringBuilder annotationAttributeName = new StringBuilder( attributeName ); + int index = annotationAttributeName.indexOf( WORD_SEPARATOR ); + while ( index != -1 ) { + annotationAttributeName.deleteCharAt( index ); + annotationAttributeName.setCharAt( + index, Character.toUpperCase( annotationAttributeName.charAt( index ) ) + ); + index = annotationAttributeName.indexOf( WORD_SEPARATOR ); + } + return annotationAttributeName.toString(); + } + + private T getPhysicalAnnotation(Class annotationType) { + return element.getAnnotation( annotationType ); + } + + private boolean isPhysicalAnnotationPresent(Class annotationType) { + return element.isAnnotationPresent( annotationType ); + } + + private Annotation[] getPhysicalAnnotations() { + return element.getAnnotations(); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java new file mode 100644 index 0000000000..99bf8efd54 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java @@ -0,0 +1,198 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.cfg.annotations.reflection.internal; + +import java.lang.reflect.AnnotatedElement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.EntityListeners; +import javax.persistence.NamedNativeQuery; +import javax.persistence.NamedQuery; +import javax.persistence.NamedStoredProcedureQuery; +import javax.persistence.SequenceGenerator; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.TableGenerator; + +import org.hibernate.annotations.common.reflection.AnnotationReader; +import org.hibernate.annotations.common.reflection.MetadataProvider; +import org.hibernate.annotations.common.reflection.java.JavaMetadataProvider; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGenerator; +import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGenerator; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.ClassLoaderAccess; + +/** + * MetadataProvider aware of the JPA Deployment descriptor (orm.xml, ...). + * + * @author Emmanuel Bernard + */ +@SuppressWarnings("unchecked") +public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider { + + private static final MetadataProvider STATELESS_BASE_DELEGATE = new JavaMetadataProvider(); + + private final ClassLoaderAccess classLoaderAccess; + private final XMLContext xmlContext; + + /** + * We allow fully disabling XML sources so to improve the efficiency of + * the boot process for those not using it. + */ + private final XmlMappingOptions xmlMappingOptions; + + private Map defaults; + private Map cache; + + public JPAXMLOverriddenMetadataProvider(BootstrapContext bootstrapContext) { + this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); + this.xmlContext = new XMLContext( classLoaderAccess ); + this.xmlMappingOptions = bootstrapContext.getMetadataBuildingOptions().getXmlMappingOptions(); + } + + //all of the above can be safely rebuilt from XMLContext: only XMLContext this object is serialized + @Override + public AnnotationReader getAnnotationReader(AnnotatedElement annotatedElement) { + if ( cache == null ) { + cache = new HashMap<>(50 ); + } + AnnotationReader reader = cache.get( annotatedElement ); + if (reader == null) { + if ( xmlContext.hasContext() ) { + reader = new JPAXMLOverriddenAnnotationReader( annotatedElement, xmlContext, classLoaderAccess ); + } + else { + reader = STATELESS_BASE_DELEGATE.getAnnotationReader( annotatedElement ); + } + cache.put( annotatedElement, reader ); + } + return reader; + } + + // @Override + // FIXME this method was introduced in HCANN 5.1.1: we can't mark it as @Override yet, + // but it's expected to be invoked when we're running with the right HCANN version. + public void reset() { + //It's better to remove the HashMap, as it could grow rather large: + //when doing a clear() the internal buckets array is not scaled down. + this.cache = null; + } + + @Override + public Map getDefaults() { + if ( !xmlMappingOptions.isEnabled() ) { + return Collections.emptyMap(); + } + else { + if ( defaults == null ) { + defaults = new HashMap<>(); + XMLContext.Default xmlDefaults = xmlContext.getDefault( null ); + + defaults.put( "schema", xmlDefaults.getSchema() ); + defaults.put( "catalog", xmlDefaults.getCatalog() ); + defaults.put( "delimited-identifier", xmlDefaults.getDelimitedIdentifier() ); + defaults.put( "cascade-persist", xmlDefaults.getCascadePersist() ); + List entityListeners = new ArrayList(); + for ( String className : xmlContext.getDefaultEntityListeners() ) { + try { + entityListeners.add( classLoaderAccess.classForName( className ) ); + } + catch ( ClassLoadingException e ) { + throw new IllegalStateException( "Default entity listener class not found: " + className ); + } + } + defaults.put( EntityListeners.class, entityListeners ); + for ( JaxbEntityMappings entityMappings : xmlContext.getAllDocuments() ) { + List jaxbSequenceGenerators = entityMappings.getSequenceGenerator(); + List sequenceGenerators = ( List ) defaults.get( SequenceGenerator.class ); + if ( sequenceGenerators == null ) { + sequenceGenerators = new ArrayList<>(); + defaults.put( SequenceGenerator.class, sequenceGenerators ); + } + for ( JaxbSequenceGenerator element : jaxbSequenceGenerators ) { + sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( element ) ); + } + + List jaxbTableGenerators = entityMappings.getTableGenerator(); + List tableGenerators = ( List ) defaults.get( TableGenerator.class ); + if ( tableGenerators == null ) { + tableGenerators = new ArrayList<>(); + defaults.put( TableGenerator.class, tableGenerators ); + } + for ( JaxbTableGenerator element : jaxbTableGenerators ) { + tableGenerators.add( + JPAXMLOverriddenAnnotationReader.buildTableGeneratorAnnotation( + element, xmlDefaults + ) + ); + } + + List namedQueries = ( List ) defaults.get( NamedQuery.class ); + if ( namedQueries == null ) { + namedQueries = new ArrayList<>(); + defaults.put( NamedQuery.class, namedQueries ); + } + List currentNamedQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries( + entityMappings.getNamedQuery(), + xmlDefaults, + classLoaderAccess + ); + namedQueries.addAll( currentNamedQueries ); + + List namedNativeQueries = ( List ) defaults.get( NamedNativeQuery.class ); + if ( namedNativeQueries == null ) { + namedNativeQueries = new ArrayList<>(); + defaults.put( NamedNativeQuery.class, namedNativeQueries ); + } + List currentNamedNativeQueries = JPAXMLOverriddenAnnotationReader.buildNamedNativeQueries( + entityMappings.getNamedNativeQuery(), + xmlDefaults, + classLoaderAccess + ); + namedNativeQueries.addAll( currentNamedNativeQueries ); + + List sqlResultSetMappings = ( List ) defaults.get( + SqlResultSetMapping.class + ); + if ( sqlResultSetMappings == null ) { + sqlResultSetMappings = new ArrayList<>(); + defaults.put( SqlResultSetMapping.class, sqlResultSetMappings ); + } + List currentSqlResultSetMappings = JPAXMLOverriddenAnnotationReader.buildSqlResultsetMappings( + entityMappings.getSqlResultSetMapping(), + xmlDefaults, + classLoaderAccess + ); + sqlResultSetMappings.addAll( currentSqlResultSetMappings ); + + List namedStoredProcedureQueries = (List)defaults.get( NamedStoredProcedureQuery.class ); + if(namedStoredProcedureQueries==null){ + namedStoredProcedureQueries = new ArrayList<>( ); + defaults.put( NamedStoredProcedureQuery.class, namedStoredProcedureQueries ); + } + List currentNamedStoredProcedureQueries = JPAXMLOverriddenAnnotationReader + .buildNamedStoreProcedureQueries( + entityMappings.getNamedStoredProcedureQuery(), + xmlDefaults, + classLoaderAccess + ); + namedStoredProcedureQueries.addAll( currentNamedStoredProcedureQueries ); + } + } + return defaults; + } + } + + public XMLContext getXMLContext() { + return xmlContext; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/PropertyMappingElementCollector.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/PropertyMappingElementCollector.java new file mode 100644 index 0000000000..97bf6b89ea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/PropertyMappingElementCollector.java @@ -0,0 +1,214 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.cfg.annotations.reflection.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; + +import org.hibernate.boot.jaxb.mapping.spi.AttributesContainer; +import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributes; +import org.hibernate.boot.jaxb.mapping.spi.JaxbBasic; +import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollection; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbedded; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedId; +import org.hibernate.boot.jaxb.mapping.spi.JaxbId; +import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToMany; +import org.hibernate.boot.jaxb.mapping.spi.JaxbManyToOne; +import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToMany; +import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToOne; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPostLoad; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPostPersist; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPostRemove; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPostUpdate; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPrePersist; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPreRemove; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPreUpdate; +import org.hibernate.boot.jaxb.mapping.spi.JaxbTransient; +import org.hibernate.boot.jaxb.mapping.spi.JaxbVersion; +import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback; +import org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer; +import org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute; + +/** + * Reproduces what we used to do with a {@code List} in {@link JPAXMLOverriddenAnnotationReader}, + * with the following constraints: + *

    + *
  • Preserve type safety
  • + *
  • Only create lists if we actually have elements (most lists should be empty in most cases)
  • + *
+ */ +final class PropertyMappingElementCollector { + static final Function PERSISTENT_ATTRIBUTE_NAME = PersistentAttribute::getName; + static final Function JAXB_TRANSIENT_NAME = JaxbTransient::getName; + static final Function LIFECYCLE_CALLBACK_NAME = LifecycleCallback::getMethodName; + + private final String propertyName; + + private List id; + private List embeddedId; + private List basic; + private List version; + private List manyToOne; + private List oneToMany; + private List oneToOne; + private List manyToMany; + private List elementCollection; + private List embedded; + private List _transient; + + private List prePersist; + private List postPersist; + private List preRemove; + private List postRemove; + private List preUpdate; + private List postUpdate; + private List postLoad; + + PropertyMappingElementCollector(String propertyName) { + this.propertyName = propertyName; + } + + public boolean isEmpty() { + return allNullOrEmpty( id, embeddedId, basic, version, manyToOne, oneToMany, oneToOne, manyToMany, + elementCollection, embedded, _transient, + prePersist, postPersist, preRemove, postRemove, preUpdate, postUpdate, postLoad ); + } + + private boolean allNullOrEmpty(List... lists) { + for ( List list : lists ) { + if ( list != null && !list.isEmpty() ) { + return false; + } + } + return true; + } + + private List defaultToEmpty(List list) { + return list == null ? Collections.emptyList() : list; + } + + public void collectPersistentAttributesIfMatching(AttributesContainer container) { + if ( container instanceof JaxbAttributes ) { + JaxbAttributes jaxbAttributes = (JaxbAttributes) container; + id = collectIfMatching( id, jaxbAttributes.getId(), PERSISTENT_ATTRIBUTE_NAME ); + embeddedId = collectIfMatching( embeddedId, jaxbAttributes.getEmbeddedId(), PERSISTENT_ATTRIBUTE_NAME ); + version = collectIfMatching( version, jaxbAttributes.getVersion(), PERSISTENT_ATTRIBUTE_NAME ); + } + basic = collectIfMatching( basic, container.getBasic(), PERSISTENT_ATTRIBUTE_NAME ); + manyToOne = collectIfMatching( manyToOne, container.getManyToOne(), PERSISTENT_ATTRIBUTE_NAME ); + oneToMany = collectIfMatching( oneToMany, container.getOneToMany(), PERSISTENT_ATTRIBUTE_NAME ); + oneToOne = collectIfMatching( oneToOne, container.getOneToOne(), PERSISTENT_ATTRIBUTE_NAME ); + manyToMany = collectIfMatching( manyToMany, container.getManyToMany(), PERSISTENT_ATTRIBUTE_NAME ); + elementCollection = collectIfMatching( elementCollection, container.getElementCollection(), PERSISTENT_ATTRIBUTE_NAME ); + embedded = collectIfMatching( embedded, container.getEmbedded(), PERSISTENT_ATTRIBUTE_NAME ); + _transient = collectIfMatching( _transient, container.getTransient(), JAXB_TRANSIENT_NAME ); + } + + public void collectLifecycleCallbacksIfMatching(LifecycleCallbackContainer container) { + prePersist = collectIfMatching( prePersist, container.getPrePersist(), LIFECYCLE_CALLBACK_NAME ); + postPersist = collectIfMatching( postPersist, container.getPostPersist(), LIFECYCLE_CALLBACK_NAME ); + preRemove = collectIfMatching( preRemove, container.getPreRemove(), LIFECYCLE_CALLBACK_NAME ); + postRemove = collectIfMatching( postRemove, container.getPostRemove(), LIFECYCLE_CALLBACK_NAME ); + preUpdate = collectIfMatching( preUpdate, container.getPreUpdate(), LIFECYCLE_CALLBACK_NAME ); + postUpdate = collectIfMatching( postUpdate, container.getPostUpdate(), LIFECYCLE_CALLBACK_NAME ); + postLoad = collectIfMatching( postLoad, container.getPostLoad(), LIFECYCLE_CALLBACK_NAME ); + } + + private List collectIfMatching(List collected, List candidates, + Function nameGetter) { + List result = collected; + for ( T candidate : candidates ) { + result = collectIfMatching( result, candidate, nameGetter ); + } + return result; + } + + private List collectIfMatching(List collected, T candidate, Function nameGetter) { + List result = collected; + if ( candidate != null && propertyName.equals( nameGetter.apply( candidate ) ) ) { + if ( result == null ) { + result = new ArrayList<>(); + } + result.add( candidate ); + } + return result; + } + + public List getId() { + return defaultToEmpty( id ); + } + + public List getEmbeddedId() { + return defaultToEmpty( embeddedId ); + } + + public List getBasic() { + return defaultToEmpty( basic ); + } + + public List getVersion() { + return defaultToEmpty( version ); + } + + public List getManyToOne() { + return defaultToEmpty( manyToOne ); + } + + public List getOneToMany() { + return defaultToEmpty( oneToMany ); + } + + public List getOneToOne() { + return defaultToEmpty( oneToOne ); + } + + public List getManyToMany() { + return defaultToEmpty( manyToMany ); + } + + public List getElementCollection() { + return defaultToEmpty( elementCollection ); + } + + public List getEmbedded() { + return defaultToEmpty( embedded ); + } + + public List getTransient() { + return defaultToEmpty( _transient ); + } + + public List getPrePersist() { + return defaultToEmpty( prePersist ); + } + + public List getPostPersist() { + return defaultToEmpty( postPersist ); + } + + public List getPreRemove() { + return defaultToEmpty( preRemove ); + } + + public List getPostRemove() { + return defaultToEmpty( postRemove ); + } + + public List getPreUpdate() { + return defaultToEmpty( preUpdate ); + } + + public List getPostUpdate() { + return defaultToEmpty( postUpdate ); + } + + public List getPostLoad() { + return defaultToEmpty( postLoad ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java new file mode 100644 index 0000000000..f009b708c4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java @@ -0,0 +1,332 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.cfg.annotations.reflection.internal; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.persistence.AccessType; +import javax.persistence.AttributeConverter; + +import org.hibernate.AnnotationException; +import org.hibernate.boot.AttributeConverterInfo; +import org.hibernate.boot.jaxb.mapping.spi.JaxbConverter; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListener; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListeners; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.mapping.spi.JaxbMappedSuperclass; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaults; +import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitMetadata; +import org.hibernate.boot.jaxb.mapping.spi.ManagedType; +import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.ClassLoaderAccess; +import org.hibernate.cfg.AttributeConverterDefinition; +import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector; +import org.hibernate.internal.CoreLogging; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.StringHelper; + +/** + * A helper for consuming orm.xml mappings. + * + * @author Emmanuel Bernard + * @author Brett Meyer + */ +public class XMLContext implements Serializable { + private static final CoreMessageLogger LOG = CoreLogging.messageLogger( XMLContext.class ); + + private final ClassLoaderAccess classLoaderAccess; + + private Default globalDefaults; + private final Map managedTypeOverride = new HashMap<>(); + private final Map entityListenerOverride = new HashMap<>(); + private final Map defaultsOverride = new HashMap<>(); + private final List defaultElements = new ArrayList<>(); + private final List defaultEntityListeners = new ArrayList<>(); + private boolean hasContext = false; + + /** + * @deprecated Use {@link org.hibernate.cfg.annotations.reflection.XMLContext#XMLContext(BootstrapContext)} instead. + */ + @Deprecated + public XMLContext(ClassLoaderAccess classLoaderAccess) { + this.classLoaderAccess = classLoaderAccess; + } + + public XMLContext(BootstrapContext bootstrapContext) { + this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); + } + + /** + * @param entityMappings The xml document to add + * @return Add an xml document to this context and return the list of added class names. + */ + @SuppressWarnings( "unchecked" ) + public List addDocument(JaxbEntityMappings entityMappings) { + hasContext = true; + List addedClasses = new ArrayList<>(); + //global defaults + JaxbPersistenceUnitMetadata metadata = entityMappings.getPersistenceUnitMetadata(); + if ( metadata != null ) { + if ( globalDefaults == null ) { + globalDefaults = new Default(); + globalDefaults.setMetadataComplete( + metadata.getXmlMappingMetadataComplete() != null ? + Boolean.TRUE : + null + ); + JaxbPersistenceUnitDefaults defaultElement = metadata.getPersistenceUnitDefaults(); + if ( defaultElement != null ) { + globalDefaults.setSchema( defaultElement.getSchema() ); + globalDefaults.setCatalog( defaultElement.getCatalog() ); + globalDefaults.setAccess( defaultElement.getAccess() ); + globalDefaults.setCascadePersist( defaultElement.getCascadePersist() != null ? Boolean.TRUE : null ); + globalDefaults.setDelimitedIdentifiers( defaultElement.getDelimitedIdentifiers() != null ? Boolean.TRUE : null ); + defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement.getEntityListeners(), null, addedClasses ) ); + } + } + else { + LOG.duplicateMetadata(); + } + } + + //entity mapping default + Default entityMappingDefault = new Default(); + String packageName = entityMappings.getPackage(); + entityMappingDefault.setPackageName( packageName ); + entityMappingDefault.setSchema( entityMappings.getSchema() ); + entityMappingDefault.setCatalog( entityMappings.getCatalog() ); + entityMappingDefault.setAccess( entityMappings.getAccess() ); + defaultElements.add( entityMappings ); + + setLocalAttributeConverterDefinitions( entityMappings.getConverter() ); + + addClass( entityMappings.getEntity(), packageName, entityMappingDefault, addedClasses ); + + addClass( entityMappings.getMappedSuperclass(), packageName, entityMappingDefault, addedClasses ); + + addClass( entityMappings.getEmbeddable(), packageName, entityMappingDefault, addedClasses ); + + return addedClasses; + } + + private void addClass(List managedTypes, String packageName, Default defaults, List addedClasses) { + for (ManagedType element : managedTypes) { + String className = buildSafeClassName( element.getClazz(), packageName ); + if ( managedTypeOverride.containsKey( className ) ) { + //maybe switch it to warn? + throw new IllegalStateException( "Duplicate XML entry for " + className ); + } + addedClasses.add( className ); + managedTypeOverride.put( className, element ); + Default localDefault = new Default(); + localDefault.override( defaults ); + localDefault.setMetadataComplete( element.isMetadataComplete() ); + localDefault.setAccess( element.getAccess() ); + defaultsOverride.put( className, localDefault ); + + LOG.debugf( "Adding XML overriding information for %s", className ); + if ( element instanceof JaxbEntity ) { + addEntityListenerClasses( ( (JaxbEntity) element ).getEntityListeners(), packageName, addedClasses ); + } + else if ( element instanceof JaxbMappedSuperclass ) { + addEntityListenerClasses( ( (JaxbMappedSuperclass) element ).getEntityListeners(), packageName, addedClasses ); + } + } + } + + private List addEntityListenerClasses(JaxbEntityListeners listeners, String packageName, List addedClasses) { + List localAddedClasses = new ArrayList<>(); + if ( listeners != null ) { + List elements = listeners.getEntityListener(); + for (JaxbEntityListener listener : elements) { + String listenerClassName = buildSafeClassName( listener.getClazz(), packageName ); + if ( entityListenerOverride.containsKey( listenerClassName ) ) { + LOG.duplicateListener( listenerClassName ); + continue; + } + localAddedClasses.add( listenerClassName ); + entityListenerOverride.put( listenerClassName, listener ); + } + } + LOG.debugf( "Adding XML overriding information for listeners: %s", localAddedClasses ); + addedClasses.addAll( localAddedClasses ); + return localAddedClasses; + } + + @SuppressWarnings("unchecked") + private void setLocalAttributeConverterDefinitions(List converterElements) { + for ( JaxbConverter converterElement : converterElements ) { + final String className = converterElement.getClazz(); + final boolean autoApply = Boolean.TRUE.equals( converterElement.isAutoApply() ); + + try { + final Class attributeConverterClass = classLoaderAccess.classForName( + className + ); + attributeConverterInfoList.add( + new AttributeConverterDefinition( attributeConverterClass.newInstance(), autoApply ) + ); + } + catch (ClassLoadingException e) { + throw new AnnotationException( "Unable to locate specified AttributeConverter implementation class : " + className, e ); + } + catch (Exception e) { + throw new AnnotationException( "Unable to instantiate specified AttributeConverter implementation class : " + className, e ); + } + } + } + + public static String buildSafeClassName(String className, String defaultPackageName) { + if ( className.indexOf( '.' ) < 0 && StringHelper.isNotEmpty( defaultPackageName ) ) { + className = StringHelper.qualify( defaultPackageName, className ); + } + return className; + } + + public static String buildSafeClassName(String className, Default defaults) { + return buildSafeClassName( className, defaults.getPackageName() ); + } + + public Default getDefault(String className) { + Default xmlDefault = new Default(); + xmlDefault.override( globalDefaults ); + if ( className != null ) { + Default entityMappingOverriding = defaultsOverride.get( className ); + xmlDefault.override( entityMappingOverriding ); + } + return xmlDefault; + } + + public ManagedType getManagedTypeOverride(String className) { + return managedTypeOverride.get( className ); + } + + public JaxbEntityListener getEntityListenerOverride(String className) { + return entityListenerOverride.get( className ); + } + + public List getAllDocuments() { + return defaultElements; + } + + public boolean hasContext() { + return hasContext; + } + + private List attributeConverterInfoList = new ArrayList<>(); + + public void applyDiscoveredAttributeConverters(AttributeConverterDefinitionCollector collector) { + for ( AttributeConverterInfo info : attributeConverterInfoList ) { + collector.addAttributeConverter( info ); + } + attributeConverterInfoList.clear(); + } + + public static class Default implements Serializable { + private AccessType access; + private String packageName; + private String schema; + private String catalog; + private Boolean metadataComplete; + private Boolean cascadePersist; + private Boolean delimitedIdentifier; + + public AccessType getAccess() { + return access; + } + + protected void setAccess(AccessType access) { + this.access = access; + } + + public String getCatalog() { + return catalog; + } + + protected void setCatalog(String catalog) { + this.catalog = catalog; + } + + public String getPackageName() { + return packageName; + } + + protected void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String getSchema() { + return schema; + } + + protected void setSchema(String schema) { + this.schema = schema; + } + + public Boolean getMetadataComplete() { + return metadataComplete; + } + + public boolean canUseJavaAnnotations() { + return metadataComplete == null || !metadataComplete; + } + + protected void setMetadataComplete(Boolean metadataComplete) { + this.metadataComplete = metadataComplete; + } + + public Boolean getCascadePersist() { + return cascadePersist; + } + + void setCascadePersist(Boolean cascadePersist) { + this.cascadePersist = cascadePersist; + } + + public void override(Default globalDefault) { + if ( globalDefault != null ) { + if ( globalDefault.getAccess() != null ) { + access = globalDefault.getAccess(); + } + if ( globalDefault.getPackageName() != null ) { + packageName = globalDefault.getPackageName(); + } + if ( globalDefault.getSchema() != null ) { + schema = globalDefault.getSchema(); + } + if ( globalDefault.getCatalog() != null ) { + catalog = globalDefault.getCatalog(); + } + if ( globalDefault.getDelimitedIdentifier() != null ) { + delimitedIdentifier = globalDefault.getDelimitedIdentifier(); + } + if ( globalDefault.getMetadataComplete() != null ) { + metadataComplete = globalDefault.getMetadataComplete(); + } + //TODO fix that in stone if cascade-persist is set already? + if ( globalDefault.getCascadePersist() != null ) cascadePersist = globalDefault.getCascadePersist(); + } + } + + public void setDelimitedIdentifiers(Boolean delimitedIdentifier) { + this.delimitedIdentifier = delimitedIdentifier; + } + + public Boolean getDelimitedIdentifier() { + return delimitedIdentifier; + } + } + + public List getDefaultEntityListeners() { + return defaultEntityListeners; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 44249e9d2f..42f29e5dc0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -265,7 +265,8 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator { public void afterTransaction() { transactionTimeOutInstant = -1; if ( getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_STATEMENT || - getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION ) { + getConnectionReleaseMode() == ConnectionReleaseMode.AFTER_TRANSACTION || + getConnectionReleaseMode() == ConnectionReleaseMode.BEFORE_TRANSACTION_COMPLETION ) { this.logicalConnection.afterTransaction(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 5606aa410a..178c35ee6e 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -18,6 +18,7 @@ import java.util.StringTokenizer; import org.hibernate.EntityMode; import org.hibernate.MappingException; +import org.hibernate.boot.model.CustomSql; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.engine.OptimisticLockStyle; @@ -754,6 +755,16 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl return properties.iterator(); } + public void setCustomSqlInsert(CustomSql customSql) { + if ( customSql != null ) { + setCustomSQLInsert( + customSql.getSql(), + customSql.isCallable(), + customSql.getCheckStyle() + ); + } + } + public void setCustomSQLInsert(String customSQLInsert, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLInsert = customSQLInsert; this.customInsertCallable = callable; @@ -772,6 +783,16 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl return insertCheckStyle; } + public void setCustomSqlUpdate(CustomSql customSql) { + if ( customSql != null ) { + setCustomSQLUpdate( + customSql.getSql(), + customSql.isCallable(), + customSql.getCheckStyle() + ); + } + } + public void setCustomSQLUpdate(String customSQLUpdate, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLUpdate = customSQLUpdate; this.customUpdateCallable = callable; @@ -790,6 +811,16 @@ public abstract class PersistentClass implements AttributeContainer, Serializabl return updateCheckStyle; } + public void setCustomSqlDelete(CustomSql customSql) { + if ( customSql != null ) { + setCustomSQLDelete( + customSql.getSql(), + customSql.isCallable(), + customSql.getCheckStyle() + ); + } + } + public void setCustomSQLDelete(String customSQLDelete, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { this.customSQLDelete = customSQLDelete; this.customDeleteCallable = callable; diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java index 31efe455ba..b49d83df80 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java @@ -13,6 +13,8 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.Objects; import java.util.Properties; import javax.persistence.AttributeConverter; @@ -411,6 +413,18 @@ public abstract class SimpleValue implements KeyValue { this.identifierGeneratorProperties = identifierGeneratorProperties; } + /** + * Sets the identifierGeneratorProperties. + * @param identifierGeneratorProperties The identifierGeneratorProperties to set + */ + public void setIdentifierGeneratorProperties(Map identifierGeneratorProperties) { + if ( identifierGeneratorProperties != null ) { + Properties properties = new Properties(); + properties.putAll( identifierGeneratorProperties ); + setIdentifierGeneratorProperties( properties ); + } + } + /** * Sets the identifierGeneratorStrategy. * @param identifierGeneratorStrategy The identifierGeneratorStrategy to set @@ -702,7 +716,15 @@ public abstract class SimpleValue implements KeyValue { public void setTypeParameters(Properties parameterMap) { this.typeParameters = parameterMap; } - + + public void setTypeParameters(Map parameters) { + if ( parameters != null ) { + Properties properties = new Properties(); + properties.putAll( parameters ); + setTypeParameters( properties ); + } + } + public Properties getTypeParameters() { return typeParameters; } diff --git a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java index 642cadcfa6..d08f420592 100644 --- a/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java @@ -166,8 +166,10 @@ public class LogicalConnectionManagedImpl extends AbstractLogicalConnectionImple super.afterTransaction(); if ( connectionHandlingMode.getReleaseMode() != ConnectionReleaseMode.ON_CLOSE ) { - // NOTE : we check for !ON_CLOSE here (rather than AFTER_TRANSACTION) to also catch AFTER_STATEMENT cases - // that were circumvented due to held resources + // NOTE : we check for !ON_CLOSE here (rather than AFTER_TRANSACTION) to also catch: + // - AFTER_STATEMENT cases that were circumvented due to held resources + // - BEFORE_TRANSACTION_COMPLETION cases that were circumvented because a rollback occurred + // (we don't get a beforeTransactionCompletion event on rollback). log.debug( "Initiating JDBC connection release from afterTransaction" ); releaseConnection(); } diff --git a/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-2.1.0.xsd b/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-2.1.0.xsd new file mode 100644 index 0000000000..d1b364771b --- /dev/null +++ b/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-2.1.0.xsd @@ -0,0 +1,2543 @@ + + + + + + + ... + + ]]> + + + + + + + + + + The entity-mappings element is the root element of a mapping + file. It contains the following four types of elements: + + 1. The persistence-unit-metadata element contains metadata + for the entire persistence unit. The behavior is undefined if this element + occurs in multiple mapping files within the same persistence unit. + + 2. The package, schema, catalog and access elements apply to all of + the entity, mapped-superclass and embeddable elements defined in + the same file in which they occur. + + 3. The sequence-generator, table-generator, converter, named-query, + named-native-query, named-stored-procedure-query, and + sql-result-set-mapping elements are global to the persistence + unit. + + a. The behavior is undefined when having more than one sequence-generator + or table-generator occur in a persistence unit (whether in the same or + different mapping file). + + b. The behavior is undefined when having more than one named-query, + named-native-query, sql-result-set-mapping, or named-stored-procedure-query + of the same name in a persistence unit (whether in the same or different + mapping file). + + c. The behavior is undefined when having more than one converter for the same + target type in a persistence unit (whether in the same or different mapping file). + + 4. The entity, mapped-superclass and embeddable elements each define + the mapping information for a managed persistent class. The mapping + information contained in these elements may be complete or it may + be partial. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Metadata that applies to the persistence unit and not just to the mapping + file in which it is contained. + + If the xml-mapping-metadata-complete element is specified, + the complete set of mapping metadata for the persistence unit + is contained in the XML mapping files for the persistence unit. + + + + + + + + + + + + + + + These defaults are applied to the persistence unit as a whole unless they + are overridden by local annotation or XML element settings. + + schema - Used as the schema for all tables, secondary tables, join + tables, collection tables, sequence generators, and table + generators that apply to the persistence unit + + catalog - Used as the catalog for all tables, secondary tables, join + tables, collection tables, sequence generators, and table + generators that apply to the persistence unit + + delimited-identifiers - Used to treat database identifiers as + delimited identifiers. + + access - Used as the access type for all managed classes in + the persistence unit + + cascade-persist - Adds cascade-persist to the set of cascade options + in all entity relationships of the persistence unit + + entity-listeners - List of default entity listeners to be invoked + on each entity in the persistence unit. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + javax.persistence.AccessType enum values + + + + + + + + + + + + + + + Hibernate specific "any" mapping, which is a polymorphic association to any different + tables based on a discriminator + + the given identifier type. The first listed column is a VARCHAR column + holding the name of the class (for that row). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.acme.Employee + ]]> + + + + + + + + + + + + + + + This element contains the entity field or property mappings. + It may be sparsely populated to include only a subset of the + fields or properties. If metadata-complete for the entity is true + then the remainder of the attributes will be defaulted according + to the default rules. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.AssociationOverride + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.AttributeOverride + + + + + + + + + + + + + + + + See javax.persistence.Basic + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the org.hibernate.annotations.Cache annotation. + + Used to specify Hibernate-specific extra control over the caching + of entity and collection state. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.hibernate.CacheMode enum values + + + + + + + + + + + + + + + + + + javax.persistence.CascadeType enum values + + + + + + + + + + + + + + + + + + + org.hibernate.annotations.CascadeType enum values + + + + + + + + + + + + + + + + + + + + + + org.hibernate.annotations.OnDeleteAction enum values + + + + + + + + + + + + + + + @CollectionTable annotation + + + + + + + + + + + + + + + + + + + + + + + See the javax.persistence.Column annotation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.Convert annotation + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.Converter annotation + + + + + + + + + + + + + + + + @DiscriminatorColumn annotation + + + + + + + + + + + + + + + + + javax.persistence.DiscriminatorType enum values + + + + + + + + + + + + + + + + + @Target({TYPE}) @Retention(RUNTIME) + public @interface DiscriminatorValue { + String value(); + } + + + + + + + + + + + + + Corresponds to the @ElementCollection annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + See javax.persistence.Embeddable + + Defines the settings and mappings for embeddable objects. + + Again, with metadata-complete=false the mapping is used in + conjunction with annotations. Alternatively, metadata-complete=true + can be used to indicate that no annotations are to be processed + in the class. If this is the case then the defaulting rules will + be recursively applied. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.Embedded annotation + + + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.EmbeddedId annotation + + + + + + + + + + + + + + + + + See javax.persistence.Entity + + Defines the settings and mappings for an entity. + + May be used in 2 ways: + 1. sparsely populated (metadata-complete=false) and used in + conjunction with the annotations. + 2. as complete self-contained metadata (metadata-complete=true) + indicating that no annotations on the entity class (and its fields + or properties) are to be processed. If this is the case then + the defaulting rules for the entity and its subelements will + be recursively applied. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the JPA javax.persistence.EntityListeners annotation + + + + + + + + + + + + + + Corresponds to the JPA javax.persistence.EntityListener annotation + + Defines an entity listener to be invoked at lifecycle events for + the entities that list this listener. + + + + + + + + + + + + + + + + + + + + + + javax.persistence.EnumType enum values + + + + + + + + + + + + + + + Corresponds to the javax.persistence.Enumerated annotation. + + + + + + + + + + + + javax.persistence.FetchType enum values + + + + + + + + + + + + + + + org.hibernate.annotations.FetchMode enum values + + + + + + + + + + + + + + + + org.hibernate.FlushMode enum values + + + + + + + + + + + + + + + + @ForeignKey annotation + + + + + + + + + + + + + + + + + + See the javax.persistence.GeneratedValue annotation + + + + + + + + + + javax.persistence.GenerationType rnum values + + todo : add custom ones like INCREMENT, UUID, etc + + + + + + + + + + + + + + + + + Hibernate-specific element used declare and short-name custom + org.hibernate.id.IdentifierGenerator implementations + + + + + + + + + + + + + Corresponds to the javax.persistence.Id annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.IdClass annotation + + + + + + + + + + + + Corresponds to @Index annotation + + + + + + + + + + + + + + + + + + + Corresponds to the @Inheritance annotation + + + + + + + + + + + + Corresponds to the JPA InheritanceType enumeration values + Hibernate's UNION_SUBCLASS + + todo : make a singular enum to cover these + + + + + + + + + + + + + + + + + JoinColumn annotation + + + + + + + + + + + + + + + + + + + + @JoinTable annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to javax.persistence.Lob (marker) annotation + + + + + + + + + + + javax.persistence.LockModeType enum values + + + + + + + + + + + + + + + + + + + + + Hibernate specific "any" mapping (plural form), which is a polymorphic association to any different + tables based on a discriminator. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @ManyToMany annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the @ManyToOne annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.MapKey + + + + + + + + + + + + @javax.persistence.MapKeyClass + + + + + + + + + + + + + @javax.persistence.MapKeyColumn + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.MapKeyJoinColumn + + + + + + + + + + + + + + + + + + + + See javax.persistence.MappedSuperclass + + Defines the settings and mappings for a mapped superclass. + + May be used in 2 ways: + 1. sparsely populated (metadata-complete=false) and used in + conjunction with the annotations. + 2. as complete self-contained metadata (metadata-complete=true) + indicating that no annotations are to be processed. If this is + the case then the defaulting rules will be recursively applied. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hibernate-specific element used to describe the meta-value (discriminator) + mapping for ANY associations. + + + + + + + + + + + + + + @NamedEntityGraph annotation + + + + + + + + + + + + + + + + + @NamedAttributeNode annotation + + + + + + + + + + + + @NamedSubgraph annotation + + + + + + + + + + + + + + + + + + Common Hibernate specific extensions available for named query definitions. + + todo : a lot of these extensions could be handled by JPA QueryHint scheme + + + + + + + + + + + + + + + + + + + + + + + Mix of @javax.persistence.NamedNativeQuery and @org.hibernate.annotations.NamedNativeQuery + + + + + + + + + + + + + + + + + + + + + + Mix of @javax.persistence.NamedQuery and @org.hibernate.annotations.NamedQuery + + + + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.NamedStoredProcedureQuery annotation + + + + + + + + + + + + + + + + + + + Corresponds to javax.persistence.StoredProcedureParameter annotation + + + + + + + + + + + + + + + + javax.persistence.ParameterMode enum values + + + + + + + + + + + + + + + + + + @OneToMany annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Corresponds to the @OneToOne annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.OrderBy annotation + + + + + + + + + + + + + @javax.persistence.OrderColumn annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PostLoad annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PostPersist annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PostRemove annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PostUpdate annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PrePersist annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PreRemove annotation + + + + + + + + + + + + + + + + + Corresponds to the javax.persistence.PreUpdate annotation + + + + + + + + + + + + + + + + + @PrimaryKeyJoinColumn annotation + + + + + + + + + + + + + + + @javax.persistence.QueryHint + + + + + + + + + + + + + + + + + + Used only by tools to generate finder methods for named queries + + + + + + + + + + + + + + @javax.persistence.SecondaryTable + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.SequenceGenerator + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.SqlResultSetMapping + + + + + + + + + + + + + + + + + + @javax.persistence.ColumnResult + + + + + + + + + + + + @javax.persistence.ConstructorResult + + + + + + + + + + + + + + + @javax.persistence.EntityResult + + + + + + + + + + + + + + + + @javax.persistence.FieldResult + + + + + + + + + + + + + + + + + + + + + @javax.persistence.Table + + + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.TableGenerator + + + + + + + + + + + + + + + + + + + + + + + + + + + + @javax.persistence.Temporal + + + + + + + + + + + + javax.persistence.TemporalType enum values + + + + + + + + + + + + + + + + @javax.persistence.Transient + + + + + + + + + + + + @javax.persistence.UniqueConstraint + + + + + + + + + + + + + + + + + @javax.persistence.Version + + + + + + + + + + + + + + + + + + + + + + element defines a single path to which the fetch + refers, as well as the style of fetch to apply. The 'root' of the + path is different depending upon the context in which the + containing occurs; within a element, + the entity-name of the containing class mapping is assumed... + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + Specifies a filter definition. After definition, a filter + can be applied to entity or collection by name. + + + + + + + + + Used to identify all bind parameters in the condition elemement + + + + + + + + + + + + + + + + Applies a filter defined by hbm-filter-def usage + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Names a org.hibernate.id.IdentifierGenerator implementation (class attribute) + as well as any configuration information need by the implementation (Hibernate + will pass it the parameters after instantiation). + + + + + + + + + + + + + Corresponds to the org.hibernate.annotations.Parameter annotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The loader element allows specification of a named query to be used for fetching + an entity or collection + + + + + + + + + + + + + + + + + + + + + + + + + tags from hbm.xml dtd; renamed here for + self-documentation. This information is intended mainly for tooling. + ]]> + + + + + + + + + + + + + + + + Corresponds to the org.hibernate.annotations.Type annotation, naming + a org.hibernate.type.* or org.hibernate.usertype.* implementation to use. + + name - names the type implementation class + + param - If the type is able to accept parameters (implementation stems from + org.hibernate.type.Type, org.hibernate.type.CollectionType, or + org.hibernate.usertype.ParameterizedType) the specified parameters will be + passed to the type instance after instantiation. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A natural-id element allows declaration of unique business key + + + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/main/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb new file mode 100644 index 0000000000..3ca2d1a54c --- /dev/null +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware + + + + org.hibernate.boot.jaxb.mapping.spi.ManagedType + org.hibernate.boot.jaxb.mapping.spi.EntityOrMappedSuperclass + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer + + + org.hibernate.boot.jaxb.mapping.spi.ManagedType + + + org.hibernate.boot.jaxb.mapping.spi.ManagedType + org.hibernate.boot.jaxb.mapping.spi.EntityOrMappedSuperclass + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer + + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallbackContainer + + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.FetchableAttribute + org.hibernate.boot.jaxb.mapping.spi.AssociationAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.CollectionAttribute + org.hibernate.boot.jaxb.mapping.spi.AssociationAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.FetchableAttribute + org.hibernate.boot.jaxb.mapping.spi.AssociationAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.CollectionAttribute + org.hibernate.boot.jaxb.mapping.spi.AssociationAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.CollectionAttribute + + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + org.hibernate.boot.jaxb.mapping.spi.LifecycleCallback + + + + org.hibernate.boot.jaxb.mapping.spi.AttributesContainer + + + org.hibernate.boot.jaxb.mapping.spi.AttributesContainer + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/main/xjb/orm-bindings.xjb b/hibernate-core/src/main/xjb/orm-bindings.xjb deleted file mode 100644 index 940eb3a86a..0000000000 --- a/hibernate-core/src/main/xjb/orm-bindings.xjb +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XMLMappingHelper.java b/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XMLMappingHelper.java new file mode 100644 index 0000000000..11d3628fe4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XMLMappingHelper.java @@ -0,0 +1,51 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.internal.util.xml; + +import java.io.IOException; +import java.io.InputStream; + +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.internal.MappingBinder; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.service.spi.ServiceRegistryImplementor; + +import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; +import org.junit.Assert; + +/** + * A small helper class for parsing XML mappings, to be used in unit tests. + */ +public final class XMLMappingHelper { + private final MappingBinder binder; + + public XMLMappingHelper(XmlMappingOptions xmlMappingOptions) { + binder = new MappingBinder( ClassLoaderServiceTestingImpl.INSTANCE, true, xmlMappingOptions ); + } + + public JaxbEntityMappings readOrmXmlMappings(String name) throws IOException { + try (InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( name )) { + return readOrmXmlMappings( is, name ); + } + } + + public JaxbEntityMappings readOrmXmlMappings(InputStream is, String name) { + try { + Assert.assertNotNull( "Resource not found: " + name, is ); + Binding binding = binder.bind( is, new Origin( SourceType.JAR, name ) ); + return (JaxbEntityMappings) binding.getRoot(); + } + catch (RuntimeException e) { + throw new IllegalStateException( "Could not parse orm.xml mapping '" + name + "': " + e.getMessage(), e ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XmlMappingOptionsStrategyRegistrationProvider.java b/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XmlMappingOptionsStrategyRegistrationProvider.java new file mode 100644 index 0000000000..97c6a617ce --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/internal/util/xml/XmlMappingOptionsStrategyRegistrationProvider.java @@ -0,0 +1,66 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.internal.util.xml; + +import java.util.Collections; + +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl; +import org.hibernate.boot.registry.selector.StrategyRegistration; +import org.hibernate.boot.registry.selector.StrategyRegistrationProvider; + +import org.jboss.logging.Logger; + +/** + * A strategy registration provider that allows running the whole test suite with different XML mapping options. + *

+ * By default, this provider does nothing. + * In some CI jobs, we set the system property {@value STRATEGY_PROPERTY_KEY} + * to re-run the whole test suite using JAXB for orm.xml parsing instead of dom4j. + */ +public class XmlMappingOptionsStrategyRegistrationProvider implements StrategyRegistrationProvider { + + protected final Logger log = Logger.getLogger( getClass() ); + + private static final String STRATEGY_PROPERTY_KEY = "testing.mapping.xml.strategy"; + + public static void applyJaxbStrategy(BootstrapServiceRegistryBuilder builder) { + builder.applyStrategySelector( XmlMappingOptions.class, XmlMappingOptions.DEFAULT_NAME, + PreferJaxbXmlMappingOptions.class + ); + } + + @Override + public Iterable getStrategyRegistrations() { + switch ( getStrategyFromSystemProperties() ) { + case "jaxb": + log.warn( "Overriding the default configuration because of a test system property:" + + " will favor jaxb when parsing XML mapping." ); + return Collections.singleton( + new SimpleStrategyRegistrationImpl<>( XmlMappingOptions.class, + PreferJaxbXmlMappingOptions.class, + XmlMappingOptions.DEFAULT_NAME ) + ); + case "default": + default: + return Collections.emptyList(); + } + } + + private static String getStrategyFromSystemProperties() { + String strategy = System.getProperty( STRATEGY_PROPERTY_KEY ); + return strategy == null || strategy.isEmpty() ? "default" : strategy; + } + + public static class PreferJaxbXmlMappingOptions implements XmlMappingOptions { + @Override + public boolean isPreferJaxb() { + return true; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTest.java index 8c80c7e921..2a36c6c8b0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/functional/SQLServerDialectTest.java @@ -55,10 +55,9 @@ import static org.junit.Assert.assertTrue; public class SQLServerDialectTest extends BaseCoreFunctionalTestCase { @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.setProperty( AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, Boolean.TRUE.toString() ); - return configuration; } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java index de8d20a4a2..54ce61fc15 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java @@ -6,6 +6,9 @@ */ package org.hibernate.test.annotations.reflection; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -13,9 +16,15 @@ import org.junit.Test; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertEquals; -@TestForIssue( jiraKey = "HHH-11924") +@TestForIssue(jiraKey = {"HHH-11924", "HHH-14529"}) public class ElementCollectionConverterTest extends BaseCoreFunctionalTestCase { + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); + } + @Override protected Class[] getAnnotatedClasses() { return new Class[] { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java new file mode 100644 index 0000000000..1704cce53e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java @@ -0,0 +1,419 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.reflection; + +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import org.hibernate.annotations.Columns; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.cfg.EJB3DTDEntityResolver; +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader; +import org.hibernate.cfg.annotations.reflection.internal.XMLContext; +import org.hibernate.internal.util.xml.ErrorLogger; +import org.hibernate.internal.util.xml.XMLHelper; +import org.hibernate.internal.util.xml.XMLMappingHelper; + +import org.hibernate.testing.boot.BootstrapContextImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; +import org.junit.Test; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotSupportedException; + +import javax.persistence.*; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static org.junit.Assert.*; + +/** + * Tests the new {@link JPAXMLOverriddenAnnotationReader}, + * which will be replacing {@link JPAOverriddenAnnotationReader}. + * {@link JPAOverriddenAnnotationReader} is still the default implementation, + * but we want to switch to {@link JPAXMLOverriddenAnnotationReader} + * as soon as it will be practical. + * + * @see LegacyJPAOverriddenAnnotationReaderTest + * @author Emmanuel Bernard + */ +@TestForIssue(jiraKey = "HHH-14529") +public class JPAXMLOverriddenAnnotationReaderTest extends BaseUnitTestCase { + @Test + public void testMappedSuperclassAnnotations() throws Exception { + XMLContext context = buildContext( + "org/hibernate/test/annotations/reflection/metadata-complete.xml" + ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( Organization.class, context, BootstrapContextImpl.INSTANCE ); + assertTrue( reader.isAnnotationPresent( MappedSuperclass.class ) ); + } + + @Test + public void testEntityRelatedAnnotations() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( Administration.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Entity.class ) ); + assertEquals( + "Default value in xml entity should not override @Entity.name", "JavaAdministration", + reader.getAnnotation( Entity.class ).name() + ); + assertNotNull( reader.getAnnotation( Table.class ) ); + assertEquals( "@Table not overridden", "tbl_admin", reader.getAnnotation( Table.class ).name() ); + assertEquals( "Default schema not overridden", "myschema", reader.getAnnotation( Table.class ).schema() ); + assertEquals( + "Proper @Table.uniqueConstraints", 2, + reader.getAnnotation( Table.class ).uniqueConstraints()[0].columnNames().length + ); + String columnName = reader.getAnnotation( Table.class ).uniqueConstraints()[0].columnNames()[0]; + assertTrue( + "Proper @Table.uniqueConstraints", "firstname".equals( columnName ) || "lastname".equals( columnName ) + ); + assertNull( "Both Java and XML used", reader.getAnnotation( SecondaryTable.class ) ); + assertNotNull( "XML does not work", reader.getAnnotation( SecondaryTables.class ) ); + SecondaryTable[] tables = reader.getAnnotation( SecondaryTables.class ).value(); + assertEquals( 1, tables.length ); + assertEquals( "admin2", tables[0].name() ); + assertEquals( "unique constraints ignored", 1, tables[0].uniqueConstraints().length ); + assertEquals( "pk join column ignored", 1, tables[0].pkJoinColumns().length ); + assertEquals( "pk join column ignored", "admin_id", tables[0].pkJoinColumns()[0].name() ); + assertNotNull( "Sequence Overriding not working", reader.getAnnotation( SequenceGenerator.class ) ); + assertEquals( + "wrong sequence name", "seqhilo", reader.getAnnotation( SequenceGenerator.class ).sequenceName() + ); + assertEquals( "default fails", 50, reader.getAnnotation( SequenceGenerator.class ).allocationSize() ); + assertNotNull( "TableOverriding not working", reader.getAnnotation( TableGenerator.class ) ); + assertEquals( "wrong tble name", "tablehilo", reader.getAnnotation( TableGenerator.class ).table() ); + assertEquals( "no schema overriding", "myschema", reader.getAnnotation( TableGenerator.class ).schema() ); + + reader = new JPAXMLOverriddenAnnotationReader( Match.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Table.class ) ); + assertEquals( + "Java annotation not taken into account", "matchtable", reader.getAnnotation( Table.class ).name() + ); + assertEquals( + "Java annotation not taken into account", "matchschema", reader.getAnnotation( Table.class ).schema() + ); + assertEquals( "Overriding not taken into account", "mycatalog", reader.getAnnotation( Table.class ).catalog() ); + assertNotNull( "SecondaryTable swallowed", reader.getAnnotation( SecondaryTables.class ) ); + assertEquals( + "Default schema not taken into account", "myschema", + reader.getAnnotation( SecondaryTables.class ).value()[0].schema() + ); + assertNotNull( reader.getAnnotation( Inheritance.class ) ); + assertEquals( + "inheritance strategy not overriden", InheritanceType.JOINED, + reader.getAnnotation( Inheritance.class ).strategy() + ); + assertNotNull( "NamedQuery not overriden", reader.getAnnotation( NamedQueries.class ) ); + assertEquals( "No deduplication", 3, reader.getAnnotation( NamedQueries.class ).value().length ); + assertEquals( + "deduplication kept the Java version", 1, + reader.getAnnotation( NamedQueries.class ).value()[1].hints().length + ); + assertEquals( + "org.hibernate.timeout", reader.getAnnotation( NamedQueries.class ).value()[1].hints()[0].name() + ); + assertNotNull( "NamedNativeQuery not overriden", reader.getAnnotation( NamedNativeQueries.class ) ); + assertEquals( "No deduplication", 3, reader.getAnnotation( NamedNativeQueries.class ).value().length ); + assertEquals( + "deduplication kept the Java version", 1, + reader.getAnnotation( NamedNativeQueries.class ).value()[1].hints().length + ); + assertEquals( + "org.hibernate.timeout", reader.getAnnotation( NamedNativeQueries.class ).value()[1].hints()[0].name() + ); + assertNotNull( reader.getAnnotation( SqlResultSetMappings.class ) ); + assertEquals( + "competitor1Point", reader.getAnnotation( SqlResultSetMappings.class ).value()[0].columns()[0].name() + ); + assertEquals( + "competitor1Point", + reader.getAnnotation( SqlResultSetMappings.class ).value()[0].entities()[0].fields()[0].column() + ); + assertNotNull( reader.getAnnotation( ExcludeSuperclassListeners.class ) ); + assertNotNull( reader.getAnnotation( ExcludeDefaultListeners.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( Competition.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( MappedSuperclass.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( TennisMatch.class, context, BootstrapContextImpl.INSTANCE ); + assertNull( "Mutualize PKJC into PKJCs", reader.getAnnotation( PrimaryKeyJoinColumn.class ) ); + assertNotNull( reader.getAnnotation( PrimaryKeyJoinColumns.class ) ); + assertEquals( + "PrimaryKeyJoinColumn overrden", "id", + reader.getAnnotation( PrimaryKeyJoinColumns.class ).value()[0].name() + ); + assertNotNull( reader.getAnnotation( AttributeOverrides.class ) ); + assertEquals( "Wrong deduplication", 3, reader.getAnnotation( AttributeOverrides.class ).value().length ); + assertEquals( + "Wrong priority (XML vs java annotations)", "fld_net", + reader.getAnnotation( AttributeOverrides.class ).value()[0].column().name() + ); + assertEquals( + "Column mapping", 2, reader.getAnnotation( AttributeOverrides.class ).value()[1].column().scale() + ); + assertEquals( + "Column mapping", true, reader.getAnnotation( AttributeOverrides.class ).value()[1].column().unique() + ); + assertNotNull( reader.getAnnotation( AssociationOverrides.class ) ); + assertEquals( "no XML processing", 1, reader.getAnnotation( AssociationOverrides.class ).value().length ); + assertEquals( + "wrong xml processing", "id", + reader.getAnnotation( AssociationOverrides.class ).value()[0].joinColumns()[0].referencedColumnName() + ); + + + reader = new JPAXMLOverriddenAnnotationReader( SocialSecurityPhysicalAccount.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( IdClass.class ) ); + assertEquals( "id-class not used", SocialSecurityNumber.class, reader.getAnnotation( IdClass.class ).value() ); + assertEquals( + "discriminator-value not used", "Physical", reader.getAnnotation( DiscriminatorValue.class ).value() + ); + assertNotNull( "discriminator-column not used", reader.getAnnotation( DiscriminatorColumn.class ) ); + assertEquals( + "discriminator-column.name default value broken", "DTYPE", + reader.getAnnotation( DiscriminatorColumn.class ).name() + ); + assertEquals( + "discriminator-column.length broken", 34, reader.getAnnotation( DiscriminatorColumn.class ).length() + ); + } + + @Test + public void testEntityRelatedAnnotationsMetadataComplete() throws Exception { + XMLContext context = buildContext( + "org/hibernate/test/annotations/reflection/metadata-complete.xml" + ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( Administration.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Entity.class ) ); + assertEquals( + "Metadata complete should ignore java annotations", "", reader.getAnnotation( Entity.class ).name() + ); + assertNotNull( reader.getAnnotation( Table.class ) ); + assertEquals( "@Table should not be used", "", reader.getAnnotation( Table.class ).name() ); + assertEquals( "Default schema not overriden", "myschema", reader.getAnnotation( Table.class ).schema() ); + + reader = new JPAXMLOverriddenAnnotationReader( Match.class, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Table.class ) ); + assertEquals( "@Table should not be used", "", reader.getAnnotation( Table.class ).name() ); + assertEquals( "Overriding not taken into account", "myschema", reader.getAnnotation( Table.class ).schema() ); + assertEquals( "Overriding not taken into account", "mycatalog", reader.getAnnotation( Table.class ).catalog() ); + assertNull( "Ignore Java annotation", reader.getAnnotation( SecondaryTable.class ) ); + assertNull( "Ignore Java annotation", reader.getAnnotation( SecondaryTables.class ) ); + assertNull( "Ignore Java annotation", reader.getAnnotation( Inheritance.class ) ); + assertNull( reader.getAnnotation( NamedQueries.class ) ); + assertNull( reader.getAnnotation( NamedNativeQueries.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( TennisMatch.class, context, BootstrapContextImpl.INSTANCE ); + assertNull( reader.getAnnotation( PrimaryKeyJoinColumn.class ) ); + assertNull( reader.getAnnotation( PrimaryKeyJoinColumns.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( Competition.class, context, BootstrapContextImpl.INSTANCE ); + assertNull( reader.getAnnotation( MappedSuperclass.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( SocialSecurityMoralAccount.class, context, BootstrapContextImpl.INSTANCE ); + assertNull( reader.getAnnotation( IdClass.class ) ); + assertNull( reader.getAnnotation( DiscriminatorValue.class ) ); + assertNull( reader.getAnnotation( DiscriminatorColumn.class ) ); + assertNull( reader.getAnnotation( SequenceGenerator.class ) ); + assertNull( reader.getAnnotation( TableGenerator.class ) ); + } + + @Test + public void testIdRelatedAnnotations() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + Method method = Administration.class.getDeclaredMethod( "getId" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertNull( reader.getAnnotation( Id.class ) ); + assertNull( reader.getAnnotation( Column.class ) ); + Field field = Administration.class.getDeclaredField( "id" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Id.class ) ); + assertNotNull( reader.getAnnotation( GeneratedValue.class ) ); + assertEquals( GenerationType.SEQUENCE, reader.getAnnotation( GeneratedValue.class ).strategy() ); + assertEquals( "generator", reader.getAnnotation( GeneratedValue.class ).generator() ); + assertNotNull( reader.getAnnotation( SequenceGenerator.class ) ); + assertEquals( "seq", reader.getAnnotation( SequenceGenerator.class ).sequenceName() ); + assertNotNull( reader.getAnnotation( Columns.class ) ); + assertEquals( 1, reader.getAnnotation( Columns.class ).columns().length ); + assertEquals( "fld_id", reader.getAnnotation( Columns.class ).columns()[0].name() ); + assertNotNull( reader.getAnnotation( Temporal.class ) ); + assertEquals( TemporalType.DATE, reader.getAnnotation( Temporal.class ).value() ); + + context = buildContext( + "org/hibernate/test/annotations/reflection/metadata-complete.xml" + ); + method = Administration.class.getDeclaredMethod( "getId" ); + reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( + "Default access type when not defined in metadata complete should be property", + reader.getAnnotation( Id.class ) + ); + field = Administration.class.getDeclaredField( "id" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNull( + "Default access type when not defined in metadata complete should be property", + reader.getAnnotation( Id.class ) + ); + + method = BusTrip.class.getDeclaredMethod( "getId" ); + reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertNull( reader.getAnnotation( EmbeddedId.class ) ); + field = BusTrip.class.getDeclaredField( "id" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( EmbeddedId.class ) ); + assertNotNull( reader.getAnnotation( AttributeOverrides.class ) ); + assertEquals( 1, reader.getAnnotation( AttributeOverrides.class ).value().length ); + } + + @Test + public void testBasicRelatedAnnotations() throws Exception { + XMLContext context = buildContext( + "org/hibernate/test/annotations/reflection/metadata-complete.xml" + ); + Field field = BusTrip.class.getDeclaredField( "status" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Enumerated.class ) ); + assertEquals( EnumType.STRING, reader.getAnnotation( Enumerated.class ).value() ); + assertEquals( false, reader.getAnnotation( Basic.class ).optional() ); + field = BusTrip.class.getDeclaredField( "serial" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Lob.class ) ); + assertEquals( "serialbytes", reader.getAnnotation( Columns.class ).columns()[0].name() ); + field = BusTrip.class.getDeclaredField( "terminusTime" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Temporal.class ) ); + assertEquals( TemporalType.TIMESTAMP, reader.getAnnotation( Temporal.class ).value() ); + assertEquals( FetchType.LAZY, reader.getAnnotation( Basic.class ).fetch() ); + + field = BusTripPk.class.getDeclaredField( "busDriver" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.isAnnotationPresent( Basic.class ) ); + } + + @Test + public void testVersionRelatedAnnotations() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + Method method = Administration.class.getDeclaredMethod( "getVersion" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Version.class ) ); + + Field field = Match.class.getDeclaredField( "version" ); + assertNotNull( reader.getAnnotation( Version.class ) ); + } + + @Test + public void testTransientAndEmbeddedRelatedAnnotations() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + + Field field = Administration.class.getDeclaredField( "transientField" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Transient.class ) ); + assertNull( reader.getAnnotation( Basic.class ) ); + + field = Match.class.getDeclaredField( "playerASSN" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( Embedded.class ) ); + } + + @Test + public void testAssociationRelatedAnnotations() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + + Field field = Administration.class.getDeclaredField( "defaultBusTrip" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( OneToOne.class ) ); + assertNull( reader.getAnnotation( JoinColumns.class ) ); + assertNotNull( reader.getAnnotation( PrimaryKeyJoinColumns.class ) ); + assertEquals( "pk", reader.getAnnotation( PrimaryKeyJoinColumns.class ).value()[0].name() ); + assertEquals( 5, reader.getAnnotation( OneToOne.class ).cascade().length ); + assertEquals( FetchType.LAZY, reader.getAnnotation( OneToOne.class ).fetch() ); + assertEquals( "test", reader.getAnnotation( OneToOne.class ).mappedBy() ); + + context = buildContext( + "org/hibernate/test/annotations/reflection/metadata-complete.xml" + ); + field = BusTrip.class.getDeclaredField( "players" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( OneToMany.class ) ); + assertNotNull( reader.getAnnotation( JoinColumns.class ) ); + assertEquals( 2, reader.getAnnotation( JoinColumns.class ).value().length ); + assertEquals( "driver", reader.getAnnotation( JoinColumns.class ).value()[0].name() ); + assertNotNull( reader.getAnnotation( MapKey.class ) ); + assertEquals( "name", reader.getAnnotation( MapKey.class ).name() ); + + field = BusTrip.class.getDeclaredField( "roads" ); + reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( ManyToMany.class ) ); + assertNotNull( reader.getAnnotation( JoinTable.class ) ); + assertEquals( "bus_road", reader.getAnnotation( JoinTable.class ).name() ); + assertEquals( 2, reader.getAnnotation( JoinTable.class ).joinColumns().length ); + assertEquals( 1, reader.getAnnotation( JoinTable.class ).inverseJoinColumns().length ); + assertEquals( 2, reader.getAnnotation( JoinTable.class ).uniqueConstraints()[0].columnNames().length ); + assertNotNull( reader.getAnnotation( OrderBy.class ) ); + assertEquals( "maxSpeed", reader.getAnnotation( OrderBy.class ).value() ); + } + + @Test + @TestForIssue(jiraKey = "HHH-11924") + public void testElementCollectionConverter() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + + Field field = Company.class.getDeclaredField( "organizations" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( field, context, BootstrapContextImpl.INSTANCE ); + assertNotNull( reader.getAnnotation( ElementCollection.class ) ); + assertNotNull( reader.getAnnotation( Converts.class ) ); + assertNotNull( reader.getAnnotation( Converts.class ).value() ); + assertTrue( reader.getAnnotation( Converts.class ).value().length == 1 ); + assertEquals(OrganizationConverter.class, reader.getAnnotation( Converts.class ).value()[0].converter()); + } + + @Test + public void testEntityListeners() throws Exception { + XMLContext context = buildContext( "org/hibernate/test/annotations/reflection/orm.xml" ); + + Method method = Administration.class.getDeclaredMethod( "calculate" ); + JPAXMLOverriddenAnnotationReader reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertTrue( reader.isAnnotationPresent( PrePersist.class ) ); + + reader = new JPAXMLOverriddenAnnotationReader( Administration.class, context, BootstrapContextImpl.INSTANCE ); + assertTrue( reader.isAnnotationPresent( EntityListeners.class ) ); + assertEquals( 1, reader.getAnnotation( EntityListeners.class ).value().length ); + assertEquals( LogListener.class, reader.getAnnotation( EntityListeners.class ).value()[0] ); + + method = LogListener.class.getDeclaredMethod( "noLog", Object.class ); + reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertTrue( reader.isAnnotationPresent( PostLoad.class ) ); + + method = LogListener.class.getDeclaredMethod( "log", Object.class ); + reader = new JPAXMLOverriddenAnnotationReader( method, context, BootstrapContextImpl.INSTANCE ); + assertTrue( reader.isAnnotationPresent( PrePersist.class ) ); + assertFalse( reader.isAnnotationPresent( PostPersist.class ) ); + + assertEquals( 1, context.getDefaultEntityListeners().size() ); + assertEquals( OtherLogListener.class.getName(), context.getDefaultEntityListeners().get( 0 ) ); + } + + private XMLContext buildContext(String ormfile) throws IOException { + XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() { + @Override + public boolean isPreferJaxb() { + return true; + } + } ); + JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( ormfile ); + XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE ); + context.addDocument( mappings ); + return context; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyElementCollectionConverterTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyElementCollectionConverterTest.java new file mode 100644 index 0000000000..5d1a41eb6e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyElementCollectionConverterTest.java @@ -0,0 +1,71 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.reflection; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; + +/** + * Tests the legacy {@link JPAOverriddenAnnotationReader}, + * which will be replaced with {@link org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader}. + * {@link JPAOverriddenAnnotationReader} is still the default implementation, + * but we want to switch to {@link org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader} + * as soon as it will be practical. + * + * @see JPAXMLOverriddenAnnotationReaderTest + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +@TestForIssue( jiraKey = "HHH-11924") +public class LegacyElementCollectionConverterTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Company.class, + }; + } + + @Override + protected String[] getOrmXmlFiles() { + return new String[] { "org/hibernate/test/annotations/reflection/element-collection-converter-orm.xml" }; + } + + + @Test + public void testConverterIsAppliedToElementCollection() { + doInHibernate( this::sessionFactory, session -> { + Company company = new Company(); + company.setId( 1L ); + + Organization org1 = new Organization(); + org1.setOrganizationId( "ACME" ); + + company.getOrganizations().add( org1 ); + + session.persist( company ); + } ); + + doInHibernate( this::sessionFactory, session -> { + String organizationId = (String) session + .createNativeQuery( "select organizations from Company_organizations" ) + .getSingleResult(); + assertEquals( "ORG-ACME", organizationId ); + + Company company = session.find( Company.class, 1L ); + + assertEquals( 1, company.getOrganizations().size() ); + assertEquals( "ACME" , company.getOrganizations().get( 0 ).getOrganizationId()); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAOverriddenAnnotationReaderTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyJPAOverriddenAnnotationReaderTest.java similarity index 88% rename from hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAOverriddenAnnotationReaderTest.java rename to hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyJPAOverriddenAnnotationReaderTest.java index c79a60e708..57561467cc 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAOverriddenAnnotationReaderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyJPAOverriddenAnnotationReaderTest.java @@ -6,8 +6,61 @@ */ package org.hibernate.test.annotations.reflection; -import org.dom4j.DocumentException; -import org.dom4j.io.SAXReader; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import javax.persistence.AssociationOverrides; +import javax.persistence.AttributeOverrides; +import javax.persistence.Basic; +import javax.persistence.Column; +import javax.persistence.Converts; +import javax.persistence.DiscriminatorColumn; +import javax.persistence.DiscriminatorValue; +import javax.persistence.ElementCollection; +import javax.persistence.Embedded; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.ExcludeDefaultListeners; +import javax.persistence.ExcludeSuperclassListeners; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.Lob; +import javax.persistence.ManyToMany; +import javax.persistence.MapKey; +import javax.persistence.MappedSuperclass; +import javax.persistence.NamedNativeQueries; +import javax.persistence.NamedQueries; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import javax.persistence.OrderBy; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PrePersist; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.PrimaryKeyJoinColumns; +import javax.persistence.SecondaryTable; +import javax.persistence.SecondaryTables; +import javax.persistence.SequenceGenerator; +import javax.persistence.SqlResultSetMappings; +import javax.persistence.Table; +import javax.persistence.TableGenerator; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Transient; +import javax.persistence.Version; + import org.hibernate.annotations.Columns; import org.hibernate.cfg.EJB3DTDEntityResolver; import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; @@ -15,28 +68,37 @@ import org.hibernate.cfg.annotations.reflection.XMLContext; import org.hibernate.internal.util.xml.ErrorLogger; import org.hibernate.internal.util.xml.XMLHelper; -import org.hibernate.testing.boot.BootstrapContextImpl; -import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.boot.BootstrapContextImpl; import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; + +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXNotSupportedException; -import javax.persistence.*; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; /** + * Tests the legacy {@link JPAOverriddenAnnotationReader}, + * which will be replaced with {@link org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader}. + * {@link JPAOverriddenAnnotationReader} is still the default implementation, + * but we want to switch to {@link org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader} + * as soon as it will be practical. + * + * @see JPAXMLOverriddenAnnotationReaderTest * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. */ -public class JPAOverriddenAnnotationReaderTest extends BaseUnitTestCase { +@Deprecated +public class LegacyJPAOverriddenAnnotationReaderTest extends BaseUnitTestCase { @Test public void testMappedSuperclassAnnotations() throws Exception { XMLContext context = buildContext( diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyXMLContextTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyXMLContextTest.java new file mode 100644 index 0000000000..38ade26b4c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyXMLContextTest.java @@ -0,0 +1,73 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.reflection; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.hibernate.cfg.EJB3DTDEntityResolver; +import org.hibernate.cfg.annotations.reflection.XMLContext; +import org.hibernate.internal.util.xml.ErrorLogger; +import org.hibernate.internal.util.xml.XMLHelper; + +import org.hibernate.testing.boot.BootstrapContextImpl; +import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; +import org.junit.Assert; +import org.junit.Test; + +import org.dom4j.io.SAXReader; +import org.xml.sax.InputSource; +import org.xml.sax.SAXNotSupportedException; + +/** + * Tests the legacy {@link XMLContext}, + * which will be replaced with {@link org.hibernate.cfg.annotations.reflection.internal.XMLContext}. + * {@link XMLContext} is still the default implementation, + * but we want to switch to {@link org.hibernate.cfg.annotations.reflection.internal.XMLContext} + * as soon as it will be practical. + * + * @see XMLContextTest + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link XMLContext}. + */ +public class LegacyXMLContextTest { + @Test + public void testAll() throws Exception { + final XMLHelper xmlHelper = new XMLHelper(); + final XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE ); + + InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( + "org/hibernate/test/annotations/reflection/orm.xml" + ); + Assert.assertNotNull( "ORM.xml not found", is ); + + final ErrorLogger errorLogger = new ErrorLogger(); + final SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE ); + + try { + saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true ); + } + catch ( SAXNotSupportedException e ) { + saxReader.setValidation( false ); + } + org.dom4j.Document doc; + try { + doc = saxReader.read( new InputSource( new BufferedInputStream( is ) ) ); + } + finally { + try { + is.close(); + } + catch ( IOException ioe ) { + //log.warn( "Could not close input stream", ioe ); + } + } + Assert.assertFalse( errorLogger.hasErrors() ); + context.addDocument( doc ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/XMLContextTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/XMLContextTest.java index 235fe820e4..39ed92d42f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/XMLContextTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/XMLContextTest.java @@ -6,60 +6,37 @@ */ package org.hibernate.test.annotations.reflection; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.dom4j.io.SAXReader; -import org.junit.Assert; -import org.junit.Test; -import org.xml.sax.InputSource; -import org.xml.sax.SAXNotSupportedException; - -import org.hibernate.cfg.EJB3DTDEntityResolver; -import org.hibernate.cfg.annotations.reflection.XMLContext; -import org.hibernate.internal.util.xml.ErrorLogger; -import org.hibernate.internal.util.xml.XMLHelper; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.cfg.annotations.reflection.internal.XMLContext; +import org.hibernate.internal.util.xml.XMLMappingHelper; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.boot.BootstrapContextImpl; -import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; +import org.junit.Test; /** + * Tests the new {@link XMLContext}, + * which will be replacing {@link org.hibernate.cfg.annotations.reflection.XMLContext}. + * {@link org.hibernate.cfg.annotations.reflection.XMLContext} is still the default implementation, + * but we want to switch to {@link XMLContext} + * as soon as it will be practical. + * * @author Emmanuel Bernard */ +@TestForIssue(jiraKey = "HHH-14529") public class XMLContextTest { @Test public void testAll() throws Exception { - final XMLHelper xmlHelper = new XMLHelper(); + XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() { + @Override + public boolean isPreferJaxb() { + return true; + } + } ); final XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE ); - InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( - "org/hibernate/test/annotations/reflection/orm.xml" - ); - Assert.assertNotNull( "ORM.xml not found", is ); - - final ErrorLogger errorLogger = new ErrorLogger(); - final SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE ); - - try { - saxReader.setFeature( "http://apache.org/xml/features/validation/schema", true ); - } - catch ( SAXNotSupportedException e ) { - saxReader.setValidation( false ); - } - org.dom4j.Document doc; - try { - doc = saxReader.read( new InputSource( new BufferedInputStream( is ) ) ); - } - finally { - try { - is.close(); - } - catch ( IOException ioe ) { - //log.warn( "Could not close input stream", ioe ); - } - } - Assert.assertFalse( errorLogger.hasErrors() ); - context.addDocument( doc ); + JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( "org/hibernate/test/annotations/reflection/orm.xml" ); + context.addDocument( mappings ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlElementCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlElementCollectionTest.java index 235b7e70a5..73d749f4f0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlElementCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlElementCollectionTest.java @@ -34,12 +34,14 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlElementCollectionTest extends Ejb3XmlTestCase { @Test public void testNoChildren() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToManyTest.java index 01f6170000..7f37ff4363 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToManyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToManyTest.java @@ -29,12 +29,14 @@ import javax.persistence.OrderColumn; import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlManyToManyTest extends Ejb3XmlTestCase { @Test public void testNoChildren() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToOneTest.java index b0e3c43b2a..dfe16a7468 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlManyToOneTest.java @@ -18,12 +18,14 @@ import javax.persistence.ManyToOne; import javax.persistence.MapsId; import javax.persistence.UniqueConstraint; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlManyToOneTest extends Ejb3XmlTestCase { @Test public void testNoJoins() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToManyTest.java index bca6d21c01..c1eda4f9d2 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToManyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToManyTest.java @@ -29,12 +29,14 @@ import javax.persistence.OrderColumn; import javax.persistence.TemporalType; import javax.persistence.UniqueConstraint; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlOneToManyTest extends Ejb3XmlTestCase { @Test public void testNoChildren() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToOneTest.java index fb2c0f70b1..e513889c0f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlOneToOneTest.java @@ -20,12 +20,14 @@ import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.PrimaryKeyJoinColumns; import javax.persistence.UniqueConstraint; +import org.hibernate.testing.TestForIssue; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlOneToOneTest extends Ejb3XmlTestCase { @Test public void testNoChildren() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTest.java index 46d95d7c55..2960c1e8e1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTest.java @@ -13,12 +13,15 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.dialect.CockroachDialect; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.TeradataDialect; +import org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider; import org.hibernate.persister.collection.BasicCollectionPersister; import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -28,7 +31,15 @@ import static org.junit.Assert.assertNotNull; /** * @author Emmanuel Bernard */ +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlTest extends BaseCoreFunctionalTestCase { + + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); + } + @Test @SkipForDialect(value = {PostgreSQL81Dialect.class, PostgreSQLDialect.class, CockroachDialect.class}, comment = "postgresql jdbc driver does not implement the setQueryTimeout method") diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTestCase.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTestCase.java index 3c96ba4df1..ea1d1fee35 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTestCase.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTestCase.java @@ -10,11 +10,11 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; -import org.dom4j.Document; -import org.dom4j.io.SAXReader; - -import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; -import org.hibernate.cfg.annotations.reflection.XMLContext; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader; +import org.hibernate.cfg.annotations.reflection.internal.XMLContext; +import org.hibernate.internal.util.xml.XMLMappingHelper; import org.hibernate.testing.boot.BootstrapContextImpl; import org.hibernate.testing.junit4.BaseUnitTestCase; @@ -28,8 +28,12 @@ import static org.junit.Assert.assertTrue; * XML to JPA annotations. The configuration is built within each test, and no * database is used. Thus, no schema generation or cleanup will be performed. */ -abstract class Ejb3XmlTestCase extends BaseUnitTestCase { - protected JPAOverriddenAnnotationReader reader; +public abstract class Ejb3XmlTestCase extends BaseUnitTestCase { + + protected JPAXMLOverriddenAnnotationReader reader; + + protected Ejb3XmlTestCase() { + } protected void assertAnnotationPresent(Class annotationType) { assertTrue( @@ -45,11 +49,11 @@ abstract class Ejb3XmlTestCase extends BaseUnitTestCase { ); } - protected JPAOverriddenAnnotationReader getReader(Class entityClass, String fieldName, String ormResourceName) + protected JPAXMLOverriddenAnnotationReader getReader(Class entityClass, String fieldName, String ormResourceName) throws Exception { AnnotatedElement el = getAnnotatedElement( entityClass, fieldName ); XMLContext xmlContext = getContext( ormResourceName ); - return new JPAOverriddenAnnotationReader( el, xmlContext, BootstrapContextImpl.INSTANCE ); + return new JPAXMLOverriddenAnnotationReader( el, xmlContext, BootstrapContextImpl.INSTANCE ); } protected AnnotatedElement getAnnotatedElement(Class entityClass, String fieldName) throws Exception { @@ -59,17 +63,19 @@ abstract class Ejb3XmlTestCase extends BaseUnitTestCase { protected XMLContext getContext(String resourceName) throws Exception { InputStream is = getClass().getResourceAsStream( resourceName ); assertNotNull( "Could not load resource " + resourceName, is ); - return getContext( is ); + return getContext( is, resourceName ); } - protected XMLContext getContext(InputStream is) throws Exception { - XMLContext xmlContext = new XMLContext( BootstrapContextImpl.INSTANCE ); - SAXReader reader = new SAXReader(); - reader.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false ); - reader.setFeature( "http://xml.org/sax/features/external-general-entities", false ); - reader.setFeature( "http://xml.org/sax/features/external-parameter-entities", false ); - Document doc = reader.read( is ); - xmlContext.addDocument( doc ); - return xmlContext; + protected XMLContext getContext(InputStream is, String resourceName) throws Exception { + XMLMappingHelper xmlHelper = new XMLMappingHelper( new XmlMappingOptions() { + @Override + public boolean isPreferJaxb() { + return true; + } + } ); + JaxbEntityMappings mappings = xmlHelper.readOrmXmlMappings( is, resourceName ); + XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE ); + context.addDocument( mappings ); + return context; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlElementCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlElementCollectionTest.java new file mode 100644 index 0000000000..7d46e662bf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlElementCollectionTest.java @@ -0,0 +1,716 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.AssociationOverride; +import javax.persistence.AssociationOverrides; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.Lob; +import javax.persistence.MapKey; +import javax.persistence.MapKeyClass; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyEnumerated; +import javax.persistence.MapKeyJoinColumn; +import javax.persistence.MapKeyJoinColumns; +import javax.persistence.MapKeyTemporal; +import javax.persistence.OrderBy; +import javax.persistence.OrderColumn; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlElementCollectionTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlElementCollectionTest extends LegacyEjb3XmlTestCase { + @Test + public void testNoChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "element-collection.orm1.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( Column.class ); + assertAnnotationNotPresent( Temporal.class ); + assertAnnotationNotPresent( Enumerated.class ); + assertAnnotationNotPresent( Lob.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationNotPresent( AttributeOverrides.class ); + assertAnnotationNotPresent( AssociationOverride.class ); + assertAnnotationNotPresent( AssociationOverrides.class ); + assertAnnotationNotPresent( CollectionTable.class ); + assertAnnotationNotPresent( Access.class ); + ElementCollection relAnno = reader.getAnnotation( ElementCollection.class ); + assertEquals( FetchType.LAZY, relAnno.fetch() ); + assertEquals( void.class, relAnno.targetClass() ); + } + + @Test + public void testOrderBy() throws Exception { + reader = getReader( Entity2.class, "field1", "element-collection.orm2.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertEquals( + "col1 ASC, col2 DESC", reader.getAnnotation( OrderBy.class ) + .value() + ); + } + + @Test + public void testOrderColumnNoAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "element-collection.orm3.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "", orderColumnAnno.columnDefinition() ); + assertEquals( "", orderColumnAnno.name() ); + assertTrue( orderColumnAnno.insertable() ); + assertTrue( orderColumnAnno.nullable() ); + assertTrue( orderColumnAnno.updatable() ); + } + + @Test + public void testOrderColumnAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "element-collection.orm4.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "int", orderColumnAnno.columnDefinition() ); + assertEquals( "col1", orderColumnAnno.name() ); + assertFalse( orderColumnAnno.insertable() ); + assertFalse( orderColumnAnno.nullable() ); + assertFalse( orderColumnAnno.updatable() ); + } + + @Test + public void testMapKeyNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm5.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm6.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "field2", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyClass() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm7.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + Entity2.class, reader.getAnnotation( MapKeyClass.class ) + .value() + ); + } + + @Test + public void testMapKeyTemporal() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm8.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + TemporalType.DATE, reader.getAnnotation( + MapKeyTemporal.class + ).value() + ); + } + + @Test + public void testMapKeyEnumerated() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm9.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + EnumType.STRING, reader.getAnnotation( + MapKeyEnumerated.class + ).value() + ); + } + + /** + * When there's a single map key attribute override, we still wrap it with + * an AttributeOverrides annotation. + */ + @Test + public void testSingleMapKeyAttributeOverride() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm10.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 1, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "col1", overrides[0].column().name() ); + } + + @Test + public void testMultipleMapKeyAttributeOverrides() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm11.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "", overrides[0].column().name() ); + assertFalse( overrides[0].column().unique() ); + assertTrue( overrides[0].column().nullable() ); + assertTrue( overrides[0].column().insertable() ); + assertTrue( overrides[0].column().updatable() ); + assertEquals( "", overrides[0].column().columnDefinition() ); + assertEquals( "", overrides[0].column().table() ); + assertEquals( 255, overrides[0].column().length() ); + assertEquals( 0, overrides[0].column().precision() ); + assertEquals( 0, overrides[0].column().scale() ); + assertEquals( "field2", overrides[1].name() ); + assertEquals( "col1", overrides[1].column().name() ); + assertTrue( overrides[1].column().unique() ); + assertFalse( overrides[1].column().nullable() ); + assertFalse( overrides[1].column().insertable() ); + assertFalse( overrides[1].column().updatable() ); + assertEquals( "int", overrides[1].column().columnDefinition() ); + assertEquals( "table1", overrides[1].column().table() ); + assertEquals( 50, overrides[1].column().length() ); + assertEquals( 2, overrides[1].column().precision() ); + assertEquals( 1, overrides[1].column().scale() ); + } + + @Test + public void testMapKeyColumnNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm12.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "", keyColAnno.columnDefinition() ); + assertEquals( "", keyColAnno.name() ); + assertEquals( "", keyColAnno.table() ); + assertFalse( keyColAnno.nullable() ); + assertTrue( keyColAnno.insertable() ); + assertFalse( keyColAnno.unique() ); + assertTrue( keyColAnno.updatable() ); + assertEquals( 255, keyColAnno.length() ); + assertEquals( 0, keyColAnno.precision() ); + assertEquals( 0, keyColAnno.scale() ); + } + + @Test + public void testMapKeyColumnAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm13.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "int", keyColAnno.columnDefinition() ); + assertEquals( "col1", keyColAnno.name() ); + assertEquals( "table1", keyColAnno.table() ); + assertTrue( keyColAnno.nullable() ); + assertFalse( keyColAnno.insertable() ); + assertTrue( keyColAnno.unique() ); + assertFalse( keyColAnno.updatable() ); + assertEquals( 50, keyColAnno.length() ); + assertEquals( 2, keyColAnno.precision() ); + assertEquals( 1, keyColAnno.scale() ); + } + + /** + * When there's a single map key join column, we still wrap it with a + * MapKeyJoinColumns annotation. + */ + @Test + public void testSingleMapKeyJoinColumn() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm14.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + } + + @Test + public void testMultipleMapKeyJoinColumns() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm15.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertFalse( joinColumns[0].unique() ); + assertFalse( joinColumns[0].nullable() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertTrue( joinColumns[1].unique() ); + assertTrue( joinColumns[1].nullable() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertEquals( "table1", joinColumns[1].table() ); + } + + @Test + public void testColumnNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm16.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( Column.class ); + Column column = reader.getAnnotation( Column.class ); + assertEquals( "", column.name() ); + assertFalse( column.unique() ); + assertTrue( column.nullable() ); + assertTrue( column.insertable() ); + assertTrue( column.updatable() ); + assertEquals( "", column.columnDefinition() ); + assertEquals( "", column.table() ); + assertEquals( 255, column.length() ); + assertEquals( 0, column.precision() ); + assertEquals( 0, column.scale() ); + } + + @Test + public void testColumnAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm17.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( Column.class ); + Column column = reader.getAnnotation( Column.class ); + assertEquals( "col1", column.name() ); + assertTrue( column.unique() ); + assertFalse( column.nullable() ); + assertFalse( column.insertable() ); + assertFalse( column.updatable() ); + assertEquals( "int", column.columnDefinition() ); + assertEquals( "table1", column.table() ); + assertEquals( 50, column.length() ); + assertEquals( 2, column.precision() ); + assertEquals( 1, column.scale() ); + } + + @Test + public void testTemporal() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm18.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( Temporal.class ); + assertAnnotationNotPresent( Enumerated.class ); + assertAnnotationNotPresent( Lob.class ); + assertEquals( + TemporalType.DATE, reader.getAnnotation( + Temporal.class + ).value() + ); + } + + @Test + public void testEnumerated() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm19.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( Temporal.class ); + assertAnnotationPresent( Enumerated.class ); + assertAnnotationNotPresent( Lob.class ); + assertEquals( + EnumType.STRING, reader.getAnnotation( + Enumerated.class + ).value() + ); + } + + @Test + public void testLob() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm20.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( Temporal.class ); + assertAnnotationNotPresent( Enumerated.class ); + assertAnnotationPresent( Lob.class ); + } + + /** + * When there's a single attribute override, we still wrap it with an + * AttributeOverrides annotation. + */ + @Test + public void testSingleAttributeOverride() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm21.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 1, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "col1", overrides[0].column().name() ); + } + + @Test + public void testMultipleAttributeOverrides() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm22.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "", overrides[0].column().name() ); + assertFalse( overrides[0].column().unique() ); + assertTrue( overrides[0].column().nullable() ); + assertTrue( overrides[0].column().insertable() ); + assertTrue( overrides[0].column().updatable() ); + assertEquals( "", overrides[0].column().columnDefinition() ); + assertEquals( "", overrides[0].column().table() ); + assertEquals( 255, overrides[0].column().length() ); + assertEquals( 0, overrides[0].column().precision() ); + assertEquals( 0, overrides[0].column().scale() ); + assertEquals( "field2", overrides[1].name() ); + assertEquals( "col1", overrides[1].column().name() ); + assertTrue( overrides[1].column().unique() ); + assertFalse( overrides[1].column().nullable() ); + assertFalse( overrides[1].column().insertable() ); + assertFalse( overrides[1].column().updatable() ); + assertEquals( "int", overrides[1].column().columnDefinition() ); + assertEquals( "table1", overrides[1].column().table() ); + assertEquals( 50, overrides[1].column().length() ); + assertEquals( 2, overrides[1].column().precision() ); + assertEquals( 1, overrides[1].column().scale() ); + } + + /** + * Tests that map-key-attribute-override and attribute-override elements + * both end up in the AttributeOverrides annotation. + */ + @Test + public void testMixedAttributeOverrides() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm23.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "col1", overrides[0].column().name() ); + assertEquals( "field2", overrides[1].name() ); + assertEquals( "col2", overrides[1].column().name() ); + } + + /** + * When there's a single association override, we still wrap it with an + * AssociationOverrides annotation. + */ + @Test + public void testSingleAssociationOverride() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm24.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( AssociationOverride.class ); + assertAnnotationPresent( AssociationOverrides.class ); + AssociationOverrides overridesAnno = reader.getAnnotation( AssociationOverrides.class ); + AssociationOverride[] overrides = overridesAnno.value(); + assertEquals( 1, overrides.length ); + assertEquals( "association1", overrides[0].name() ); + assertEquals( 0, overrides[0].joinColumns().length ); + assertEquals( "", overrides[0].joinTable().name() ); + } + + @Test + public void testMultipleAssociationOverridesJoinColumns() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm25.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( AssociationOverride.class ); + assertAnnotationPresent( AssociationOverrides.class ); + AssociationOverrides overridesAnno = reader.getAnnotation( AssociationOverrides.class ); + AssociationOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + //First, an association using join table + assertEquals( "association1", overrides[0].name() ); + assertEquals( 0, overrides[0].joinColumns().length ); + + JoinTable joinTableAnno = overrides[0].joinTable(); + assertEquals( "catalog1", joinTableAnno.catalog() ); + assertEquals( "table1", joinTableAnno.name() ); + assertEquals( "schema1", joinTableAnno.schema() ); + + //JoinColumns + JoinColumn[] joinColumns = joinTableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + //InverseJoinColumns + JoinColumn[] inverseJoinColumns = joinTableAnno.inverseJoinColumns(); + assertEquals( 2, inverseJoinColumns.length ); + assertEquals( "", inverseJoinColumns[0].name() ); + assertEquals( "", inverseJoinColumns[0].referencedColumnName() ); + assertEquals( "", inverseJoinColumns[0].table() ); + assertEquals( "", inverseJoinColumns[0].columnDefinition() ); + assertTrue( inverseJoinColumns[0].insertable() ); + assertTrue( inverseJoinColumns[0].updatable() ); + assertTrue( inverseJoinColumns[0].nullable() ); + assertFalse( inverseJoinColumns[0].unique() ); + assertEquals( "col3", inverseJoinColumns[1].name() ); + assertEquals( "col4", inverseJoinColumns[1].referencedColumnName() ); + assertEquals( "table3", inverseJoinColumns[1].table() ); + assertEquals( "int", inverseJoinColumns[1].columnDefinition() ); + assertFalse( inverseJoinColumns[1].insertable() ); + assertFalse( inverseJoinColumns[1].updatable() ); + assertFalse( inverseJoinColumns[1].nullable() ); + assertTrue( inverseJoinColumns[1].unique() ); + + //UniqueConstraints + UniqueConstraint[] uniqueConstraints = joinTableAnno + .uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col5", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col6", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col7", uniqueConstraints[1].columnNames()[1] ); + + //Second, an association using join columns + assertEquals( "association2", overrides[1].name() ); + + //JoinColumns + joinColumns = overrides[1].joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col8", joinColumns[1].name() ); + assertEquals( "col9", joinColumns[1].referencedColumnName() ); + assertEquals( "table4", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + } + + @Test + public void testCollectionTableNoChildren() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm26.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( CollectionTable.class ); + CollectionTable tableAnno = reader.getAnnotation( CollectionTable.class ); + assertEquals( "", tableAnno.name() ); + assertEquals( "", tableAnno.catalog() ); + assertEquals( "", tableAnno.schema() ); + assertEquals( 0, tableAnno.joinColumns().length ); + assertEquals( 0, tableAnno.uniqueConstraints().length ); + } + + @Test + public void testCollectionTableAllChildren() throws Exception { + reader = getReader( Entity3.class, "field1", "element-collection.orm27.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationPresent( CollectionTable.class ); + CollectionTable tableAnno = reader.getAnnotation( CollectionTable.class ); + assertEquals( "table1", tableAnno.name() ); + assertEquals( "catalog1", tableAnno.catalog() ); + assertEquals( "schema1", tableAnno.schema() ); + + //JoinColumns + JoinColumn[] joinColumns = tableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + //UniqueConstraints + UniqueConstraint[] uniqueConstraints = tableAnno.uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col3", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col4", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col5", uniqueConstraints[1].columnNames()[1] ); + } + + @Test + public void testAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "element-collection.orm28.xml" ); + assertAnnotationPresent( ElementCollection.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( Column.class ); + assertAnnotationNotPresent( Temporal.class ); + assertAnnotationNotPresent( Enumerated.class ); + assertAnnotationNotPresent( Lob.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationNotPresent( AttributeOverrides.class ); + assertAnnotationNotPresent( AssociationOverride.class ); + assertAnnotationNotPresent( AssociationOverrides.class ); + assertAnnotationNotPresent( CollectionTable.class ); + assertAnnotationPresent( Access.class ); + ElementCollection relAnno = reader.getAnnotation( ElementCollection.class ); + assertEquals( FetchType.EAGER, relAnno.fetch() ); + assertEquals( Entity3.class, relAnno.targetClass() ); + assertEquals( + AccessType.PROPERTY, reader.getAnnotation( Access.class ) + .value() + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToManyTest.java new file mode 100644 index 0000000000..e80c00e08e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToManyTest.java @@ -0,0 +1,508 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.CascadeType; +import javax.persistence.EnumType; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.MapKey; +import javax.persistence.MapKeyClass; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyEnumerated; +import javax.persistence.MapKeyJoinColumn; +import javax.persistence.MapKeyJoinColumns; +import javax.persistence.MapKeyTemporal; +import javax.persistence.OrderBy; +import javax.persistence.OrderColumn; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlManyToManyTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlManyToManyTest extends LegacyEjb3XmlTestCase { + @Test + public void testNoChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm1.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationNotPresent( Access.class ); + ManyToMany relAnno = reader.getAnnotation( ManyToMany.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.LAZY, relAnno.fetch() ); + assertEquals( "", relAnno.mappedBy() ); + assertEquals( void.class, relAnno.targetEntity() ); + } + + @Test + public void testOrderBy() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm2.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertEquals( + "col1 ASC, col2 DESC", reader.getAnnotation( OrderBy.class ) + .value() + ); + } + + @Test + public void testOrderColumnNoAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm3.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "", orderColumnAnno.columnDefinition() ); + assertEquals( "", orderColumnAnno.name() ); + assertTrue( orderColumnAnno.insertable() ); + assertTrue( orderColumnAnno.nullable() ); + assertTrue( orderColumnAnno.updatable() ); + } + + @Test + public void testOrderColumnAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm4.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "int", orderColumnAnno.columnDefinition() ); + assertEquals( "col1", orderColumnAnno.name() ); + assertFalse( orderColumnAnno.insertable() ); + assertFalse( orderColumnAnno.nullable() ); + assertFalse( orderColumnAnno.updatable() ); + } + + @Test + public void testMapKeyNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm5.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm6.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "field2", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyClass() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm7.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + Entity2.class, reader.getAnnotation( MapKeyClass.class ) + .value() + ); + } + + @Test + public void testMapKeyTemporal() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm8.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + TemporalType.DATE, reader.getAnnotation( + MapKeyTemporal.class + ).value() + ); + } + + @Test + public void testMapKeyEnumerated() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm9.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + EnumType.STRING, reader.getAnnotation( + MapKeyEnumerated.class + ).value() + ); + } + + /** + * When there's a single map key attribute override, we still wrap it with + * an AttributeOverrides annotation. + */ + @Test + public void testSingleMapKeyAttributeOverride() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm10.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 1, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "col1", overrides[0].column().name() ); + } + + @Test + public void testMultipleMapKeyAttributeOverrides() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm11.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "", overrides[0].column().name() ); + assertFalse( overrides[0].column().unique() ); + assertTrue( overrides[0].column().nullable() ); + assertTrue( overrides[0].column().insertable() ); + assertTrue( overrides[0].column().updatable() ); + assertEquals( "", overrides[0].column().columnDefinition() ); + assertEquals( "", overrides[0].column().table() ); + assertEquals( 255, overrides[0].column().length() ); + assertEquals( 0, overrides[0].column().precision() ); + assertEquals( 0, overrides[0].column().scale() ); + assertEquals( "field2", overrides[1].name() ); + assertEquals( "col1", overrides[1].column().name() ); + assertTrue( overrides[1].column().unique() ); + assertFalse( overrides[1].column().nullable() ); + assertFalse( overrides[1].column().insertable() ); + assertFalse( overrides[1].column().updatable() ); + assertEquals( "int", overrides[1].column().columnDefinition() ); + assertEquals( "table1", overrides[1].column().table() ); + assertEquals( 50, overrides[1].column().length() ); + assertEquals( 2, overrides[1].column().precision() ); + assertEquals( 1, overrides[1].column().scale() ); + } + + @Test + public void testMapKeyColumnNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm12.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "", keyColAnno.columnDefinition() ); + assertEquals( "", keyColAnno.name() ); + assertEquals( "", keyColAnno.table() ); + assertFalse( keyColAnno.nullable() ); + assertTrue( keyColAnno.insertable() ); + assertFalse( keyColAnno.unique() ); + assertTrue( keyColAnno.updatable() ); + assertEquals( 255, keyColAnno.length() ); + assertEquals( 0, keyColAnno.precision() ); + assertEquals( 0, keyColAnno.scale() ); + } + + @Test + public void testMapKeyColumnAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm13.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "int", keyColAnno.columnDefinition() ); + assertEquals( "col1", keyColAnno.name() ); + assertEquals( "table1", keyColAnno.table() ); + assertTrue( keyColAnno.nullable() ); + assertFalse( keyColAnno.insertable() ); + assertTrue( keyColAnno.unique() ); + assertFalse( keyColAnno.updatable() ); + assertEquals( 50, keyColAnno.length() ); + assertEquals( 2, keyColAnno.precision() ); + assertEquals( 1, keyColAnno.scale() ); + } + + /** + * When there's a single map key join column, we still wrap it with a + * MapKeyJoinColumns annotation. + */ + @Test + public void testSingleMapKeyJoinColumn() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm14.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + } + + @Test + public void testMultipleMapKeyJoinColumns() throws Exception { + reader = getReader( Entity3.class, "field1", "many-to-many.orm15.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertFalse( joinColumns[0].unique() ); + assertFalse( joinColumns[0].nullable() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertTrue( joinColumns[1].unique() ); + assertTrue( joinColumns[1].nullable() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertEquals( "table1", joinColumns[1].table() ); + } + + @Test + public void testJoinTableNoChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm16.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "", joinTableAnno.catalog() ); + assertEquals( "", joinTableAnno.name() ); + assertEquals( "", joinTableAnno.schema() ); + assertEquals( 0, joinTableAnno.joinColumns().length ); + assertEquals( 0, joinTableAnno.inverseJoinColumns().length ); + assertEquals( 0, joinTableAnno.uniqueConstraints().length ); + } + + @Test + public void testJoinTableAllChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm17.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "cat1", joinTableAnno.catalog() ); + assertEquals( "table1", joinTableAnno.name() ); + assertEquals( "schema1", joinTableAnno.schema() ); + + // JoinColumns + JoinColumn[] joinColumns = joinTableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + // InverseJoinColumns + JoinColumn[] inverseJoinColumns = joinTableAnno.inverseJoinColumns(); + assertEquals( 2, inverseJoinColumns.length ); + assertEquals( "", inverseJoinColumns[0].name() ); + assertEquals( "", inverseJoinColumns[0].referencedColumnName() ); + assertEquals( "", inverseJoinColumns[0].table() ); + assertEquals( "", inverseJoinColumns[0].columnDefinition() ); + assertTrue( inverseJoinColumns[0].insertable() ); + assertTrue( inverseJoinColumns[0].updatable() ); + assertTrue( inverseJoinColumns[0].nullable() ); + assertFalse( inverseJoinColumns[0].unique() ); + assertEquals( "col3", inverseJoinColumns[1].name() ); + assertEquals( "col4", inverseJoinColumns[1].referencedColumnName() ); + assertEquals( "table3", inverseJoinColumns[1].table() ); + assertEquals( "int", inverseJoinColumns[1].columnDefinition() ); + assertFalse( inverseJoinColumns[1].insertable() ); + assertFalse( inverseJoinColumns[1].updatable() ); + assertFalse( inverseJoinColumns[1].nullable() ); + assertTrue( inverseJoinColumns[1].unique() ); + + // UniqueConstraints + UniqueConstraint[] uniqueConstraints = joinTableAnno + .uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col5", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col6", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col7", uniqueConstraints[1].columnNames()[1] ); + } + + @Test + public void testCascadeAll() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm18.xml" ); + assertAnnotationPresent( ManyToMany.class ); + ManyToMany relAnno = reader.getAnnotation( ManyToMany.class ); + assertEquals( 1, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + } + + @Test + public void testCascadeSomeWithDefaultPersist() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm19.xml" ); + assertAnnotationPresent( ManyToMany.class ); + ManyToMany relAnno = reader.getAnnotation( ManyToMany.class ); + assertEquals( 4, relAnno.cascade().length ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[0] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[1] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[2] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[3] ); + } + + /** + * Make sure that it doesn't break the handler when {@link CascadeType#ALL} + * is specified in addition to a default cascade-persist or individual + * cascade settings. + */ + @Test + public void testCascadeAllPlusMore() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm20.xml" ); + assertAnnotationPresent( ManyToMany.class ); + ManyToMany relAnno = reader.getAnnotation( ManyToMany.class ); + assertEquals( 6, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[1] ); + assertEquals( CascadeType.MERGE, relAnno.cascade()[2] ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[3] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[4] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[5] ); + } + + @Test + public void testAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "many-to-many.orm21.xml" ); + assertAnnotationPresent( ManyToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationPresent( Access.class ); + ManyToMany relAnno = reader.getAnnotation( ManyToMany.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.EAGER, relAnno.fetch() ); + assertEquals( "field2", relAnno.mappedBy() ); + assertEquals( Entity3.class, relAnno.targetEntity() ); + assertEquals( + AccessType.PROPERTY, reader.getAnnotation( Access.class ) + .value() + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToOneTest.java new file mode 100644 index 0000000000..e665be4f2e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToOneTest.java @@ -0,0 +1,245 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.CascadeType; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; +import javax.persistence.MapsId; +import javax.persistence.UniqueConstraint; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlManyToOneTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlManyToOneTest extends LegacyEjb3XmlTestCase { + @Test + public void testNoJoins() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm1.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationNotPresent( Id.class ); + assertAnnotationNotPresent( MapsId.class ); + assertAnnotationNotPresent( Access.class ); + ManyToOne relAnno = reader.getAnnotation( ManyToOne.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.EAGER, relAnno.fetch() ); + assertTrue( relAnno.optional() ); + assertEquals( void.class, relAnno.targetEntity() ); + } + + /** + * When there's a single join column, we still wrap it with a JoinColumns + * annotation. + */ + @Test + public void testSingleJoinColumn() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm2.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + assertEquals( "col2", joinColumns[0].referencedColumnName() ); + assertEquals( "table1", joinColumns[0].table() ); + } + + @Test + public void testMultipleJoinColumns() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm3.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table1", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + } + + @Test + public void testJoinTableNoChildren() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm4.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationPresent( JoinTable.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "", joinTableAnno.catalog() ); + assertEquals( "", joinTableAnno.name() ); + assertEquals( "", joinTableAnno.schema() ); + assertEquals( 0, joinTableAnno.joinColumns().length ); + assertEquals( 0, joinTableAnno.inverseJoinColumns().length ); + assertEquals( 0, joinTableAnno.uniqueConstraints().length ); + } + + @Test + public void testJoinTableAllChildren() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm5.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationPresent( JoinTable.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "cat1", joinTableAnno.catalog() ); + assertEquals( "table1", joinTableAnno.name() ); + assertEquals( "schema1", joinTableAnno.schema() ); + + // JoinColumns + JoinColumn[] joinColumns = joinTableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + // InverseJoinColumns + JoinColumn[] inverseJoinColumns = joinTableAnno.inverseJoinColumns(); + assertEquals( 2, inverseJoinColumns.length ); + assertEquals( "", inverseJoinColumns[0].name() ); + assertEquals( "", inverseJoinColumns[0].referencedColumnName() ); + assertEquals( "", inverseJoinColumns[0].table() ); + assertEquals( "", inverseJoinColumns[0].columnDefinition() ); + assertTrue( inverseJoinColumns[0].insertable() ); + assertTrue( inverseJoinColumns[0].updatable() ); + assertTrue( inverseJoinColumns[0].nullable() ); + assertFalse( inverseJoinColumns[0].unique() ); + assertEquals( "col3", inverseJoinColumns[1].name() ); + assertEquals( "col4", inverseJoinColumns[1].referencedColumnName() ); + assertEquals( "table3", inverseJoinColumns[1].table() ); + assertEquals( "int", inverseJoinColumns[1].columnDefinition() ); + assertFalse( inverseJoinColumns[1].insertable() ); + assertFalse( inverseJoinColumns[1].updatable() ); + assertFalse( inverseJoinColumns[1].nullable() ); + assertTrue( inverseJoinColumns[1].unique() ); + + // UniqueConstraints + UniqueConstraint[] uniqueConstraints = joinTableAnno + .uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col5", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col6", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col7", uniqueConstraints[1].columnNames()[1] ); + } + + @Test + public void testAllAttributes() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm6.xml" ); + assertAnnotationPresent( ManyToOne.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationPresent( Id.class ); + assertAnnotationPresent( MapsId.class ); + assertAnnotationPresent( Access.class ); + ManyToOne relAnno = reader.getAnnotation( ManyToOne.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.LAZY, relAnno.fetch() ); + assertFalse( relAnno.optional() ); + assertEquals( Entity3.class, relAnno.targetEntity() ); + assertEquals( "col1", reader.getAnnotation( MapsId.class ).value() ); + assertEquals( + AccessType.PROPERTY, reader.getAnnotation( Access.class ) + .value() + ); + } + + @Test + public void testCascadeAll() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm7.xml" ); + assertAnnotationPresent( ManyToOne.class ); + ManyToOne relAnno = reader.getAnnotation( ManyToOne.class ); + assertEquals( 1, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + } + + @Test + public void testCascadeSomeWithDefaultPersist() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm8.xml" ); + assertAnnotationPresent( ManyToOne.class ); + ManyToOne relAnno = reader.getAnnotation( ManyToOne.class ); + assertEquals( 4, relAnno.cascade().length ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[0] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[1] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[2] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[3] ); + } + + /** + * Make sure that it doesn't break the handler when {@link CascadeType#ALL} + * is specified in addition to a default cascade-persist or individual + * cascade settings. + */ + @Test + public void testCascadeAllPlusMore() throws Exception { + reader = getReader( Entity1.class, "field1", "many-to-one.orm9.xml" ); + assertAnnotationPresent( ManyToOne.class ); + ManyToOne relAnno = reader.getAnnotation( ManyToOne.class ); + assertEquals( 6, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[1] ); + assertEquals( CascadeType.MERGE, relAnno.cascade()[2] ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[3] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[4] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[5] ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToManyTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToManyTest.java new file mode 100644 index 0000000000..b531fabecf --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToManyTest.java @@ -0,0 +1,561 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; +import javax.persistence.CascadeType; +import javax.persistence.EnumType; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.MapKey; +import javax.persistence.MapKeyClass; +import javax.persistence.MapKeyColumn; +import javax.persistence.MapKeyEnumerated; +import javax.persistence.MapKeyJoinColumn; +import javax.persistence.MapKeyJoinColumns; +import javax.persistence.MapKeyTemporal; +import javax.persistence.OneToMany; +import javax.persistence.OrderBy; +import javax.persistence.OrderColumn; +import javax.persistence.TemporalType; +import javax.persistence.UniqueConstraint; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlOneToManyTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlOneToManyTest extends LegacyEjb3XmlTestCase { + @Test + public void testNoChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm1.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( Access.class ); + OneToMany relAnno = reader.getAnnotation( OneToMany.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.LAZY, relAnno.fetch() ); + assertEquals( "", relAnno.mappedBy() ); + assertFalse( relAnno.orphanRemoval() ); + assertEquals( void.class, relAnno.targetEntity() ); + } + + @Test + public void testOrderBy() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm2.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertEquals( + "col1 ASC, col2 DESC", reader.getAnnotation( OrderBy.class ) + .value() + ); + } + + @Test + public void testOrderColumnNoAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm3.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "", orderColumnAnno.columnDefinition() ); + assertEquals( "", orderColumnAnno.name() ); + assertTrue( orderColumnAnno.insertable() ); + assertTrue( orderColumnAnno.nullable() ); + assertTrue( orderColumnAnno.updatable() ); + } + + @Test + public void testOrderColumnAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm4.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationPresent( OrderColumn.class ); + OrderColumn orderColumnAnno = reader.getAnnotation( OrderColumn.class ); + assertEquals( "int", orderColumnAnno.columnDefinition() ); + assertEquals( "col1", orderColumnAnno.name() ); + assertFalse( orderColumnAnno.insertable() ); + assertFalse( orderColumnAnno.nullable() ); + assertFalse( orderColumnAnno.updatable() ); + } + + @Test + public void testMapKeyNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm5.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm6.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( "field2", reader.getAnnotation( MapKey.class ).name() ); + } + + @Test + public void testMapKeyClass() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm7.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + Entity2.class, reader.getAnnotation( MapKeyClass.class ) + .value() + ); + } + + @Test + public void testMapKeyTemporal() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm8.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + TemporalType.DATE, reader.getAnnotation( + MapKeyTemporal.class + ).value() + ); + } + + @Test + public void testMapKeyEnumerated() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm9.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertEquals( + EnumType.STRING, reader.getAnnotation( + MapKeyEnumerated.class + ).value() + ); + } + + /** + * When there's a single map key attribute override, we still wrap it with + * an AttributeOverrides annotation. + */ + @Test + public void testSingleMapKeyAttributeOverride() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm10.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 1, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "col1", overrides[0].column().name() ); + } + + @Test + public void testMultipleMapKeyAttributeOverrides() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm11.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( AttributeOverride.class ); + assertAnnotationPresent( AttributeOverrides.class ); + AttributeOverrides overridesAnno = reader + .getAnnotation( AttributeOverrides.class ); + AttributeOverride[] overrides = overridesAnno.value(); + assertEquals( 2, overrides.length ); + assertEquals( "field1", overrides[0].name() ); + assertEquals( "", overrides[0].column().name() ); + assertFalse( overrides[0].column().unique() ); + assertTrue( overrides[0].column().nullable() ); + assertTrue( overrides[0].column().insertable() ); + assertTrue( overrides[0].column().updatable() ); + assertEquals( "", overrides[0].column().columnDefinition() ); + assertEquals( "", overrides[0].column().table() ); + assertEquals( 255, overrides[0].column().length() ); + assertEquals( 0, overrides[0].column().precision() ); + assertEquals( 0, overrides[0].column().scale() ); + assertEquals( "field2", overrides[1].name() ); + assertEquals( "col1", overrides[1].column().name() ); + assertTrue( overrides[1].column().unique() ); + assertFalse( overrides[1].column().nullable() ); + assertFalse( overrides[1].column().insertable() ); + assertFalse( overrides[1].column().updatable() ); + assertEquals( "int", overrides[1].column().columnDefinition() ); + assertEquals( "table1", overrides[1].column().table() ); + assertEquals( 50, overrides[1].column().length() ); + assertEquals( 2, overrides[1].column().precision() ); + assertEquals( 1, overrides[1].column().scale() ); + } + + @Test + public void testMapKeyColumnNoAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm12.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "", keyColAnno.columnDefinition() ); + assertEquals( "", keyColAnno.name() ); + assertEquals( "", keyColAnno.table() ); + assertFalse( keyColAnno.nullable() ); + assertTrue( keyColAnno.insertable() ); + assertFalse( keyColAnno.unique() ); + assertTrue( keyColAnno.updatable() ); + assertEquals( 255, keyColAnno.length() ); + assertEquals( 0, keyColAnno.precision() ); + assertEquals( 0, keyColAnno.scale() ); + } + + @Test + public void testMapKeyColumnAllAttributes() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm13.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyColumn keyColAnno = reader.getAnnotation( MapKeyColumn.class ); + assertEquals( "int", keyColAnno.columnDefinition() ); + assertEquals( "col1", keyColAnno.name() ); + assertEquals( "table1", keyColAnno.table() ); + assertTrue( keyColAnno.nullable() ); + assertFalse( keyColAnno.insertable() ); + assertTrue( keyColAnno.unique() ); + assertFalse( keyColAnno.updatable() ); + assertEquals( 50, keyColAnno.length() ); + assertEquals( 2, keyColAnno.precision() ); + assertEquals( 1, keyColAnno.scale() ); + } + + /** + * When there's a single map key join column, we still wrap it with a + * MapKeyJoinColumns annotation. + */ + @Test + public void testSingleMapKeyJoinColumn() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm14.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + } + + @Test + public void testMultipleMapKeyJoinColumns() throws Exception { + reader = getReader( Entity3.class, "field1", "one-to-many.orm15.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + MapKeyJoinColumns joinColumnsAnno = reader + .getAnnotation( MapKeyJoinColumns.class ); + MapKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertFalse( joinColumns[0].unique() ); + assertFalse( joinColumns[0].nullable() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertTrue( joinColumns[1].unique() ); + assertTrue( joinColumns[1].nullable() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertEquals( "table1", joinColumns[1].table() ); + } + + @Test + public void testJoinTableNoChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm16.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "", joinTableAnno.catalog() ); + assertEquals( "", joinTableAnno.name() ); + assertEquals( "", joinTableAnno.schema() ); + assertEquals( 0, joinTableAnno.joinColumns().length ); + assertEquals( 0, joinTableAnno.inverseJoinColumns().length ); + assertEquals( 0, joinTableAnno.uniqueConstraints().length ); + } + + @Test + public void testJoinTableAllChildren() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm17.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "cat1", joinTableAnno.catalog() ); + assertEquals( "table1", joinTableAnno.name() ); + assertEquals( "schema1", joinTableAnno.schema() ); + + // JoinColumns + JoinColumn[] joinColumns = joinTableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + // InverseJoinColumns + JoinColumn[] inverseJoinColumns = joinTableAnno.inverseJoinColumns(); + assertEquals( 2, inverseJoinColumns.length ); + assertEquals( "", inverseJoinColumns[0].name() ); + assertEquals( "", inverseJoinColumns[0].referencedColumnName() ); + assertEquals( "", inverseJoinColumns[0].table() ); + assertEquals( "", inverseJoinColumns[0].columnDefinition() ); + assertTrue( inverseJoinColumns[0].insertable() ); + assertTrue( inverseJoinColumns[0].updatable() ); + assertTrue( inverseJoinColumns[0].nullable() ); + assertFalse( inverseJoinColumns[0].unique() ); + assertEquals( "col3", inverseJoinColumns[1].name() ); + assertEquals( "col4", inverseJoinColumns[1].referencedColumnName() ); + assertEquals( "table3", inverseJoinColumns[1].table() ); + assertEquals( "int", inverseJoinColumns[1].columnDefinition() ); + assertFalse( inverseJoinColumns[1].insertable() ); + assertFalse( inverseJoinColumns[1].updatable() ); + assertFalse( inverseJoinColumns[1].nullable() ); + assertTrue( inverseJoinColumns[1].unique() ); + + // UniqueConstraints + UniqueConstraint[] uniqueConstraints = joinTableAnno + .uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col5", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col6", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col7", uniqueConstraints[1].columnNames()[1] ); + } + + /** + * When there's a single join column, we still wrap it with a JoinColumns + * annotation. + */ + @Test + public void testSingleJoinColumn() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm18.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + assertEquals( "col2", joinColumns[0].referencedColumnName() ); + assertEquals( "table1", joinColumns[0].table() ); + } + + @Test + public void testMultipleJoinColumns() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm19.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table1", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + } + + @Test + public void testCascadeAll() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm20.xml" ); + assertAnnotationPresent( OneToMany.class ); + OneToMany relAnno = reader.getAnnotation( OneToMany.class ); + assertEquals( 1, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + } + + @Test + public void testCascadeSomeWithDefaultPersist() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm21.xml" ); + assertAnnotationPresent( OneToMany.class ); + OneToMany relAnno = reader.getAnnotation( OneToMany.class ); + assertEquals( 4, relAnno.cascade().length ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[0] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[1] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[2] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[3] ); + } + + /** + * Make sure that it doesn't break the handler when {@link CascadeType#ALL} + * is specified in addition to a default cascade-persist or individual + * cascade settings. + */ + @Test + public void testCascadeAllPlusMore() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm22.xml" ); + assertAnnotationPresent( OneToMany.class ); + OneToMany relAnno = reader.getAnnotation( OneToMany.class ); + assertEquals( 6, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[1] ); + assertEquals( CascadeType.MERGE, relAnno.cascade()[2] ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[3] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[4] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[5] ); + } + + @Test + public void testAllAttributes() throws Exception { + reader = getReader( Entity2.class, "field1", "one-to-many.orm23.xml" ); + assertAnnotationPresent( OneToMany.class ); + assertAnnotationNotPresent( OrderBy.class ); + assertAnnotationNotPresent( OrderColumn.class ); + assertAnnotationNotPresent( MapKey.class ); + assertAnnotationNotPresent( MapKeyClass.class ); + assertAnnotationNotPresent( MapKeyTemporal.class ); + assertAnnotationNotPresent( MapKeyEnumerated.class ); + assertAnnotationNotPresent( MapKeyColumn.class ); + assertAnnotationNotPresent( MapKeyJoinColumns.class ); + assertAnnotationNotPresent( MapKeyJoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( Access.class ); + OneToMany relAnno = reader.getAnnotation( OneToMany.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.EAGER, relAnno.fetch() ); + assertEquals( "field2", relAnno.mappedBy() ); + assertTrue( relAnno.orphanRemoval() ); + assertEquals( Entity3.class, relAnno.targetEntity() ); + assertEquals( + AccessType.PROPERTY, reader.getAnnotation( Access.class ) + .value() + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToOneTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToOneTest.java new file mode 100644 index 0000000000..45c5e5e844 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToOneTest.java @@ -0,0 +1,309 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.CascadeType; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.JoinTable; +import javax.persistence.MapsId; +import javax.persistence.OneToOne; +import javax.persistence.PrimaryKeyJoinColumn; +import javax.persistence.PrimaryKeyJoinColumns; +import javax.persistence.UniqueConstraint; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlOneToOneTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlOneToOneTest extends LegacyEjb3XmlTestCase { + @Test + public void testNoChildren() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm1.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( MapsId.class ); + assertAnnotationNotPresent( Id.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationNotPresent( Access.class ); + OneToOne relAnno = reader.getAnnotation( OneToOne.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.EAGER, relAnno.fetch() ); + assertEquals( "", relAnno.mappedBy() ); + assertTrue( relAnno.optional() ); + assertFalse( relAnno.orphanRemoval() ); + assertEquals( void.class, relAnno.targetEntity() ); + } + + /** + * When there's a single primary key join column, we still wrap it with + * a PrimaryKeyJoinColumns annotation. + */ + @Test + public void testSinglePrimaryKeyJoinColumn() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm2.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationPresent( PrimaryKeyJoinColumns.class ); + PrimaryKeyJoinColumns joinColumnsAnno = + reader.getAnnotation( PrimaryKeyJoinColumns.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + PrimaryKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + assertEquals( "col2", joinColumns[0].referencedColumnName() ); + assertEquals( "int", joinColumns[0].columnDefinition() ); + } + + @Test + public void testMultiplePrimaryKeyJoinColumn() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm3.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + PrimaryKeyJoinColumns joinColumnsAnno = + reader.getAnnotation( PrimaryKeyJoinColumns.class ); + PrimaryKeyJoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + } + + /** + * When there's a single join column, we still wrap it with a JoinColumns + * annotation. + */ + @Test + public void testSingleJoinColumn() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm4.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 1, joinColumns.length ); + assertEquals( "col1", joinColumns[0].name() ); + assertEquals( "col2", joinColumns[0].referencedColumnName() ); + assertEquals( "table1", joinColumns[0].table() ); + } + + @Test + public void testMultipleJoinColumns() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm5.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinTable.class ); + JoinColumns joinColumnsAnno = reader.getAnnotation( JoinColumns.class ); + JoinColumn[] joinColumns = joinColumnsAnno.value(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table1", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + } + + @Test + public void testJoinTableNoChildren() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm6.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "", joinTableAnno.catalog() ); + assertEquals( "", joinTableAnno.name() ); + assertEquals( "", joinTableAnno.schema() ); + assertEquals( 0, joinTableAnno.joinColumns().length ); + assertEquals( 0, joinTableAnno.inverseJoinColumns().length ); + assertEquals( 0, joinTableAnno.uniqueConstraints().length ); + } + + @Test + public void testJoinTableAllChildren() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm7.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationPresent( JoinTable.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + JoinTable joinTableAnno = reader.getAnnotation( JoinTable.class ); + assertEquals( "cat1", joinTableAnno.catalog() ); + assertEquals( "table1", joinTableAnno.name() ); + assertEquals( "schema1", joinTableAnno.schema() ); + + // JoinColumns + JoinColumn[] joinColumns = joinTableAnno.joinColumns(); + assertEquals( 2, joinColumns.length ); + assertEquals( "", joinColumns[0].name() ); + assertEquals( "", joinColumns[0].referencedColumnName() ); + assertEquals( "", joinColumns[0].table() ); + assertEquals( "", joinColumns[0].columnDefinition() ); + assertTrue( joinColumns[0].insertable() ); + assertTrue( joinColumns[0].updatable() ); + assertTrue( joinColumns[0].nullable() ); + assertFalse( joinColumns[0].unique() ); + assertEquals( "col1", joinColumns[1].name() ); + assertEquals( "col2", joinColumns[1].referencedColumnName() ); + assertEquals( "table2", joinColumns[1].table() ); + assertEquals( "int", joinColumns[1].columnDefinition() ); + assertFalse( joinColumns[1].insertable() ); + assertFalse( joinColumns[1].updatable() ); + assertFalse( joinColumns[1].nullable() ); + assertTrue( joinColumns[1].unique() ); + + // InverseJoinColumns + JoinColumn[] inverseJoinColumns = joinTableAnno.inverseJoinColumns(); + assertEquals( 2, inverseJoinColumns.length ); + assertEquals( "", inverseJoinColumns[0].name() ); + assertEquals( "", inverseJoinColumns[0].referencedColumnName() ); + assertEquals( "", inverseJoinColumns[0].table() ); + assertEquals( "", inverseJoinColumns[0].columnDefinition() ); + assertTrue( inverseJoinColumns[0].insertable() ); + assertTrue( inverseJoinColumns[0].updatable() ); + assertTrue( inverseJoinColumns[0].nullable() ); + assertFalse( inverseJoinColumns[0].unique() ); + assertEquals( "col3", inverseJoinColumns[1].name() ); + assertEquals( "col4", inverseJoinColumns[1].referencedColumnName() ); + assertEquals( "table3", inverseJoinColumns[1].table() ); + assertEquals( "int", inverseJoinColumns[1].columnDefinition() ); + assertFalse( inverseJoinColumns[1].insertable() ); + assertFalse( inverseJoinColumns[1].updatable() ); + assertFalse( inverseJoinColumns[1].nullable() ); + assertTrue( inverseJoinColumns[1].unique() ); + + // UniqueConstraints + UniqueConstraint[] uniqueConstraints = joinTableAnno + .uniqueConstraints(); + assertEquals( 2, uniqueConstraints.length ); + assertEquals( "", uniqueConstraints[0].name() ); + assertEquals( 1, uniqueConstraints[0].columnNames().length ); + assertEquals( "col5", uniqueConstraints[0].columnNames()[0] ); + assertEquals( "uq1", uniqueConstraints[1].name() ); + assertEquals( 2, uniqueConstraints[1].columnNames().length ); + assertEquals( "col6", uniqueConstraints[1].columnNames()[0] ); + assertEquals( "col7", uniqueConstraints[1].columnNames()[1] ); + } + + @Test + public void testCascadeAll() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm8.xml" ); + assertAnnotationPresent( OneToOne.class ); + OneToOne relAnno = reader.getAnnotation( OneToOne.class ); + assertEquals( 1, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + } + + @Test + public void testCascadeSomeWithDefaultPersist() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm9.xml" ); + assertAnnotationPresent( OneToOne.class ); + OneToOne relAnno = reader.getAnnotation( OneToOne.class ); + assertEquals( 4, relAnno.cascade().length ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[0] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[1] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[2] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[3] ); + } + + /** + * Make sure that it doesn't break the handler when {@link CascadeType#ALL} + * is specified in addition to a default cascade-persist or individual + * cascade settings. + */ + @Test + public void testCascadeAllPlusMore() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm10.xml" ); + assertAnnotationPresent( OneToOne.class ); + OneToOne relAnno = reader.getAnnotation( OneToOne.class ); + assertEquals( 6, relAnno.cascade().length ); + assertEquals( CascadeType.ALL, relAnno.cascade()[0] ); + assertEquals( CascadeType.PERSIST, relAnno.cascade()[1] ); + assertEquals( CascadeType.MERGE, relAnno.cascade()[2] ); + assertEquals( CascadeType.REMOVE, relAnno.cascade()[3] ); + assertEquals( CascadeType.REFRESH, relAnno.cascade()[4] ); + assertEquals( CascadeType.DETACH, relAnno.cascade()[5] ); + } + + @Test + public void testAllAttributes() throws Exception { + reader = getReader( Entity1.class, "field1", "one-to-one.orm11.xml" ); + assertAnnotationPresent( OneToOne.class ); + assertAnnotationPresent( MapsId.class ); + assertAnnotationPresent( Id.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumn.class ); + assertAnnotationNotPresent( PrimaryKeyJoinColumns.class ); + assertAnnotationNotPresent( JoinColumns.class ); + assertAnnotationNotPresent( JoinColumn.class ); + assertAnnotationNotPresent( JoinTable.class ); + assertAnnotationPresent( Access.class ); + OneToOne relAnno = reader.getAnnotation( OneToOne.class ); + assertEquals( 0, relAnno.cascade().length ); + assertEquals( FetchType.LAZY, relAnno.fetch() ); + assertEquals( "field2", relAnno.mappedBy() ); + assertFalse( relAnno.optional() ); + assertTrue( relAnno.orphanRemoval() ); + assertEquals( Entity3.class, relAnno.targetEntity() ); + assertEquals( + AccessType.PROPERTY, reader.getAnnotation( Access.class ) + .value() + ); + assertEquals( + "field3", reader.getAnnotation( MapsId.class ) + .value() + ); + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTest.java new file mode 100644 index 0000000000..984ae0a316 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTest.java @@ -0,0 +1,148 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import java.util.Date; +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.PostgreSQL81Dialect; +import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.TeradataDialect; +import org.hibernate.persister.collection.BasicCollectionPersister; + +import org.hibernate.testing.SkipForDialect; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +public class LegacyEjb3XmlTest extends BaseCoreFunctionalTestCase { + @Test + @SkipForDialect(value = {PostgreSQL81Dialect.class, PostgreSQLDialect.class, CockroachDialect.class}, + comment = "postgresql jdbc driver does not implement the setQueryTimeout method") + @SkipForDialect(value = TeradataDialect.class, + jiraKey = "HHH-8190", + comment = "uses Teradata reserved word - year") + public void testEjb3Xml() throws Exception { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + CarModel model = new CarModel(); + model.setYear( new Date() ); + Manufacturer manufacturer = new Manufacturer(); + //s.persist( manufacturer ); + model.setManufacturer( manufacturer ); + manufacturer.getModels().add( model ); + s.persist( model ); + s.flush(); + s.clear(); + + model.setYear( new Date() ); + manufacturer = (Manufacturer) s.get( Manufacturer.class, manufacturer.getId() ); + @SuppressWarnings("unchecked") + List cars = s.getNamedQuery( "allModelsPerManufacturer" ) + .setParameter( "manufacturer", manufacturer ) + .list(); + assertEquals( 1, cars.size() ); + for ( Model car : cars ) { + assertNotNull( car.getManufacturer() ); + s.delete( manufacturer ); + s.delete( car ); + } + tx.rollback(); + s.close(); + } + + @Test + public void testXMLEntityHandled() throws Exception { + Session s = openSession(); + s.getTransaction().begin(); + Lighter l = new Lighter(); + l.name = "Blue"; + l.power = "400F"; + s.persist( l ); + s.flush(); + s.getTransaction().rollback(); + s.close(); + } + + @Test + public void testXmlDefaultOverriding() throws Exception { + Session s = openSession(); + Transaction tx = s.beginTransaction(); + Manufacturer manufacturer = new Manufacturer(); + s.persist( manufacturer ); + s.flush(); + s.clear(); + + assertEquals( 1, s.getNamedQuery( "manufacturer.findAll" ).list().size() ); + tx.rollback(); + s.close(); + } + + @Test + @SuppressWarnings("unchecked") + public void testMapXMLSupport() throws Exception { + Session s = openSession(); + SessionFactory sf = s.getSessionFactory(); + Transaction tx = s.beginTransaction(); + + // Verify that we can persist an object with a couple Map mappings + VicePresident vpSales = new VicePresident(); + vpSales.name = "Dwight"; + Company company = new Company(); + company.conferenceRoomExtensions.put( "8932", "x1234" ); + company.organization.put( "sales", vpSales ); + s.persist( company ); + s.flush(); + s.clear(); + + // For the element-collection, check that the orm.xml entries are honored. + // This includes: map-key-column/column/collection-table/join-column + BasicCollectionPersister confRoomMeta = (BasicCollectionPersister) sf.getCollectionMetadata( Company.class.getName() + ".conferenceRoomExtensions" ); + assertEquals( "company_id", confRoomMeta.getKeyColumnNames()[0] ); + assertEquals( "phone_extension", confRoomMeta.getElementColumnNames()[0] ); + assertEquals( "room_number", confRoomMeta.getIndexColumnNames()[0] ); + assertEquals( "phone_extension_lookup", confRoomMeta.getTableName() ); + tx.rollback(); + s.close(); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + CarModel.class, + Manufacturer.class, + Model.class, + Light.class + //Lighter.class xml only entuty + }; + } + + @Override + protected String[] getOrmXmlFiles() { + return new String[] { + "org/hibernate/test/annotations/xml/ejb3/orm.xml", + "org/hibernate/test/annotations/xml/ejb3/orm2.xml", + "org/hibernate/test/annotations/xml/ejb3/orm3.xml", + "org/hibernate/test/annotations/xml/ejb3/orm4.xml" + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTestCase.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTestCase.java new file mode 100644 index 0000000000..e22dfea26e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTestCase.java @@ -0,0 +1,82 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; + +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; +import org.hibernate.cfg.annotations.reflection.XMLContext; + +import org.hibernate.testing.boot.BootstrapContextImpl; +import org.hibernate.testing.junit4.BaseUnitTestCase; + +import org.dom4j.Document; +import org.dom4j.io.SAXReader; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test superclass to provide utility methods for testing the mapping of JPA + * XML to JPA annotations. The configuration is built within each test, and no + * database is used. Thus, no schema generation or cleanup will be performed. + *

+ * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.Ejb3XmlTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@Deprecated +abstract class LegacyEjb3XmlTestCase extends BaseUnitTestCase { + protected JPAOverriddenAnnotationReader reader; + + protected void assertAnnotationPresent(Class annotationType) { + assertTrue( + "Expected annotation " + annotationType.getSimpleName() + " was not present", + reader.isAnnotationPresent( annotationType ) + ); + } + + protected void assertAnnotationNotPresent(Class annotationType) { + assertFalse( + "Unexpected annotation " + annotationType.getSimpleName() + " was present", + reader.isAnnotationPresent( annotationType ) + ); + } + + protected JPAOverriddenAnnotationReader getReader(Class entityClass, String fieldName, String ormResourceName) + throws Exception { + AnnotatedElement el = getAnnotatedElement( entityClass, fieldName ); + XMLContext xmlContext = getContext( ormResourceName ); + return new JPAOverriddenAnnotationReader( el, xmlContext, BootstrapContextImpl.INSTANCE ); + } + + protected AnnotatedElement getAnnotatedElement(Class entityClass, String fieldName) throws Exception { + return entityClass.getDeclaredField( fieldName ); + } + + protected XMLContext getContext(String resourceName) throws Exception { + InputStream is = getClass().getResourceAsStream( resourceName ); + assertNotNull( "Could not load resource " + resourceName, is ); + return getContext( is ); + } + + protected XMLContext getContext(InputStream is) throws Exception { + XMLContext xmlContext = new XMLContext( BootstrapContextImpl.INSTANCE ); + SAXReader reader = new SAXReader(); + reader.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false ); + reader.setFeature( "http://xml.org/sax/features/external-general-entities", false ); + reader.setFeature( "http://xml.org/sax/features/external-parameter-entities", false ); + Document doc = reader.read( is ); + xmlContext.addDocument( doc ); + return xmlContext; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyNonExistentOrmVersionTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyNonExistentOrmVersionTest.java new file mode 100644 index 0000000000..74509b879c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyNonExistentOrmVersionTest.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import org.hibernate.InvalidMappingException; +import org.hibernate.boot.MetadataSources; +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; +import org.hibernate.internal.util.xml.UnsupportedOrmXsdVersionException; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Test; + +import static org.junit.Assert.fail; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.NonExistentOrmVersionTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@TestForIssue(jiraKey = "HHH-6271") +public class LegacyNonExistentOrmVersionTest extends BaseUnitTestCase { + @Test + public void testNonExistentOrmVersion() { + try { + new MetadataSources() + .addResource( "org/hibernate/test/annotations/xml/ejb3/orm5.xml" ) + .buildMetadata(); + fail( "Expecting failure due to unsupported xsd version" ); + } + catch ( InvalidMappingException expected ) { + } + catch ( UnsupportedOrmXsdVersionException expected ) { + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyOrmVersion1SupportedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyOrmVersion1SupportedTest.java new file mode 100644 index 0000000000..912ea91b3d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyOrmVersion1SupportedTest.java @@ -0,0 +1,68 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import org.hibernate.Session; +import org.hibernate.Transaction; +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.internal.util.xml.ErrorLogger; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.testing.logger.LoggerInspectionRule; +import org.hibernate.testing.logger.Triggerable; +import org.junit.Rule; +import org.junit.Test; + +import org.jboss.logging.Logger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.OrmVersion1SupportedTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@TestForIssue(jiraKey = "HHH-6271") +public class LegacyOrmVersion1SupportedTest extends BaseCoreFunctionalTestCase { + + @Rule + public LoggerInspectionRule logInspection = new LoggerInspectionRule( + Logger.getMessageLogger( + CoreMessageLogger.class, + ErrorLogger.class.getName() + ) + ); + + @Test + public void testOrm1Support() { + Triggerable triggerable = logInspection.watchForLogMessages( "HHH00196" ); + + Session s = openSession(); + Transaction tx = s.beginTransaction(); + Light light = new Light(); + light.name = "the light at the end of the tunnel"; + s.persist( light ); + s.flush(); + s.clear(); + + assertEquals( 1, s.getNamedQuery( "find.the.light" ).list().size() ); + tx.rollback(); + s.close(); + + assertFalse( triggerable.wasTriggered() ); + } + + @Override + protected String[] getOrmXmlFiles() { + return new String[] { "org/hibernate/test/annotations/xml/ejb3/orm2.xml" }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyPreParsedOrmXmlTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyPreParsedOrmXmlTest.java new file mode 100644 index 0000000000..9d3e4654b2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyPreParsedOrmXmlTest.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; + +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.annotations.reflection.JPAOverriddenAnnotationReader; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.test.annotations.xml.ejb3.PreParsedOrmXmlTest.NonAnnotatedEntity; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Equivalent to {@link org.hibernate.test.annotations.xml.ejb3.PreParsedOrmXmlTest} + * for the legacy {@link JPAOverriddenAnnotationReader}. + * + * @author Emmanuel Bernard + * @deprecated This test will be removed in Hibernate ORM 6, along with the legacy {@link JPAOverriddenAnnotationReader}. + */ +@TestForIssue(jiraKey = "HHH-14530") +public class LegacyPreParsedOrmXmlTest extends BaseCoreFunctionalTestCase { + + @Override + protected void addMappings(Configuration configuration) { + super.addMappings( configuration ); + try (InputStream xmlStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream( "org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml" )) { + Binding parsed = configuration.getXmlMappingBinderAccess().bind( xmlStream ); + configuration.addXmlMapping( parsed ); + } + catch (IOException e) { + throw new UncheckedIOException( e ); + } + } + + @Test + public void testPreParsedOrmXml() { + // Just check that the entity can be persisted, which means the mapping file was taken into account + NonAnnotatedEntity persistedEntity = new NonAnnotatedEntity( "someName" ); + inTransaction( s -> s.persist( persistedEntity ) ); + inTransaction( s -> { + NonAnnotatedEntity retrievedEntity = s.find( NonAnnotatedEntity.class, persistedEntity.getId() ); + assertThat( retrievedEntity ).extracting( NonAnnotatedEntity::getName ) + .isEqualTo( persistedEntity.getName() ); + } ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/NonExistentOrmVersionTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/NonExistentOrmVersionTest.java index 986c9c077e..13a8535ca9 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/NonExistentOrmVersionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/NonExistentOrmVersionTest.java @@ -8,7 +8,9 @@ package org.hibernate.test.annotations.xml.ejb3; import org.hibernate.InvalidMappingException; import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.internal.util.xml.UnsupportedOrmXsdVersionException; +import org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseUnitTestCase; @@ -16,12 +18,14 @@ import org.junit.Test; import static org.junit.Assert.fail; -@TestForIssue(jiraKey = "HHH-6271") +@TestForIssue(jiraKey = {"HHH-6271", "HHH-14529"}) public class NonExistentOrmVersionTest extends BaseUnitTestCase { @Test public void testNonExistentOrmVersion() { try { - new MetadataSources() + BootstrapServiceRegistryBuilder builder = new BootstrapServiceRegistryBuilder(); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); + new MetadataSources( builder.build() ) .addResource( "org/hibernate/test/annotations/xml/ejb3/orm5.xml" ) .buildMetadata(); fail( "Expecting failure due to unsupported xsd version" ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java index dc3f2b4d1f..9b6c2e2dbe 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java @@ -8,8 +8,10 @@ package org.hibernate.test.annotations.xml.ejb3; import org.hibernate.Session; import org.hibernate.Transaction; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.xml.ErrorLogger; +import org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -23,9 +25,15 @@ import org.jboss.logging.Logger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -@TestForIssue(jiraKey = "HHH-6271") +@TestForIssue(jiraKey = {"HHH-6271", "HHH-14529"}) public class OrmVersion1SupportedTest extends BaseCoreFunctionalTestCase { + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); + } + @Rule public LoggerInspectionRule logInspection = new LoggerInspectionRule( Logger.getMessageLogger( diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java new file mode 100644 index 0000000000..057c4ede5d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java @@ -0,0 +1,86 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.ejb3; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; + +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestForIssue(jiraKey = {"HHH-14530", "HHH-14529"}) +public class PreParsedOrmXmlTest extends BaseCoreFunctionalTestCase { + + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); + } + + @Override + protected void addMappings(Configuration configuration) { + super.addMappings( configuration ); + try (InputStream xmlStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream( "org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml" )) { + Binding parsed = configuration.getXmlMappingBinderAccess().bind( xmlStream ); + configuration.addXmlMapping( parsed ); + } + catch (IOException e) { + throw new UncheckedIOException( e ); + } + } + + @Test + public void testPreParsedOrmXml() { + // Just check that the entity can be persisted, which means the mapping file was taken into account + NonAnnotatedEntity persistedEntity = new NonAnnotatedEntity( "someName" ); + inTransaction( s -> s.persist( persistedEntity ) ); + inTransaction( s -> { + NonAnnotatedEntity retrievedEntity = s.find( NonAnnotatedEntity.class, persistedEntity.getId() ); + assertThat( retrievedEntity ).extracting( NonAnnotatedEntity::getName ) + .isEqualTo( persistedEntity.getName() ); + } ); + } + + public static class NonAnnotatedEntity { + private long id; + + private String name; + + public NonAnnotatedEntity() { + } + + public NonAnnotatedEntity(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/PreParsedHbmXmlTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/PreParsedHbmXmlTest.java new file mode 100644 index 0000000000..441751aa61 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/PreParsedHbmXmlTest.java @@ -0,0 +1,78 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.annotations.xml.hbm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; + +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.cfg.Configuration; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestForIssue(jiraKey = "HHH-14530") +public class PreParsedHbmXmlTest extends BaseCoreFunctionalTestCase { + + @Override + protected void addMappings(Configuration configuration) { + super.addMappings( configuration ); + try (InputStream xmlStream = Thread.currentThread().getContextClassLoader() + .getResourceAsStream( "org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml" )) { + Binding parsed = configuration.getXmlMappingBinderAccess().bind( xmlStream ); + configuration.addXmlMapping( parsed ); + } + catch (IOException e) { + throw new UncheckedIOException( e ); + } + } + + @Test + public void testPreParsedHbmXml() { + // Just check that the entity can be persisted, which means the mapping file was taken into account + NonAnnotatedEntity persistedEntity = new NonAnnotatedEntity( "someName" ); + inTransaction( s -> s.persist( persistedEntity ) ); + inTransaction( s -> { + NonAnnotatedEntity retrievedEntity = s.find( NonAnnotatedEntity.class, persistedEntity.getId() ); + assertThat( retrievedEntity ).extracting( NonAnnotatedEntity::getName ) + .isEqualTo( persistedEntity.getName() ); + } ); + } + + public static class NonAnnotatedEntity { + private long id; + + private String name; + + public NonAnnotatedEntity() { + } + + public NonAnnotatedEntity(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/orm-serializable.xml b/hibernate-core/src/test/java/org/hibernate/test/cfg/orm-serializable.xml deleted file mode 100644 index c9620d83b8..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/orm-serializable.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java index 365dd3cdc4..69305549d8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/connections/BeforeCompletionReleaseTest.java @@ -110,11 +110,11 @@ public class BeforeCompletionReleaseTest extends BaseEntityManagerFunctionalTest @Test @TestForIssue(jiraKey = {"HHH-13976", "HHH-14326"}) public void testResourcesReleasedThenConnectionClosedThenCommit() throws SQLException, XAException { - XAResource transactionSpy = mock( XAResource.class ); - Connection[] connectionSpies = new Connection[1]; - Statement statementMock = Mockito.mock( Statement.class ); - try (SessionImplementor s = (SessionImplementor) openSession()) { + XAResource transactionSpy = mock( XAResource.class ); + Connection[] connectionSpies = new Connection[1]; + Statement statementMock = Mockito.mock( Statement.class ); + TransactionUtil2.inTransaction( s, session -> { spyOnTransaction( transactionSpy ); @@ -126,16 +126,62 @@ public class BeforeCompletionReleaseTest extends BaseEntityManagerFunctionalTest logicalConnection.getResourceRegistry().register( statementMock, true ); connectionSpies[0] = logicalConnection.getPhysicalConnection(); } ); + + // Note: all this must happen BEFORE the session is closed; + // it's particularly important when reusing the session. + + Connection connectionSpy = connectionSpies[0]; + + // Must close the resources, then the connection, then commit + InOrder inOrder = inOrder( statementMock, connectionSpy, transactionSpy ); + inOrder.verify( statementMock ).close(); + inOrder.verify( connectionSpy ).close(); + inOrder.verify( transactionSpy ).commit( any(), anyBoolean() ); } + } - Connection connectionSpy = connectionSpies[0]; + @Test + @TestForIssue(jiraKey = {"HHH-14557"}) + public void testResourcesReleasedThenConnectionClosedOnEachRollback() throws SQLException { + try (SessionImplementor s = (SessionImplementor) openSession()) { + Connection[] connectionSpies = new Connection[1]; + Statement statementMock = Mockito.mock( Statement.class ); + RuntimeException rollbackException = new RuntimeException("Rollback"); - // Must close the resources, then the connection, then commit - InOrder inOrder = inOrder( statementMock, connectionSpy, transactionSpy ); - inOrder.verify( statementMock ).close(); - inOrder.verify( connectionSpy ).close(); - inOrder.verify( transactionSpy ).commit( any(), anyBoolean() ); - Mockito.reset( connectionSpy ); + try { + TransactionUtil2.inTransaction( s, session -> { + Thing thing = new Thing(); + thing.setId( 1 ); + session.persist( thing ); + + LogicalConnectionImplementor logicalConnection = session.getJdbcCoordinator().getLogicalConnection(); + logicalConnection.getResourceRegistry().register( statementMock, true ); + connectionSpies[0] = logicalConnection.getPhysicalConnection(); + + throw rollbackException; + } ); + } + catch (RuntimeException e) { + if ( e != rollbackException ) { + throw e; + } + // Else: ignore, that was expected. + } + + // Note: all this must happen BEFORE the session is closed; + // it's particularly important when reusing the session. + + Connection connectionSpy = connectionSpies[0]; + + // Must close the resources, then the connection + InOrder inOrder = inOrder( statementMock, connectionSpy ); + inOrder.verify( statementMock ).close(); + inOrder.verify( connectionSpy ).close(); + // We don't check the relative ordering of the rollback here, + // because unfortunately we know it's wrong: + // we don't get a "before transaction completion" event for rollbacks, + // so in the case of rollbacks the closing always happen after transaction completion. + } } private void spyOnTransaction(XAResource xaResource) { diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java index b58142cf08..9e018f00b1 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectCollationTest.java @@ -48,15 +48,16 @@ import static org.junit.Assert.fail; public class SQLServerDialectCollationTest extends BaseCoreFunctionalTestCase { @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.setProperty( AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, Boolean.TRUE.toString() ); - return configuration; } + @Override protected void buildSessionFactory() { BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); - StandardServiceRegistryImpl _serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() ); + StandardServiceRegistryImpl _serviceRegistry = + buildServiceRegistry( bootRegistry, constructAndConfigureConfiguration( bootRegistry ) ); try { try { diff --git a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java index 217fc7c8c0..399f59979f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTempTableCollationTest.java @@ -43,10 +43,9 @@ public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTe private boolean collationChanged; @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.setProperty( AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED, Boolean.TRUE.toString() ); - return configuration; } @Override @@ -56,7 +55,7 @@ public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTe BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, - constructConfiguration() + constructAndConfigureConfiguration( bootRegistry ) ); try { TransactionUtil.doWithJDBC( @@ -92,7 +91,8 @@ public class SQLServerDialectTempTableCollationTest extends BaseCoreFunctionalTe @Override protected void buildSessionFactory() { BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); - StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, constructConfiguration() ); + StandardServiceRegistryImpl serviceRegistry = + buildServiceRegistry( bootRegistry, constructAndConfigureConfiguration( bootRegistry ) ); try { try { diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/TenantResolverConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/TenantResolverConfigurationTest.java index e8423e6a4a..18aacf5428 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/TenantResolverConfigurationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/TenantResolverConfigurationTest.java @@ -24,10 +24,9 @@ public class TenantResolverConfigurationTest extends BaseCoreFunctionalTestCase private TestCurrentTenantIdentifierResolver currentTenantResolver = new TestCurrentTenantIdentifierResolver(); @Override - protected Configuration constructAndConfigureConfiguration() { - Configuration configuration = super.constructAndConfigureConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.setCurrentTenantIdentifierResolver( currentTenantResolver ); - return configuration; } @Test diff --git a/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider new file mode 100644 index 0000000000..98148e1689 --- /dev/null +++ b/hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider @@ -0,0 +1 @@ +org.hibernate.internal.util.xml.XmlMappingOptionsStrategyRegistrationProvider \ No newline at end of file diff --git a/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml b/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml new file mode 100644 index 0000000000..bc06432f18 --- /dev/null +++ b/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml b/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml new file mode 100644 index 0000000000..8aca88a94f --- /dev/null +++ b/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + diff --git a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java index 819c6a451f..a3c4d5138f 100644 --- a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java +++ b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java @@ -33,13 +33,12 @@ public abstract class AbstractBulkCompositeIdTest extends BaseCoreFunctionalTest } @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); Class strategyClass = getMultiTableBulkIdStrategyClass(); if ( strategyClass != null ) { - configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, strategyClass.getName() ); + configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, strategyClass.getName() ); } - return configuration; } protected abstract Class getMultiTableBulkIdStrategyClass(); diff --git a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkIdTest.java b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkIdTest.java index 73bef97192..76b74adb9d 100644 --- a/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkIdTest.java +++ b/hibernate-core/src/test_legacy/org/hibernate/test/bulkid/AbstractBulkIdTest.java @@ -32,12 +32,13 @@ public abstract class AbstractBulkIdTest extends BaseCoreFunctionalTestCase { } @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); - configuration.setProperty( AvailableSettings.QUERY_MULTI_TABLE_MUTATION_STRATEGY, getMultiTableBulkIdStrategyClass().getName() ); - return configuration; + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, getMultiTableBulkIdStrategyClass().getName() ); } + protected abstract Class getMultiTableBulkIdStrategyClass(); @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/AdditionalJaxbMappingProducerImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/AdditionalJaxbMappingProducerImpl.java index 57e86eb10b..64415b8833 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/AdditionalJaxbMappingProducerImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/AdditionalJaxbMappingProducerImpl.java @@ -24,12 +24,15 @@ import org.hibernate.boot.jaxb.SourceType; import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmHibernateMapping; import org.hibernate.boot.jaxb.internal.MappingBinder; import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.boot.jaxb.spi.XmlMappingOptions; import org.hibernate.boot.model.source.internal.hbm.MappingDocument; import org.hibernate.boot.spi.AdditionalJaxbMappingProducer; import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.envers.boot.EnversBootLogger; import org.hibernate.envers.configuration.internal.MappingCollector; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.ServiceRegistry; import org.jboss.jandex.IndexView; @@ -40,6 +43,8 @@ import org.dom4j.DocumentException; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; +import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; + /** * @author Steve Ebersole */ @@ -52,7 +57,8 @@ public class AdditionalJaxbMappingProducerImpl implements AdditionalJaxbMappingP IndexView jandexIndex, final MappingBinder mappingBinder, final MetadataBuildingContext buildingContext) { - final ServiceRegistry serviceRegistry = metadata.getMetadataBuildingOptions().getServiceRegistry(); + MetadataBuildingOptions metadataBuildingOptions = metadata.getMetadataBuildingOptions(); + final ServiceRegistry serviceRegistry = metadataBuildingOptions.getServiceRegistry(); final EnversService enversService = serviceRegistry.getService( EnversService.class ); if ( !enversService.isEnabled() ) { @@ -60,6 +66,12 @@ public class AdditionalJaxbMappingProducerImpl implements AdditionalJaxbMappingP return Collections.emptyList(); } + if ( !metadataBuildingOptions.getXmlMappingOptions().isEnabled() ) { + throw new HibernateException( "Hibernate Envers currently requires XML mapping to be enabled." + + " Please don't disable setting `" + XML_MAPPING_ENABLED + + "`; alternatively disable Hibernate Envers." ); + } + final ArrayList additionalMappingDocuments = new ArrayList<>(); // atm we do not have distinct origin info for envers diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java index f0ab2ad6f2..05d749b3b1 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java @@ -9,14 +9,11 @@ package org.hibernate.envers.boot.internal; import java.util.Map; import java.util.Properties; -import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.config.spi.ConfigurationService; -import org.hibernate.engine.config.spi.StandardConverters; import org.hibernate.envers.configuration.internal.AuditEntitiesConfiguration; import org.hibernate.envers.configuration.internal.EntitiesConfigurator; import org.hibernate.envers.configuration.internal.GlobalConfiguration; @@ -39,8 +36,6 @@ import org.hibernate.service.spi.Stoppable; import org.jboss.logging.Logger; -import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; - /** * Provides central access to Envers' configuration. * @@ -89,10 +84,6 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable ); } this.integrationEnabled = ConfigurationHelper.getBoolean( INTEGRATION_ENABLED, configurationValues, true ); - boolean xmlMappingEnabled = ConfigurationHelper.getBoolean( XML_MAPPING_ENABLED, configurationValues, true ); - if ( this.integrationEnabled && !xmlMappingEnabled ) { - throw new HibernateException( "Hibernate Envers currently requires XML mapping to be enabled. Please don't disable setting `" + XML_MAPPING_ENABLED + "`; alternatively disable Hibernate Envers." ); - } log.infof( "Envers integration enabled? : %s", integrationEnabled ); } diff --git a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java index 7230cdd2ba..b57dec4cb7 100644 --- a/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java +++ b/hibernate-spatial/src/main/java/org/hibernate/spatial/dialect/postgis/PGGeometryTypeDescriptor.java @@ -20,6 +20,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BasicBinder; import org.hibernate.type.descriptor.jdbc.BasicExtractor; import org.hibernate.type.descriptor.jdbc.SqlTypeDescriptor; +import org.hibernate.type.descriptor.sql.BasicExtractor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; import org.geolatte.geom.ByteBuffer; import org.geolatte.geom.ByteOrder; @@ -39,12 +41,12 @@ import org.postgresql.util.PGobject; public class PGGeometryTypeDescriptor implements SqlTypeDescriptor { - final private Wkb.Dialect wkbDialect; + private final Wkb.Dialect wkbDialect; // Type descriptor instance using EWKB v1 (postgis versions < 2.2.2) - public static final PGGeometryTypeDescriptor INSTANCE_WKB_1 = new PGGeometryTypeDescriptor( Wkb.Dialect.POSTGIS_EWKB_1); + public static final PGGeometryTypeDescriptor INSTANCE_WKB_1 = new PGGeometryTypeDescriptor( Wkb.Dialect.POSTGIS_EWKB_1 ); // Type descriptor instance using EWKB v2 (postgis versions >= 2.2.2, see: https://trac.osgeo.org/postgis/ticket/3181) - public static final PGGeometryTypeDescriptor INSTANCE_WKB_2 = new PGGeometryTypeDescriptor(Wkb.Dialect.POSTGIS_EWKB_2); + public static final PGGeometryTypeDescriptor INSTANCE_WKB_2 = new PGGeometryTypeDescriptor( Wkb.Dialect.POSTGIS_EWKB_2 ); private PGGeometryTypeDescriptor(Wkb.Dialect dialect) { wkbDialect = dialect; @@ -79,7 +81,7 @@ public class PGGeometryTypeDescriptor implements SqlTypeDescriptor { @Override public int getSqlType() { - return Types.OTHER; + return 5432; } @Override @@ -122,8 +124,8 @@ public class PGGeometryTypeDescriptor implements SqlTypeDescriptor { return new BasicExtractor( javaTypeDescriptor, this ) { @Override - protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException { - return getJavaDescriptor().wrap( toGeometry( rs.getObject( paramIndex ) ), options ); + protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { + return getJavaDescriptor().wrap( toGeometry( rs.getObject( name ) ), options ); } @Override diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index 116cb48ecf..c1ea6fb791 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -113,11 +113,11 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { protected void buildSessionFactory(Consumer configurationAdapter) { // for now, build the configuration to get all the property settings - configuration = constructAndConfigureConfiguration(); + BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); + configuration = constructAndConfigureConfiguration( bootRegistry ); if ( configurationAdapter != null ) { configurationAdapter.accept(configuration); } - BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); serviceRegistry = buildServiceRegistry( bootRegistry, configuration ); // this is done here because Configuration does not currently support 4.0 xsd afterConstructAndConfigureConfiguration( configuration ); @@ -146,14 +146,8 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { buildSessionFactory( configurationAdapter ); } - protected Configuration buildConfiguration() { - Configuration cfg = constructAndConfigureConfiguration(); - afterConstructAndConfigureConfiguration( cfg ); - return cfg; - } - - protected Configuration constructAndConfigureConfiguration() { - Configuration cfg = constructConfiguration(); + protected Configuration constructAndConfigureConfiguration(BootstrapServiceRegistry bootstrapServiceRegistry) { + Configuration cfg = constructConfiguration( bootstrapServiceRegistry ); configure( cfg ); return cfg; } @@ -164,8 +158,8 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { afterConfigurationBuilt( cfg ); } - protected Configuration constructConfiguration() { - Configuration configuration = new Configuration(); + protected Configuration constructConfiguration(BootstrapServiceRegistry bootstrapServiceRegistry) { + Configuration configuration = new Configuration( bootstrapServiceRegistry ); configuration.setProperty( AvailableSettings.CACHE_REGION_FACTORY, CachingRegionFactory.class.getName() ); configuration.setProperty( AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true" ); if ( createSchema() ) {