From 3825a671a70fc67e0773019c0c4f2f80278a24a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Apr 2021 14:24:10 +0200 Subject: [PATCH 01/21] HHH-14557 Always release JDBC connection and resources on rollback --- .../hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java | 3 ++- .../jdbc/internal/LogicalConnectionManagedImpl.java | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 5e018dc63ee4..2d0dae12e242 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 @@ -270,7 +270,8 @@ public void afterStatementExecution() { 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/resource/jdbc/internal/LogicalConnectionManagedImpl.java b/hibernate-core/src/main/java/org/hibernate/resource/jdbc/internal/LogicalConnectionManagedImpl.java index 1d13e5d28408..6ac9a878a55b 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 @@ -167,8 +167,10 @@ public void afterTransaction() { 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(); } From e03beca97f9d117f7d651b66f5241322f7769e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Apr 2021 14:09:43 +0200 Subject: [PATCH 02/21] HHH-14557 Test JDBC resources are released on each commit Not just on session closing. --- .../BeforeCompletionReleaseTest.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) 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 365dd3cdc437..e60735d23021 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 @@ protected Class[] getAnnotatedClasses() { @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,18 @@ public void testResourcesReleasedThenConnectionClosedThenCommit() throws SQLExce logicalConnection.getResourceRegistry().register( statementMock, true ); connectionSpies[0] = logicalConnection.getPhysicalConnection(); } ); - } - Connection connectionSpy = connectionSpies[0]; + // Note: all this must happen BEFORE the session is closed; + // it's particularly important when reusing the session. - // 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 ); + 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() ); + } } private void spyOnTransaction(XAResource xaResource) { From 571af7bc9ee6b926abf70e219d862ad48dcc25e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 13 Apr 2021 14:32:14 +0200 Subject: [PATCH 03/21] HHH-14557 Test JDBC resources are released on each rollback --- .../BeforeCompletionReleaseTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) 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 e60735d23021..69305549d83a 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 @@ -140,6 +140,50 @@ public void testResourcesReleasedThenConnectionClosedThenCommit() throws SQLExce } } + @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"); + + 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) { try { TestingJtaPlatformImpl.transactionManager().getTransaction().enlistResource( xaResource ); From ebb30aa178a36af567ddd8bf4e0a6481b44ec82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Mon, 22 Mar 2021 16:36:46 +0100 Subject: [PATCH 04/21] HHH-14530 Allow adding pre-parsed XML mappings to MetadataSources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yoann Rodière --- .../org/hibernate/boot/MetadataSources.java | 12 +++ .../java/org/hibernate/cfg/Configuration.java | 20 +++++ .../xml/ejb3/PreParsedOrmXmlTest.java | 78 +++++++++++++++++++ .../xml/hbm/PreParsedHbmXmlTest.java | 78 +++++++++++++++++++ .../annotations/xml/ejb3/pre-parsed-orm.xml | 22 ++++++ .../annotations/xml/hbm/pre-parsed-hbm.xml | 18 +++++ 6 files changed, 228 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/PreParsedOrmXmlTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/PreParsedHbmXmlTest.java create mode 100644 hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/ejb3/pre-parsed-orm.xml create mode 100644 hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml 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 5e86f504ad46..932ac5c16cf1 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/MetadataSources.java @@ -338,6 +338,18 @@ public MetadataSources addFile(File file) { 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/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index 85b63d24a4fe..d6c4715f1ab0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -29,6 +29,7 @@ import org.hibernate.boot.MetadataBuilder; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.jaxb.spi.Binding; import org.hibernate.boot.model.TypeContributor; import org.hibernate.boot.model.naming.ImplicitNamingStrategy; import org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl; @@ -39,6 +40,7 @@ import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.boot.spi.XmlMappingBinderAccess; import org.hibernate.cfg.annotations.NamedEntityGraphDefinition; import org.hibernate.cfg.annotations.NamedProcedureCallDefinition; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; @@ -364,6 +366,13 @@ public Configuration addFile(File xmlFile) throws MappingException { 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. */ @@ -371,6 +380,17 @@ public Configuration addFile(File xmlFile) throws MappingException { 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/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 000000000000..04640901d860 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/PreParsedOrmXmlTest.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.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.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 PreParsedOrmXmlTest 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() ); + } ); + } + + 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 000000000000..441751aa61b0 --- /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/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 000000000000..bc06432f182a --- /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 000000000000..8aca88a94f5f --- /dev/null +++ b/hibernate-core/src/test/resources/org/hibernate/test/annotations/xml/hbm/pre-parsed-hbm.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + From fcc63d9ab0d4e2206d1d1d92ca2b24f1e38e5f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 31 Mar 2021 16:51:42 +0200 Subject: [PATCH 05/21] HHH-14529 Stop using two separate BootstrapServiceRegistries for the same SessionFactory in BaseCoreFunctionalTestCase --- .../collections/BasicTypeCollectionTest.java | 5 ++--- .../mapping/basic/BitSetTypeTest.java | 5 ++--- .../mapping/basic/BitSetUserTypeTest.java | 5 ++--- .../bulkid/AbstractBulkCompositeIdTest.java | 5 ++--- .../test/bulkid/AbstractBulkIdTest.java | 5 ++--- .../SQLServerDialectCollationTest.java | 9 +++++---- ...SQLServerDialectTempTableCollationTest.java | 10 +++++----- .../functional/SQLServerDialectTest.java | 5 ++--- .../TenantResolverConfigurationTest.java | 5 ++--- .../junit4/BaseCoreFunctionalTestCase.java | 18 ++++++------------ 10 files changed, 30 insertions(+), 42 deletions(-) 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 8def4aedf7c1..edf53fcb330a 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 void testLifecycle() { } @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 d7e426463351..baa87e38aad5 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 @@ protected Class[] getAnnotatedClasses() { } @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 a0c7fd7293fe..6a7a7ffb52f7 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 @@ protected Class[] getAnnotatedClasses() { } @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/src/test/java/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java b/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java index 4b468ccd1365..a3c4d5138f39 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkCompositeIdTest.java @@ -33,13 +33,12 @@ protected Class[] getAnnotatedClasses() { } @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.HQL_BULK_ID_STRATEGY, strategyClass.getName() ); } - return configuration; } protected abstract Class getMultiTableBulkIdStrategyClass(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkIdTest.java b/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkIdTest.java index eeef7b9a676b..fddbd710eeff 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/bulkid/AbstractBulkIdTest.java @@ -32,10 +32,9 @@ protected Class[] getAnnotatedClasses() { } @Override - protected Configuration constructConfiguration() { - Configuration configuration = super.constructConfiguration(); + protected void configure(Configuration configuration) { + super.configure( configuration ); configuration.setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, getMultiTableBulkIdStrategyClass().getName() ); - return configuration; } protected abstract Class getMultiTableBulkIdStrategyClass(); 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 02eeb5602d39..0de04ac8540f 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 @@ -47,15 +47,16 @@ 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 0322f9284f1b..de4241181601 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 @@ protected void releaseSessionFactory() { BootstrapServiceRegistry bootRegistry = buildBootstrapServiceRegistry(); StandardServiceRegistryImpl serviceRegistry = buildServiceRegistry( bootRegistry, - constructConfiguration() + constructAndConfigureConfiguration( bootRegistry ) ); try { TransactionUtil.doWithJDBC( @@ -92,7 +91,8 @@ protected void releaseSessionFactory() { @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/SQLServerDialectTest.java b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTest.java index c1ba41ed72f4..98643877b7da 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/dialect/functional/SQLServerDialectTest.java @@ -44,10 +44,9 @@ 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/multitenancy/schema/TenantResolverConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/schema/TenantResolverConfigurationTest.java index e8423e6a4ad6..18aacf542814 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-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index ebd52c8ee21f..d3dee0e2c494 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 @@ -112,11 +112,11 @@ protected void buildSessionFactory() { 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 ); @@ -145,14 +145,8 @@ protected void rebuildSessionFactory(Consumer configurationAdapte 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; } @@ -163,8 +157,8 @@ private void afterConstructAndConfigureConfiguration(Configuration cfg) { 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() ) { From 18f23ee7015096a5a1d03288f4bcab2873c6f727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 31 Mar 2021 14:37:03 +0200 Subject: [PATCH 06/21] HHH-14529 Fix invalid namespace URIs in MappingXsdSupport Probably copy/pasted from ConfigXsdSupport, and we forgot to add the "/orm" suffix. --- .../main/java/org/hibernate/boot/xsd/MappingXsdSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 33a371927088..0ba5ee2ab8b2 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( From 252fb65f95f8984eea6b44dc268cf57e00a68eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 24 Mar 2021 15:28:09 +0100 Subject: [PATCH 07/21] HHH-14529 Remove an unused xjb file --- hibernate-core/src/main/xjb/orm-bindings.xjb | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 hibernate-core/src/main/xjb/orm-bindings.xjb 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 940eb3a86a24..000000000000 --- a/hibernate-core/src/main/xjb/orm-bindings.xjb +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file From 2907c95cbd92f382a83252723b944cfdbcdf25b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 24 Mar 2021 15:40:12 +0100 Subject: [PATCH 08/21] HHH-14529 Introduce JAXB bindings for JPA's orm.xml Adapted from Steve's work on the unified JPA/ORM XML mapping. See: https://github.com/sebersole/hibernate-orm/commit/4ff3795e60ef33bc893d4174b6c998027e64be10#diff-b407928c3aa7ee1f231e0119ff70345caa5f6a83ed6348128c5159afbe3c6df2 https://github.com/sebersole/hibernate-orm/compare/jandex-binding Co-authored-by: Steve Ebersole --- hibernate-core/hibernate-core.gradle | 5 + .../internal/AccessTypeMarshalling.java | 24 + .../DiscriminatorTypeMarshalling.java | 24 + .../mapping/internal/EnumTypeMarshalling.java | 24 + .../internal/FetchTypeMarshalling.java | 24 + .../internal/LockModeTypeMarshalling.java | 24 + .../internal/ParameterModeMarshalling.java | 24 + .../internal/TemporalTypeMarshalling.java | 24 + .../boot/jaxb/mapping/package-info.java | 11 + .../jaxb/mapping/spi/AttributesContainer.java | 35 + .../jaxb/mapping/spi/CollectionAttribute.java | 57 + .../jaxb/mapping/spi/FetchableAttribute.java | 22 + .../jaxb/mapping/spi/LifecycleCallback.java | 17 + .../boot/jaxb/mapping/spi/ManagedType.java | 28 + .../jaxb/mapping/spi/PersistentAttribute.java | 23 + .../boot/jaxb/mapping/spi/SchemaAware.java | 23 + .../hibernate/mapping/PersistentClass.java | 31 + .../org/hibernate/mapping/SimpleValue.java | 21 + .../hibernate/xsd/mapping/mapping-2.1.0.xsd | 2543 +++++++++++++++++ .../src/main/xjb/mapping-bindings.xjb | 164 ++ 20 files changed, 3148 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/AccessTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/DiscriminatorTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/EnumTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/FetchTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/LockModeTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ParameterModeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/TemporalTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/package-info.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AttributesContainer.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/CollectionAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/FetchableAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallback.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/PersistentAttribute.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/SchemaAware.java create mode 100644 hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-2.1.0.xsd create mode 100644 hibernate-core/src/main/xjb/mapping-bindings.xjb diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index f4ae9c527003..8008fa1c4fd2 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -200,6 +200,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/jaxb/mapping/internal/AccessTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/AccessTypeMarshalling.java new file mode 100644 index 000000000000..6d413c8024ed --- /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/DiscriminatorTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/DiscriminatorTypeMarshalling.java new file mode 100644 index 000000000000..06ece7dff167 --- /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 000000000000..9164ad6e6f4b --- /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 000000000000..537c76377dce --- /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/LockModeTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/LockModeTypeMarshalling.java new file mode 100644 index 000000000000..fb975cc7568a --- /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 000000000000..a48193340005 --- /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 000000000000..09ace499db99 --- /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 000000000000..0d61b3224e46 --- /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/AttributesContainer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AttributesContainer.java new file mode 100644 index 000000000000..18b20d6592ab --- /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 000000000000..dc81a6f2245c --- /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/FetchableAttribute.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/FetchableAttribute.java new file mode 100644 index 000000000000..edce1ae6555a --- /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 000000000000..223207dd81fb --- /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/ManagedType.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java new file mode 100644 index 000000000000..ab70cbf6d3db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/ManagedType.java @@ -0,0 +1,28 @@ +/* + * 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 getClazz(); + + void setClazz(String className); + + Boolean isMetadataComplete(); + + void setMetadataComplete(Boolean isMetadataComplete); + + AccessType getAccess(); +} 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 000000000000..f1ee13b1edfd --- /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 000000000000..3f726a4b5159 --- /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/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index 6ab1e7de76b3..7703755e3003 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -17,6 +17,7 @@ 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; @@ -744,6 +745,16 @@ public Iterator getUnjoinedPropertyIterator() { 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; @@ -762,6 +773,16 @@ public ExecuteUpdateResultCheckStyle getCustomSQLInsertCheckStyle() { 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; @@ -780,6 +801,16 @@ public ExecuteUpdateResultCheckStyle getCustomSQLUpdateCheckStyle() { 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 b1c9790f24b4..da3afbe33ea5 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,7 @@ 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 javax.persistence.AttributeConverter; @@ -399,6 +400,18 @@ public void setIdentifierGeneratorProperties(Properties identifierGeneratorPrope 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 @@ -679,6 +692,14 @@ public boolean isTypeSpecified() { 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/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 000000000000..d1b364771bbe --- /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 000000000000..8ee8f838c3fd --- /dev/null +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.ManagedType + + + org.hibernate.boot.jaxb.mapping.spi.ManagedType + + + + 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.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.CollectionAttribute + + + org.hibernate.boot.jaxb.mapping.spi.PersistentAttribute + org.hibernate.boot.jaxb.mapping.spi.FetchableAttribute + + + 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.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 From f92275f6c230d89c7bcc0c91a675b5b228a24b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Wed, 24 Mar 2021 17:38:30 +0100 Subject: [PATCH 09/21] HHH-14529 Clarify that most fields are final in JPAOverriddenAnnotationReader --- .../JPAOverriddenAnnotationReader.java | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) 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 b40d2e531c7c..b9adae8b41f2 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 @@ -238,12 +238,12 @@ private enum PropertyType { 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; @@ -263,6 +263,8 @@ public JPAOverriddenAnnotationReader( if ( el instanceof Class ) { Class clazz = (Class) el; className = clazz.getName(); + propertyName = null; + propertyType = null; } else if ( el instanceof Field ) { Field field = (Field) el; @@ -282,18 +284,18 @@ else if ( el instanceof Field ) { 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 { @@ -304,12 +306,14 @@ else if ( propertyName.startsWith( "is" ) ) { } } else { + propertyName = methodName; propertyType = PropertyType.METHOD; } } else { className = null; propertyName = null; + propertyType = null; } } From 55ef4d47f2aa595734fca8632477ff104b002b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 08:26:44 +0100 Subject: [PATCH 10/21] HHH-14529 Remove commented-out code related to mapping parsing using dom4j --- ...AnnotationMetadataSourceProcessorImpl.java | 23 ------------------- 1 file changed, 23 deletions(-) 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 e7b4e609463c..9fe1d5ed8342 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 @@ -83,13 +83,6 @@ public AnnotationMetadataSourceProcessorImpl( 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; } @@ -138,22 +131,6 @@ private XClass toXClass(String className, ReflectionManager reflectionManager, C 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 From 72910366801831e7ccaea216318a79dd5b3bf204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 08:59:56 +0100 Subject: [PATCH 11/21] HHH-14529 Copy orm.xml handling code to prepare for the new JAXB-based implementation We will adapt the code in a later commit; I'm only doing the copy in this commit in order to keep the review manageable. --- .../JPAXMLOverriddenAnnotationReader.java | 3084 +++++++++++++++++ .../JPAXMLOverriddenMetadataProvider.java | 230 ++ .../reflection/internal/XMLContext.java | 359 ++ .../ElementCollectionConverterTest.java | 10 +- .../JPAXMLOverriddenAnnotationReaderTest.java | 432 +++ .../LegacyElementCollectionConverterTest.java | 71 + ...acyJPAOverriddenAnnotationReaderTest.java} | 88 +- .../reflection/LegacyXMLContextTest.java | 73 + .../reflection/XMLContextTest.java | 10 +- .../ejb3/Ejb3XmlElementCollectionTest.java | 2 + .../xml/ejb3/Ejb3XmlManyToManyTest.java | 2 + .../xml/ejb3/Ejb3XmlManyToOneTest.java | 2 + .../xml/ejb3/Ejb3XmlOneToManyTest.java | 2 + .../xml/ejb3/Ejb3XmlOneToOneTest.java | 2 + .../annotations/xml/ejb3/Ejb3XmlTest.java | 10 + .../annotations/xml/ejb3/Ejb3XmlTestCase.java | 12 +- .../LegacyEjb3XmlElementCollectionTest.java | 716 ++++ .../xml/ejb3/LegacyEjb3XmlManyToManyTest.java | 508 +++ .../xml/ejb3/LegacyEjb3XmlManyToOneTest.java | 245 ++ .../xml/ejb3/LegacyEjb3XmlOneToManyTest.java | 561 +++ .../xml/ejb3/LegacyEjb3XmlOneToOneTest.java | 309 ++ .../xml/ejb3/LegacyEjb3XmlTest.java | 148 + .../xml/ejb3/LegacyEjb3XmlTestCase.java | 82 + .../ejb3/LegacyNonExistentOrmVersionTest.java | 42 + .../ejb3/LegacyOrmVersion1SupportedTest.java | 68 + .../xml/ejb3/LegacyPreParsedOrmXmlTest.java | 58 + .../xml/ejb3/NonExistentOrmVersionTest.java | 6 +- .../xml/ejb3/OrmVersion1SupportedTest.java | 9 +- .../xml/ejb3/PreParsedOrmXmlTest.java | 9 +- 29 files changed, 7125 insertions(+), 25 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyElementCollectionConverterTest.java rename hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/{JPAOverriddenAnnotationReaderTest.java => LegacyJPAOverriddenAnnotationReaderTest.java} (88%) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/LegacyXMLContextTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlElementCollectionTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToManyTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlManyToOneTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToManyTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlOneToOneTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyEjb3XmlTestCase.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyNonExistentOrmVersionTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyOrmVersion1SupportedTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/LegacyPreParsedOrmXmlTest.java 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 000000000000..4e2a630952ee --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java @@ -0,0 +1,3084 @@ +/* + * 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.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +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.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 org.dom4j.Attribute; +import org.dom4j.Element; + +/** + * 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 + */ +@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 List elementsForProperty; + private AccessibleObject mirroredAttribute; + + /** + * @deprecated Use {@link #JPAXMLOverriddenAnnotationReader(AnnotatedElement, XMLContext, BootstrapContext)} instead. + */ + public 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; + } + } + + 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 + Element tree = xmlContext.getXMLTree( 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( tree, defaults ) ); + addIfNotNull( annotationList, getMappedSuperclass( tree, defaults ) ); + addIfNotNull( annotationList, getEmbeddable( tree, defaults ) ); + addIfNotNull( annotationList, getTable( tree, defaults ) ); + addIfNotNull( annotationList, getSecondaryTables( tree, defaults ) ); + addIfNotNull( annotationList, getPrimaryKeyJoinColumns( tree, defaults, true ) ); + addIfNotNull( annotationList, getIdClass( tree, defaults ) ); + addIfNotNull( annotationList, getCacheable( tree, defaults ) ); + addIfNotNull( annotationList, getInheritance( tree, defaults ) ); + addIfNotNull( annotationList, getDiscriminatorValue( tree, defaults ) ); + addIfNotNull( annotationList, getDiscriminatorColumn( tree, defaults ) ); + addIfNotNull( annotationList, getSequenceGenerator( tree, defaults ) ); + addIfNotNull( annotationList, getTableGenerator( tree, defaults ) ); + addIfNotNull( annotationList, getNamedQueries( tree, defaults ) ); + addIfNotNull( annotationList, getNamedNativeQueries( tree, defaults ) ); + addIfNotNull( annotationList, getNamedStoredProcedureQueries( tree, defaults ) ); + addIfNotNull( annotationList, getNamedEntityGraphs( tree, defaults ) ); + addIfNotNull( annotationList, getSqlResultSetMappings( tree, defaults ) ); + addIfNotNull( annotationList, getExcludeDefaultListeners( tree, defaults ) ); + addIfNotNull( annotationList, getExcludeSuperclassListeners( tree, defaults ) ); + addIfNotNull( annotationList, getAccessType( tree, defaults ) ); + addIfNotNull( annotationList, getAttributeOverrides( tree, defaults, true ) ); + addIfNotNull( annotationList, getAssociationOverrides( tree, defaults, true ) ); + addIfNotNull( annotationList, getEntityListeners( tree, defaults ) ); + addIfNotNull( annotationList, getConverts( tree, defaults ) ); + + this.annotations = annotationList.toArray( new Annotation[annotationList.size()] ); + for ( Annotation ann : this.annotations ) { + annotationsMap.put( ann.annotationType(), ann ); + } + checkForOrphanProperties( tree ); + } + else if ( className != null ) { //&& propertyName != null ) { //always true but less confusing + Element tree = xmlContext.getXMLTree( 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( tree ); + 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 ); + getAssociation( ManyToOne.class, annotationList, defaults ); + getAssociation( OneToOne.class, annotationList, defaults ); + getAssociation( OneToMany.class, annotationList, defaults ); + getAssociation( ManyToMany.class, annotationList, defaults ); + getAssociation( Any.class, annotationList, defaults ); + getAssociation( ManyToAny.class, 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(List 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 ( Element element : elementsForProperty ) { + final boolean isBasic = "basic".equals( element.getName() ); + final boolean isEmbedded = "embedded".equals( element.getName() ); + final boolean isElementCollection = "element-collection".equals(element.getName()); + + final boolean canHaveConverts = isBasic || isEmbedded || isElementCollection; + + if ( !canHaveConverts ) { + continue; + } + + final String attributeNamePrefix = isBasic ? null : propertyName; + applyXmlDefinedConverts( element, defaults, attributeNamePrefix, 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(Element tree, 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 ( tree != null ) { + applyXmlDefinedConverts( tree, 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( + Element containingElement, + XMLContext.Default defaults, + String attributeNamePrefix, + Map convertAnnotationsMap) { + final List convertElements = containingElement.elements( "convert" ); + for ( Element convertElement : convertElements ) { + final AnnotationDescriptor convertAnnotationDescriptor = new AnnotationDescriptor( Convert.class ); + copyStringAttribute( convertAnnotationDescriptor, convertElement, "attribute-name", false ); + copyBooleanAttribute( convertAnnotationDescriptor, convertElement, "disable-conversion" ); + + final Attribute converterClassAttr = convertElement.attribute( "converter" ); + if ( converterClassAttr != null ) { + final String converterClassName = XMLContext.buildSafeClassName( + converterClassAttr.getValue(), + 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(Element tree) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( className ); + } + catch ( ClassLoadingException e ) { + return; //a primitive type most likely + } + Element element = tree != null ? tree.element( "attributes" ) : null; + //put entity.attributes elements + if ( element != 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() ) ) ); + } + } + for ( Element subelement : (List) element.elements() ) { + String propertyName = subelement.attributeValue( "name" ); + 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(List elementsForProperty, XMLContext.Default defaults) { + for ( Element element : elementsForProperty ) { + Element subelement = element != null ? element.element( annotationToXml.get( TableGenerator.class ) ) : null; + if ( subelement != null ) { + return buildTableGeneratorAnnotation( subelement, defaults ); + } + } + if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( TableGenerator.class ); + } + else { + return null; + } + } + + private Annotation getSequenceGenerator(List elementsForProperty, XMLContext.Default defaults) { + for ( Element element : elementsForProperty ) { + Element subelement = element != null ? element.element( annotationToXml.get( SequenceGenerator.class ) ) : null; + if ( subelement != null ) { + return buildSequenceGeneratorAnnotation( subelement ); + } + } + if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( SequenceGenerator.class ); + } + else { + return null; + } + } + + private void processEventAnnotations(List annotationList, XMLContext.Default defaults) { + boolean eventElement = false; + for ( Element element : elementsForProperty ) { + String elementName = element.getName(); + if ( "pre-persist".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PrePersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "pre-remove".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "pre-update".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PreUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-persist".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostPersist.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-remove".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostRemove.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-update".equals( elementName ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( PostUpdate.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + eventElement = true; + } + else if ( "post-load".equals( elementName ) ) { + 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(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( "entity-listeners" ) : null; + if ( element != null ) { + List entityListenerClasses = new ArrayList<>(); + for ( Element subelement : (List) element.elements( "entity-listener" ) ) { + String className = subelement.attributeValue( "class" ); + try { + entityListenerClasses.add( + classLoaderAccess.classForName( + XMLContext.buildSafeClassName( className, defaults ) + ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( + "Unable to find " + element.getPath() + ".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, Element tree, XMLContext.Default defaults) { + addIfNotNull( annotationList, buildJoinTable( tree, defaults ) ); + } + + /* + * no partial overriding possible + */ + private JoinTable buildJoinTable(Element tree, XMLContext.Default defaults) { + Element subelement = tree == null ? null : tree.element( "join-table" ); + final Class annotationType = JoinTable.class; + if ( subelement == null ) { + return null; + } + //ignore java annotation, an element is defined + AnnotationDescriptor annotation = new AnnotationDescriptor( annotationType ); + copyStringAttribute( annotation, subelement, "name", false ); + copyStringAttribute( annotation, subelement, "catalog", false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyStringAttribute( annotation, subelement, "schema", false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, subelement ); + buildIndex( annotation, subelement ); + annotation.setValue( "joinColumns", getJoinColumns( subelement, false ) ); + annotation.setValue( "inverseJoinColumns", getJoinColumns( subelement, true ) ); + return AnnotationFactory.create( annotation ); + } + + /** + * 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 getAssociation( + Class annotationType, List annotationList, XMLContext.Default defaults + ) { + String xmlName = annotationToXml.get( annotationType ); + for ( Element element : elementsForProperty ) { + if ( xmlName.equals( element.getName() ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); + addTargetClass( element, ad, "target-entity", defaults ); + getFetchType( ad, element ); + getCascades( ad, element, defaults ); + getJoinTable( annotationList, element, defaults ); + buildJoinColumns( annotationList, element ); + Annotation annotation = getPrimaryKeyJoinColumns( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + copyBooleanAttribute( ad, element, "optional" ); + copyBooleanAttribute( ad, element, "orphan-removal" ); + copyStringAttribute( ad, element, "mapped-by", false ); + getOrderBy( annotationList, element ); + getMapKey( annotationList, element ); + getMapKeyClass( annotationList, element, defaults ); + getMapKeyColumn( annotationList, element ); + getOrderColumn( annotationList, element ); + getMapKeyTemporal( annotationList, element ); + getMapKeyEnumerated( annotationList, element ); + annotation = getMapKeyAttributeOverrides( element, defaults ); + addIfNotNull( annotationList, annotation ); + buildMapKeyJoinColumns( annotationList, element ); + getAssociationId( annotationList, element ); + getMapsId( annotationList, element ); + annotationList.add( AnnotationFactory.create( ad ) ); + getAccessType( annotationList, element ); + } + } + if ( elementsForProperty.size() == 0 && 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 buildMapKeyJoinColumns(List annotationList, Element element) { + MapKeyJoinColumn[] joinColumns = getMapKeyJoinColumns( element ); + if ( joinColumns.length > 0 ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyJoinColumns.class ); + ad.setValue( "value", joinColumns ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private MapKeyJoinColumn[] getMapKeyJoinColumns(Element element) { + List subelements = element != null ? element.elements( "map-key-join-column" ) : null; + List joinColumns = new ArrayList<>(); + if ( subelements != null ) { + for ( Element subelement : subelements ) { + AnnotationDescriptor column = new AnnotationDescriptor( MapKeyJoinColumn.class ); + copyStringAttribute( column, subelement, "name", false ); + copyStringAttribute( column, subelement, "referenced-column-name", false ); + copyBooleanAttribute( column, subelement, "unique" ); + copyBooleanAttribute( column, subelement, "nullable" ); + copyBooleanAttribute( column, subelement, "insertable" ); + copyBooleanAttribute( column, subelement, "updatable" ); + copyStringAttribute( column, subelement, "column-definition", false ); + copyStringAttribute( column, subelement, "table", false ); + joinColumns.add( AnnotationFactory.create( column ) ); + } + } + return joinColumns.toArray( new MapKeyJoinColumn[joinColumns.size()] ); + } + + private AttributeOverrides getMapKeyAttributeOverrides(Element tree, XMLContext.Default defaults) { + List attributes = buildAttributeOverrides( tree, "map-key-attribute-override" ); + return mergeAttributeOverrides( defaults, attributes, false ); + } + + private Cacheable getCacheable(Element element, XMLContext.Default defaults){ + if ( element != null ) { + String attValue = element.attributeValue( "cacheable" ); + if ( attValue != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Cacheable.class ); + ad.setValue( "value", Boolean.valueOf( 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, Element element) { + Element subelement = element != null ? element.element( "map-key-enumerated" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyEnumerated.class ); + EnumType value = EnumType.valueOf( subelement.getTextTrim() ); + ad.setValue( "value", value ); + 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, Element element) { + Element subelement = element != null ? element.element( "map-key-temporal" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyTemporal.class ); + TemporalType value = TemporalType.valueOf( subelement.getTextTrim() ); + ad.setValue( "value", value ); + 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, Element element) { + Element subelement = element != null ? element.element( "order-column" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( OrderColumn.class ); + copyStringAttribute( ad, subelement, "name", false ); + copyBooleanAttribute( ad, subelement, "nullable" ); + copyBooleanAttribute( ad, subelement, "insertable" ); + copyBooleanAttribute( ad, subelement, "updatable" ); + copyStringAttribute( ad, subelement, "column-definition", 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, Element element) { + String attrVal = element.attributeValue( "maps-id" ); + if ( attrVal != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapsId.class ); + ad.setValue( "value", attrVal ); + 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, Element element) { + String attrVal = element.attributeValue( "id" ); + if ( "true".equals( attrVal ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Id.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void addTargetClass(Element element, AnnotationDescriptor ad, String nodeName, XMLContext.Default defaults) { + String className = element.attributeValue( nodeName ); + if ( className != null ) { + Class clazz; + try { + clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( className, defaults ) ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( + "Unable to find " + element.getPath() + " " + 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 ( Element element : elementsForProperty ) { + if ( "element-collection".equals( element.getName() ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( ElementCollection.class ); + addTargetClass( element, ad, "target-class", defaults ); + getFetchType( ad, element ); + getOrderBy( annotationList, element ); + getOrderColumn( annotationList, element ); + getMapKey( annotationList, element ); + getMapKeyClass( annotationList, element, defaults ); + getMapKeyTemporal( annotationList, element ); + getMapKeyEnumerated( annotationList, element ); + getMapKeyColumn( annotationList, element ); + buildMapKeyJoinColumns( annotationList, element ); + Annotation annotation = getColumn( element.element( "column" ), false, element ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element ); + getEnumerated( annotationList, element ); + getLob( annotationList, element ); + //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, "map-key-attribute-override" ) ); + attributes.addAll( buildAttributeOverrides( element, "attribute-override" ) ); + annotation = mergeAttributeOverrides( defaults, attributes, false ); + addIfNotNull( annotationList, annotation ); + annotation = getAssociationOverrides( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + getCollectionTable( annotationList, element, defaults ); + annotationList.add( AnnotationFactory.create( ad ) ); + getAccessType( annotationList, element ); + } + } + } + + private void getOrderBy(List annotationList, Element element) { + Element subelement = element != null ? element.element( "order-by" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( OrderBy.class ); + copyStringElement( subelement, ad, "value" ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKey(List annotationList, Element element) { + Element subelement = element != null ? element.element( "map-key" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKey.class ); + copyStringAttribute( ad, subelement, "name", false ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKeyColumn(List annotationList, Element element) { + Element subelement = element != null ? element.element( "map-key-column" ) : null; + if ( subelement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyColumn.class ); + copyStringAttribute( ad, subelement, "name", false ); + copyBooleanAttribute( ad, subelement, "unique" ); + copyBooleanAttribute( ad, subelement, "nullable" ); + copyBooleanAttribute( ad, subelement, "insertable" ); + copyBooleanAttribute( ad, subelement, "updatable" ); + copyStringAttribute( ad, subelement, "column-definition", false ); + copyStringAttribute( ad, subelement, "table", false ); + copyIntegerAttribute( ad, subelement, "length" ); + copyIntegerAttribute( ad, subelement, "precision" ); + copyIntegerAttribute( ad, subelement, "scale" ); + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getMapKeyClass(List annotationList, Element element, XMLContext.Default defaults) { + String nodeName = "map-key-class"; + Element subelement = element != null ? element.element( nodeName ) : null; + if ( subelement != null ) { + String mapKeyClassName = subelement.attributeValue( "class" ); + 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 " + element.getPath() + " " + nodeName + ": " + mapKeyClassName, e + ); + } + ad.setValue( "value", clazz ); + } + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getCollectionTable(List annotationList, Element element, XMLContext.Default defaults) { + Element subelement = element != null ? element.element( "collection-table" ) : null; + if ( subelement != null ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( CollectionTable.class ); + copyStringAttribute( annotation, subelement, "name", false ); + copyStringAttribute( annotation, subelement, "catalog", false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyStringAttribute( annotation, subelement, "schema", false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + JoinColumn[] joinColumns = getJoinColumns( subelement, false ); + if ( joinColumns.length > 0 ) { + annotation.setValue( "joinColumns", joinColumns ); + } + buildUniqueConstraints( annotation, subelement ); + buildIndex( annotation, subelement ); + annotationList.add( AnnotationFactory.create( annotation ) ); + } + } + + private void buildJoinColumns(List annotationList, Element element) { + JoinColumn[] joinColumns = getJoinColumns( element, 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, Element element, XMLContext.Default defaults) { + List elements = element != null ? element.elements( "cascade" ) : new ArrayList<>( 0 ); + List cascades = new ArrayList<>(); + for ( Element subelement : elements ) { + if ( subelement.element( "cascade-all" ) != null ) { + cascades.add( CascadeType.ALL ); + } + if ( subelement.element( "cascade-persist" ) != null ) { + cascades.add( CascadeType.PERSIST ); + } + if ( subelement.element( "cascade-merge" ) != null ) { + cascades.add( CascadeType.MERGE ); + } + if ( subelement.element( "cascade-remove" ) != null ) { + cascades.add( CascadeType.REMOVE ); + } + if ( subelement.element( "cascade-refresh" ) != null ) { + cascades.add( CascadeType.REFRESH ); + } + if ( subelement.element( "cascade-detach" ) != 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 ( Element element : elementsForProperty ) { + if ( "embedded".equals( element.getName() ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Embedded.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + Annotation annotation = getAttributeOverrides( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + annotation = getAssociationOverrides( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + getAccessType( annotationList, element ); + } + } + if ( elementsForProperty.size() == 0 && 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) { + for ( Element element : elementsForProperty ) { + if ( "transient".equals( element.getName() ) ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Transient.class ); + return AnnotationFactory.create( ad ); + } + } + if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Transient.class ); + } + else { + return null; + } + } + + private void getVersion(List annotationList, XMLContext.Default defaults) { + for ( Element element : elementsForProperty ) { + if ( "version".equals( element.getName() ) ) { + Annotation annotation = buildColumns( element ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element ); + AnnotationDescriptor basic = new AnnotationDescriptor( Version.class ); + annotationList.add( AnnotationFactory.create( basic ) ); + getAccessType( annotationList, element ); + } + } + if ( elementsForProperty.size() == 0 && 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 ( Element element : elementsForProperty ) { + if ( "basic".equals( element.getName() ) ) { + Annotation annotation = buildColumns( element ); + addIfNotNull( annotationList, annotation ); + getAccessType( annotationList, element ); + getTemporal( annotationList, element ); + getLob( annotationList, element ); + getEnumerated( annotationList, element ); + AnnotationDescriptor basic = new AnnotationDescriptor( Basic.class ); + getFetchType( basic, element ); + copyBooleanAttribute( basic, element, "optional" ); + annotationList.add( AnnotationFactory.create( basic ) ); + } + } + if ( elementsForProperty.size() == 0 && 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, Element element) { + Element subElement = element != null ? element.element( "enumerated" ) : null; + if ( subElement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Enumerated.class ); + String enumerated = subElement.getTextTrim(); + if ( "ORDINAL".equalsIgnoreCase( enumerated ) ) { + ad.setValue( "value", EnumType.ORDINAL ); + } + else if ( "STRING".equalsIgnoreCase( enumerated ) ) { + ad.setValue( "value", EnumType.STRING ); + } + else if ( StringHelper.isNotEmpty( enumerated ) ) { + throw new AnnotationException( "Unknown EnumType: " + enumerated + ". " + SCHEMA_VALIDATION ); + } + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getLob(List annotationList, Element element) { + Element subElement = element != null ? element.element( "lob" ) : null; + if ( subElement != null ) { + annotationList.add( AnnotationFactory.create( new AnnotationDescriptor( Lob.class ) ) ); + } + } + + private void getFetchType(AnnotationDescriptor descriptor, Element element) { + String fetchString = element != null ? element.attributeValue( "fetch" ) : null; + if ( fetchString != null ) { + if ( "eager".equalsIgnoreCase( fetchString ) ) { + descriptor.setValue( "fetch", FetchType.EAGER ); + } + else if ( "lazy".equalsIgnoreCase( fetchString ) ) { + descriptor.setValue( "fetch", FetchType.LAZY ); + } + } + } + + private void getEmbeddedId(List annotationList, XMLContext.Default defaults) { + for ( Element element : elementsForProperty ) { + if ( "embedded-id".equals( element.getName() ) ) { + if ( isProcessingId( defaults ) ) { + Annotation annotation = getAttributeOverrides( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + annotation = getAssociationOverrides( element, defaults, false ); + addIfNotNull( annotationList, annotation ); + AnnotationDescriptor ad = new AnnotationDescriptor( EmbeddedId.class ); + annotationList.add( AnnotationFactory.create( ad ) ); + getAccessType( annotationList, element ); + } + } + } + if ( elementsForProperty.size() == 0 && 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(Element tree) { + elementsForProperty = new ArrayList<>(); + Element element = tree != null ? tree.element( "attributes" ) : null; + //put entity.attributes elements + if ( element != null ) { + for ( Element subelement : (List) element.elements() ) { + if ( propertyName.equals( subelement.attributeValue( "name" ) ) ) { + elementsForProperty.add( subelement ); + } + } + } + //add pre-* etc from entity and pure entity listener classes + if ( tree != null ) { + for ( Element subelement : (List) tree.elements() ) { + if ( propertyName.equals( subelement.attributeValue( "method-name" ) ) ) { + elementsForProperty.add( subelement ); + } + } + } + } + + private void getId(List annotationList, XMLContext.Default defaults) { + for ( Element element : elementsForProperty ) { + if ( "id".equals( element.getName() ) ) { + boolean processId = isProcessingId( defaults ); + if ( processId ) { + Annotation annotation = buildColumns( element ); + addIfNotNull( annotationList, annotation ); + annotation = buildGeneratedValue( element ); + addIfNotNull( annotationList, annotation ); + getTemporal( annotationList, element ); + //FIXME: fix the priority of xml over java for generator names + annotation = getTableGenerator( element, defaults ); + addIfNotNull( annotationList, annotation ); + annotation = getSequenceGenerator( element, defaults ); + addIfNotNull( annotationList, annotation ); + AnnotationDescriptor id = new AnnotationDescriptor( Id.class ); + annotationList.add( AnnotationFactory.create( id ) ); + getAccessType( annotationList, element ); + } + } + } + if ( elementsForProperty.size() == 0 && 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(Element element) { + List subelements = element.elements( "column" ); + List columns = new ArrayList<>( subelements.size() ); + for ( Element subelement : subelements ) { + columns.add( getColumn( subelement, false, element ) ); + } + 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(Element element) { + Element subElement = element != null ? element.element( "generated-value" ) : null; + if ( subElement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( GeneratedValue.class ); + String strategy = subElement.attributeValue( "strategy" ); + if ( "TABLE".equalsIgnoreCase( strategy ) ) { + ad.setValue( "strategy", GenerationType.TABLE ); + } + else if ( "SEQUENCE".equalsIgnoreCase( strategy ) ) { + ad.setValue( "strategy", GenerationType.SEQUENCE ); + } + else if ( "IDENTITY".equalsIgnoreCase( strategy ) ) { + ad.setValue( "strategy", GenerationType.IDENTITY ); + } + else if ( "AUTO".equalsIgnoreCase( strategy ) ) { + ad.setValue( "strategy", GenerationType.AUTO ); + } + else if ( StringHelper.isNotEmpty( strategy ) ) { + throw new AnnotationException( "Unknown GenerationType: " + strategy + ". " + SCHEMA_VALIDATION ); + } + copyStringAttribute( ad, subElement, "generator", false ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private void getTemporal(List annotationList, Element element) { + Element subElement = element != null ? element.element( "temporal" ) : null; + if ( subElement != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Temporal.class ); + String temporal = subElement.getTextTrim(); + if ( "DATE".equalsIgnoreCase( temporal ) ) { + ad.setValue( "value", TemporalType.DATE ); + } + else if ( "TIME".equalsIgnoreCase( temporal ) ) { + ad.setValue( "value", TemporalType.TIME ); + } + else if ( "TIMESTAMP".equalsIgnoreCase( temporal ) ) { + ad.setValue( "value", TemporalType.TIMESTAMP ); + } + else if ( StringHelper.isNotEmpty( temporal ) ) { + throw new AnnotationException( "Unknown TemporalType: " + temporal + ". " + SCHEMA_VALIDATION ); + } + annotationList.add( AnnotationFactory.create( ad ) ); + } + } + + private void getAccessType(List annotationList, Element element) { + if ( element == null ) { + return; + } + String access = element.attributeValue( "access" ); + if ( access != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); + AccessType type; + try { + type = AccessType.valueOf( access ); + } + catch ( IllegalArgumentException e ) { + throw new AnnotationException( access + " is not a valid access type. Check you xml confguration." ); + } + + 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 ) ); + } + } + + /** + * @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 + * with annotations is never allowed. + */ + private AssociationOverrides getAssociationOverrides(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) { + List attributes = buildAssociationOverrides( tree, 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(Element element, XMLContext.Default defaults) { + List subelements = element == null ? null : element.elements( "association-override" ); + List overrides = new ArrayList<>(); + if ( subelements != null && subelements.size() > 0 ) { + for ( Element current : subelements ) { + AnnotationDescriptor override = new AnnotationDescriptor( AssociationOverride.class ); + copyStringAttribute( override, current, "name", true ); + override.setValue( "joinColumns", getJoinColumns( current, false ) ); + JoinTable joinTable = buildJoinTable( current, defaults ); + if ( joinTable != null ) { + override.setValue( "joinTable", joinTable ); + } + overrides.add( AnnotationFactory.create( override ) ); + } + } + return overrides; + } + + private JoinColumn[] getJoinColumns(Element element, boolean isInverse) { + List subelements = element != null ? + element.elements( isInverse ? "inverse-join-column" : "join-column" ) : + null; + List joinColumns = new ArrayList<>(); + if ( subelements != null ) { + for ( Element subelement : subelements ) { + AnnotationDescriptor column = new AnnotationDescriptor( JoinColumn.class ); + copyStringAttribute( column, subelement, "name", false ); + copyStringAttribute( column, subelement, "referenced-column-name", false ); + copyBooleanAttribute( column, subelement, "unique" ); + copyBooleanAttribute( column, subelement, "nullable" ); + copyBooleanAttribute( column, subelement, "insertable" ); + copyBooleanAttribute( column, subelement, "updatable" ); + copyStringAttribute( column, subelement, "column-definition", false ); + copyStringAttribute( column, subelement, "table", 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 ); + } + } + } + + /** + * @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(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) { + List attributes = buildAttributeOverrides( tree, "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(Element element, String nodeName) { + List subelements = element == null ? null : element.elements( nodeName ); + return buildAttributeOverrides( subelements, nodeName ); + } + + private List buildAttributeOverrides(List subelements, String nodeName) { + List overrides = new ArrayList<>(); + if ( subelements != null && subelements.size() > 0 ) { + for ( Element current : subelements ) { + if ( !current.getName().equals( nodeName ) ) { + continue; + } + AnnotationDescriptor override = new AnnotationDescriptor( AttributeOverride.class ); + copyStringAttribute( override, current, "name", true ); + Element column = current.element( "column" ); + override.setValue( "column", getColumn( column, true, current ) ); + overrides.add( AnnotationFactory.create( override ) ); + } + } + return overrides; + } + + private Column getColumn(Element element, boolean isMandatory, Element current) { + //Element subelement = element != null ? element.element( "column" ) : null; + if ( element != null ) { + AnnotationDescriptor column = new AnnotationDescriptor( Column.class ); + copyStringAttribute( column, element, "name", false ); + copyBooleanAttribute( column, element, "unique" ); + copyBooleanAttribute( column, element, "nullable" ); + copyBooleanAttribute( column, element, "insertable" ); + copyBooleanAttribute( column, element, "updatable" ); + copyStringAttribute( column, element, "column-definition", false ); + copyStringAttribute( column, element, "table", false ); + copyIntegerAttribute( column, element, "length" ); + copyIntegerAttribute( column, element, "precision" ); + copyIntegerAttribute( column, element, "scale" ); + return (Column) AnnotationFactory.create( column ); + } + else { + if ( isMandatory ) { + throw new AnnotationException( current.getPath() + ".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(Element tree, XMLContext.Default defaults) { + String access = tree == null ? null : tree.attributeValue( "access" ); + if ( access != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); + AccessType type; + try { + type = AccessType.valueOf( access ); + } + catch ( IllegalArgumentException e ) { + throw new AnnotationException( access + " is not a valid access type. Check you xml confguration." ); + } + ad.setValue( "value", type ); + 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(Element tree, XMLContext.Default defaults) { + return (ExcludeSuperclassListeners) getMarkerAnnotation( ExcludeSuperclassListeners.class, tree, defaults ); + } + + private ExcludeDefaultListeners getExcludeDefaultListeners(Element tree, XMLContext.Default defaults) { + return (ExcludeDefaultListeners) getMarkerAnnotation( ExcludeDefaultListeners.class, tree, defaults ); + } + + private Annotation getMarkerAnnotation( + Class clazz, Element element, XMLContext.Default defaults + ) { + Element subelement = element == null ? null : element.element( annotationToXml.get( clazz ) ); + if ( subelement != 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(Element tree, XMLContext.Default defaults) { + List results = buildSqlResultsetMappings( tree, defaults, classLoaderAccess ); + 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( + Element element, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + if ( element == null ) { + return new ArrayList<>(); + } + List namedEntityGraphList = new ArrayList<>(); + List namedEntityGraphElements = element.elements( "named-entity-graph" ); + for ( Element subElement : namedEntityGraphElements ) { + AnnotationDescriptor ann = new AnnotationDescriptor( NamedEntityGraph.class ); + copyStringAttribute( ann, subElement, "name", false ); + copyBooleanAttribute( ann, subElement, "include-all-attributes" ); + bindNamedAttributeNodes( subElement, ann ); + + List subgraphNodes = subElement.elements( "subgraph" ); + List subclassSubgraphNodes = subElement.elements( "subclass-subgraph" ); + if(!subclassSubgraphNodes.isEmpty()) { + subgraphNodes.addAll( subclassSubgraphNodes ); + } + bindNamedSubgraph( defaults, ann, subgraphNodes, classLoaderAccess ); + namedEntityGraphList.add( AnnotationFactory.create( ann ) ); + } + //TODO + return namedEntityGraphList; + } + + private static void bindNamedSubgraph( + XMLContext.Default defaults, + AnnotationDescriptor ann, + List subgraphNodes, + ClassLoaderAccess classLoaderAccess) { + List annSubgraphNodes = new ArrayList<>( ); + for(Element subgraphNode : subgraphNodes){ + AnnotationDescriptor annSubgraphNode = new AnnotationDescriptor( NamedSubgraph.class ); + copyStringAttribute( annSubgraphNode, subgraphNode, "name", true ); + String clazzName = subgraphNode.attributeValue( "class" ); + 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, annSubgraphNode); + annSubgraphNodes.add( AnnotationFactory.create( annSubgraphNode ) ); + } + + ann.setValue( "subgraphs", annSubgraphNodes.toArray( new NamedSubgraph[annSubgraphNodes.size()] ) ); + } + + private static void bindNamedAttributeNodes(Element subElement, AnnotationDescriptor ann) { + List namedAttributeNodes = subElement.elements("named-attribute-node"); + List annNamedAttributeNodes = new ArrayList<>( ); + for(Element namedAttributeNode : namedAttributeNodes){ + AnnotationDescriptor annNamedAttributeNode = new AnnotationDescriptor( NamedAttributeNode.class ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "value", "name", true ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "subgraph", false ); + copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "key-subgraph", false ); + annNamedAttributeNodes.add( AnnotationFactory.create( annNamedAttributeNode ) ); + } + ann.setValue( "attributeNodes", annNamedAttributeNodes.toArray( new NamedAttributeNode[annNamedAttributeNodes.size()] ) ); + } + + public static List buildNamedStoreProcedureQueries( + Element element, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + if ( element == null ) { + return new ArrayList<>(); + } + List namedStoredProcedureElements = element.elements( "named-stored-procedure-query" ); + List namedStoredProcedureQueries = new ArrayList<>(); + for ( Object obj : namedStoredProcedureElements ) { + Element subElement = (Element) obj; + AnnotationDescriptor ann = new AnnotationDescriptor( NamedStoredProcedureQuery.class ); + copyStringAttribute( ann, subElement, "name", true ); + copyStringAttribute( ann, subElement, "procedure-name", true ); + + List elements = subElement.elements( "parameter" ); + List storedProcedureParameters = new ArrayList<>(); + + for ( Element parameterElement : elements ) { + AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class ); + copyStringAttribute( parameterDescriptor, parameterElement, "name", false ); + String modeValue = parameterElement.attributeValue( "mode" ); + if ( modeValue == null ) { + parameterDescriptor.setValue( "mode", ParameterMode.IN ); + } + else { + parameterDescriptor.setValue( "mode", ParameterMode.valueOf( modeValue.toUpperCase(Locale.ROOT) ) ); + } + String clazzName = parameterElement.attributeValue( "class" ); + 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()] ) + ); + + elements = subElement.elements( "result-class" ); + List returnClasses = new ArrayList<>(); + for ( Element classElement : elements ) { + String clazzName = classElement.getTextTrim(); + 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()] ) ); + + + elements = subElement.elements( "result-set-mapping" ); + List resultSetMappings = new ArrayList<>(); + for ( Element resultSetMappingElement : elements ) { + resultSetMappings.add( resultSetMappingElement.getTextTrim() ); + } + ann.setValue( "resultSetMappings", resultSetMappings.toArray( new String[resultSetMappings.size()] ) ); + elements = subElement.elements( "hint" ); + buildQueryHints( elements, ann ); + namedStoredProcedureQueries.add( AnnotationFactory.create( ann ) ); + } + return namedStoredProcedureQueries; + + } + + public static List buildSqlResultsetMappings( + Element element, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + final List builtResultSetMappings = new ArrayList<>(); + if ( element == null ) { + return builtResultSetMappings; + } + + // iterate over each element + for ( Object resultSetMappingElementObject : element.elements( "sql-result-set-mapping" ) ) { + final Element resultSetMappingElement = (Element) resultSetMappingElementObject; + + final AnnotationDescriptor resultSetMappingAnnotation = new AnnotationDescriptor( SqlResultSetMapping.class ); + copyStringAttribute( resultSetMappingAnnotation, resultSetMappingElement, "name", true ); + + // iterate over the sub-elements, which should include: + // * + // * + // * + + List entityResultAnnotations = null; + List columnResultAnnotations = null; + List constructorResultAnnotations = null; + + for ( Object resultElementObject : resultSetMappingElement.elements() ) { + final Element resultElement = (Element) resultElementObject; + + if ( "entity-result".equals( resultElement.getName() ) ) { + if ( entityResultAnnotations == null ) { + entityResultAnnotations = new ArrayList<>(); + } + // process the + entityResultAnnotations.add( buildEntityResult( resultElement, defaults, classLoaderAccess ) ); + } + else if ( "column-result".equals( resultElement.getName() ) ) { + if ( columnResultAnnotations == null ) { + columnResultAnnotations = new ArrayList<>(); + } + columnResultAnnotations.add( buildColumnResult( resultElement, defaults, classLoaderAccess ) ); + } + else if ( "constructor-result".equals( resultElement.getName() ) ) { + if ( constructorResultAnnotations == null ) { + constructorResultAnnotations = new ArrayList<>(); + } + constructorResultAnnotations.add( buildConstructorResult( resultElement, defaults, classLoaderAccess ) ); + } + else { + // most likely the this code used to handle. I have left the code here, + // but commented it out for now. I'll just log a warning for now. + LOG.debug( "Encountered unrecognized sql-result-set-mapping sub-element : " + resultElement.getName() ); + +// String clazzName = subelement.attributeValue( "result-class" ); +// if ( StringHelper.isNotEmpty( clazzName ) ) { +// Class clazz; +// try { +// clazz = ReflectHelper.classForName( +// XMLContext.buildSafeClassName( clazzName, defaults ), +// JPAOverriddenAnnotationReader.class +// ); +// } +// catch ( ClassNotFoundException e ) { +// throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); +// } +// ann.setValue( "resultClass", clazz ); +// } + } + } + + 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()] ) + ); + } + + + // this was part of the old code too, but could never figure out what it is supposed to do... + // copyStringAttribute( ann, subelement, "result-set-mapping", false ); + + builtResultSetMappings.add( AnnotationFactory.create( resultSetMappingAnnotation ) ); + } + + return builtResultSetMappings; + } + + private static EntityResult buildEntityResult( + Element entityResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + final AnnotationDescriptor entityResultDescriptor = new AnnotationDescriptor( EntityResult.class ); + + final Class entityClass = resolveClassReference( entityResultElement.attributeValue( "entity-class" ), defaults, classLoaderAccess ); + entityResultDescriptor.setValue( "entityClass", entityClass ); + + copyStringAttribute( entityResultDescriptor, entityResultElement, "discriminator-column", false ); + + // process the sub-elements + List fieldResultAnnotations = new ArrayList<>(); + for ( Element fieldResult : (List) entityResultElement.elements( "field-result" ) ) { + AnnotationDescriptor fieldResultDescriptor = new AnnotationDescriptor( FieldResult.class ); + copyStringAttribute( fieldResultDescriptor, fieldResult, "name", true ); + copyStringAttribute( fieldResultDescriptor, fieldResult, "column", 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( + Element columnResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { +// AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor( ColumnResult.class ); +// copyStringAttribute( columnResultDescriptor, columnResultElement, "name", true ); +// return AnnotationFactory.create( columnResultDescriptor ); + + AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor( ColumnResult.class ); + copyStringAttribute( columnResultDescriptor, columnResultElement, "name", true ); + final String columnTypeName = columnResultElement.attributeValue( "class" ); + if ( StringHelper.isNotEmpty( columnTypeName ) ) { + columnResultDescriptor.setValue( "type", resolveClassReference( columnTypeName, defaults, classLoaderAccess ) ); + } + return AnnotationFactory.create( columnResultDescriptor ); + } + + private static ConstructorResult buildConstructorResult( + Element constructorResultElement, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + AnnotationDescriptor constructorResultDescriptor = new AnnotationDescriptor( ConstructorResult.class ); + + final Class entityClass = resolveClassReference( constructorResultElement.attributeValue( "target-class" ), defaults, classLoaderAccess ); + constructorResultDescriptor.setValue( "targetClass", entityClass ); + + List columnResultAnnotations = new ArrayList<>(); + for ( Element columnResultElement : (List) constructorResultElement.elements( "column" ) ) { + 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(Element tree, XMLContext.Default defaults) { + //TODO avoid the Proxy Creation (@NamedQueries) when possible + List queries = (List) buildNamedQueries( tree, false, defaults, classLoaderAccess ); + 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(Element tree, XMLContext.Default defaults) { + List queries = buildNamedEntityGraph( tree, defaults, classLoaderAccess ); + 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(Element tree, XMLContext.Default defaults) { + List queries = buildNamedStoreProcedureQueries( tree, defaults, classLoaderAccess ); + 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( + Element tree, + XMLContext.Default defaults) { + List queries = (List) buildNamedQueries( tree, true, defaults, classLoaderAccess ); + 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 ( Element hint : elements ) { + AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); + String value = hint.attributeValue( "name" ); + if ( value == null ) { + throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); + } + hintDescriptor.setValue( "name", value ); + value = hint.attributeValue( "value" ); + 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( + Element element, + boolean isNative, + XMLContext.Default defaults, + ClassLoaderAccess classLoaderAccess) { + if ( element == null ) { + return new ArrayList(); + } + List namedQueryElementList = isNative ? + element.elements( "named-native-query" ) : + element.elements( "named-query" ); + List namedQueries = new ArrayList(); + for ( Object aNamedQueryElementList : namedQueryElementList ) { + Element subelement = (Element) aNamedQueryElementList; + AnnotationDescriptor ann = new AnnotationDescriptor( + isNative ? NamedNativeQuery.class : NamedQuery.class + ); + copyStringAttribute( ann, subelement, "name", false ); + Element queryElt = subelement.element( "query" ); + if ( queryElt == null ) { + throw new AnnotationException( "No element found." + SCHEMA_VALIDATION ); + } + copyStringElement( queryElt, ann, "query" ); + List elements = subelement.elements( "hint" ); + buildQueryHints( elements, ann ); + String clazzName = subelement.attributeValue( "result-class" ); + 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 ); + } + copyStringAttribute( ann, subelement, "result-set-mapping", false ); + namedQueries.add( AnnotationFactory.create( ann ) ); + } + return namedQueries; + } + + private TableGenerator getTableGenerator(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( annotationToXml.get( TableGenerator.class ) ) : null; + 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(Element element, XMLContext.Default defaults) { + AnnotationDescriptor ad = new AnnotationDescriptor( TableGenerator.class ); + copyStringAttribute( ad, element, "name", false ); + copyStringAttribute( ad, element, "table", false ); + copyStringAttribute( ad, element, "catalog", false ); + copyStringAttribute( ad, element, "schema", false ); + copyStringAttribute( ad, element, "pk-column-name", false ); + copyStringAttribute( ad, element, "value-column-name", false ); + copyStringAttribute( ad, element, "pk-column-value", false ); + copyIntegerAttribute( ad, element, "initial-value" ); + copyIntegerAttribute( ad, element, "allocation-size" ); + buildUniqueConstraints( ad, element ); + 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(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( annotationToXml.get( SequenceGenerator.class ) ) : null; + if ( element != null ) { + return buildSequenceGeneratorAnnotation( element ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( SequenceGenerator.class ); + } + else { + return null; + } + } + + public static SequenceGenerator buildSequenceGeneratorAnnotation(Element element) { + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( SequenceGenerator.class ); + copyStringAttribute( ad, element, "name", false ); + copyStringAttribute( ad, element, "sequence-name", false ); + copyIntegerAttribute( ad, element, "initial-value" ); + copyIntegerAttribute( ad, element, "allocation-size" ); + return AnnotationFactory.create( ad ); + } + else { + return null; + } + } + + private DiscriminatorColumn getDiscriminatorColumn(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( "discriminator-column" ) : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( DiscriminatorColumn.class ); + copyStringAttribute( ad, element, "name", false ); + copyStringAttribute( ad, element, "column-definition", false ); + String value = element.attributeValue( "discriminator-type" ); + DiscriminatorType type = DiscriminatorType.STRING; + if ( value != null ) { + if ( "STRING".equals( value ) ) { + type = DiscriminatorType.STRING; + } + else if ( "CHAR".equals( value ) ) { + type = DiscriminatorType.CHAR; + } + else if ( "INTEGER".equals( value ) ) { + type = DiscriminatorType.INTEGER; + } + else { + throw new AnnotationException( + "Unknown DiscriminatorType in XML: " + value + " (" + SCHEMA_VALIDATION + ")" + ); + } + } + ad.setValue( "discriminatorType", type ); + copyIntegerAttribute( ad, element, "length" ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( DiscriminatorColumn.class ); + } + else { + return null; + } + } + + private DiscriminatorValue getDiscriminatorValue(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( "discriminator-value" ) : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( DiscriminatorValue.class ); + copyStringElement( element, ad, "value" ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( DiscriminatorValue.class ); + } + else { + return null; + } + } + + private Inheritance getInheritance(Element tree, XMLContext.Default defaults) { + Element element = tree != null ? tree.element( "inheritance" ) : null; + if ( element != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Inheritance.class ); + Attribute attr = element.attribute( "strategy" ); + InheritanceType strategy = InheritanceType.SINGLE_TABLE; + if ( attr != null ) { + String value = attr.getValue(); + if ( "SINGLE_TABLE".equals( value ) ) { + strategy = InheritanceType.SINGLE_TABLE; + } + else if ( "JOINED".equals( value ) ) { + strategy = InheritanceType.JOINED; + } + else if ( "TABLE_PER_CLASS".equals( value ) ) { + strategy = InheritanceType.TABLE_PER_CLASS; + } + else { + throw new AnnotationException( + "Unknown InheritanceType in XML: " + value + " (" + SCHEMA_VALIDATION + ")" + ); + } + } + ad.setValue( "strategy", strategy ); + return AnnotationFactory.create( ad ); + } + else if ( defaults.canUseJavaAnnotations() ) { + return getPhysicalAnnotation( Inheritance.class ); + } + else { + return null; + } + } + + private IdClass getIdClass(Element tree, XMLContext.Default defaults) { + Element element = tree == null ? null : tree.element( "id-class" ); + if ( element != null ) { + Attribute attr = element.attribute( "class" ); + if ( attr != null ) { + AnnotationDescriptor ad = new AnnotationDescriptor( IdClass.class ); + Class clazz; + try { + clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( attr.getValue(), defaults ) + ); + } + catch ( ClassLoadingException e ) { + throw new AnnotationException( "Unable to find id-class: " + attr.getValue(), 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; + } + } + + /** + * @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 PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(Element element, XMLContext.Default defaults, boolean mergeWithAnnotations) { + PrimaryKeyJoinColumn[] columns = buildPrimaryKeyJoinColumns( element ); + 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(Element tree, XMLContext.Default defaults) { + if ( tree == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Entity.class ) : null; + } + else { + if ( "entity".equals( tree.getName() ) ) { + AnnotationDescriptor entity = new AnnotationDescriptor( Entity.class ); + copyStringAttribute( entity, tree, "name", 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(Element tree, XMLContext.Default defaults) { + if ( tree == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( MappedSuperclass.class ) : null; + } + else { + if ( "mapped-superclass".equals( tree.getName() ) ) { + AnnotationDescriptor entity = new AnnotationDescriptor( MappedSuperclass.class ); + return AnnotationFactory.create( entity ); + } + else { + return null; //this is not an entity + } + } + } + + private Embeddable getEmbeddable(Element tree, XMLContext.Default defaults) { + if ( tree == null ) { + return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Embeddable.class ) : null; + } + else { + if ( "embeddable".equals( tree.getName() ) ) { + AnnotationDescriptor entity = new AnnotationDescriptor( Embeddable.class ); + return AnnotationFactory.create( entity ); + } + else { + return null; //this is not an entity + } + } + } + + private Table getTable(Element tree, XMLContext.Default defaults) { + Element subelement = tree == null ? null : tree.element( "table" ); + if ( subelement == 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 ); + copyStringAttribute( annotation, subelement, "name", false ); + copyStringAttribute( annotation, subelement, "catalog", false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyStringAttribute( annotation, subelement, "schema", false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, subelement ); + buildIndex( annotation, subelement ); + return AnnotationFactory.create( annotation ); + } + } + + private SecondaryTables getSecondaryTables(Element tree, XMLContext.Default defaults) { + List elements = tree == null ? + new ArrayList<>() : + (List) tree.elements( "secondary-table" ); + List secondaryTables = new ArrayList<>( 3 ); + for ( Element element : elements ) { + AnnotationDescriptor annotation = new AnnotationDescriptor( SecondaryTable.class ); + copyStringAttribute( annotation, element, "name", false ); + copyStringAttribute( annotation, element, "catalog", false ); + if ( StringHelper.isNotEmpty( defaults.getCatalog() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "catalog" ) ) ) { + annotation.setValue( "catalog", defaults.getCatalog() ); + } + copyStringAttribute( annotation, element, "schema", false ); + if ( StringHelper.isNotEmpty( defaults.getSchema() ) + && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { + annotation.setValue( "schema", defaults.getSchema() ); + } + buildUniqueConstraints( annotation, element ); + buildIndex( annotation, element ); + annotation.setValue( "pkJoinColumns", buildPrimaryKeyJoinColumns( element ) ); + 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, Element element){ + List indexElementList = element.elements( "index" ); + Index[] indexes = new Index[indexElementList.size()]; + for(int i=0;i columnNamesElements = subelement.elements( "column-name" ); + String[] columnNames = new String[columnNamesElements.size()]; + int columnNameIndex = 0; + Iterator it = columnNamesElements.listIterator(); + while ( it.hasNext() ) { + Element columnNameElt = (Element) it.next(); + columnNames[columnNameIndex++] = columnNameElt.getTextTrim(); + } + AnnotationDescriptor ucAnn = new AnnotationDescriptor( UniqueConstraint.class ); + copyStringAttribute( ucAnn, subelement, "name", false ); + ucAnn.setValue( "columnNames", columnNames ); + uniqueConstraints[ucIndex++] = AnnotationFactory.create( ucAnn ); + } + annotation.setValue( "uniqueConstraints", uniqueConstraints ); + } + + private PrimaryKeyJoinColumn[] buildPrimaryKeyJoinColumns(Element element) { + if ( element == null ) { + return new PrimaryKeyJoinColumn[] { }; + } + List pkJoinColumnElementList = element.elements( "primary-key-join-column" ); + PrimaryKeyJoinColumn[] pkJoinColumns = new PrimaryKeyJoinColumn[pkJoinColumnElementList.size()]; + int index = 0; + Iterator pkIt = pkJoinColumnElementList.listIterator(); + while ( pkIt.hasNext() ) { + Element subelement = (Element) pkIt.next(); + AnnotationDescriptor pkAnn = new AnnotationDescriptor( PrimaryKeyJoinColumn.class ); + copyStringAttribute( pkAnn, subelement, "name", false ); + copyStringAttribute( pkAnn, subelement, "referenced-column-name", false ); + copyStringAttribute( pkAnn, subelement, "column-definition", false ); + pkJoinColumns[index++] = AnnotationFactory.create( pkAnn ); + } + return pkJoinColumns; + } + + /** + * Copy a string 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 element XML element from where to copy the attribute. + * @param attributeName name of the XML attribute to copy. + * @param mandatory whether the attribute is mandatory. + */ + private static void copyStringAttribute( + final AnnotationDescriptor annotation, final Element element, + final String attributeName, final boolean mandatory) { + copyStringAttribute( + annotation, + element, + getJavaAttributeNameFromXMLOne( attributeName ), + attributeName, + mandatory + ); + } + + /** + * Copy a string 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 element XML element from where to copy the attribute. + * @param annotationAttributeName name of the annotation attribute where to copy. + * @param attributeName name of the XML attribute to copy. + * @param mandatory whether the attribute is mandatory. + */ + private static void copyStringAttribute( + final AnnotationDescriptor annotation, final Element element, + final String annotationAttributeName, final String attributeName, boolean mandatory) { + String attribute = element.attributeValue( attributeName ); + if ( attribute != null ) { + annotation.setValue( annotationAttributeName, attribute ); + } + else { + if ( mandatory ) { + throw new AnnotationException( + element.getName() + "." + attributeName + " is mandatory in XML overriding. " + SCHEMA_VALIDATION + ); + } + } + } + + private static void copyIntegerAttribute(AnnotationDescriptor annotation, Element element, String attributeName) { + String attribute = element.attributeValue( attributeName ); + if ( attribute != null ) { + String annotationAttributeName = getJavaAttributeNameFromXMLOne( attributeName ); + annotation.setValue( annotationAttributeName, attribute ); + try { + int length = Integer.parseInt( attribute ); + annotation.setValue( annotationAttributeName, length ); + } + catch ( NumberFormatException e ) { + throw new AnnotationException( + element.getPath() + attributeName + " not parseable: " + attribute + " (" + 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 static void copyStringElement(Element element, AnnotationDescriptor ad, String annotationAttribute) { + String discr = element.getTextTrim(); + ad.setValue( annotationAttribute, discr ); + } + + private static void copyBooleanAttribute(AnnotationDescriptor descriptor, Element element, String attribute) { + String attributeValue = element.attributeValue( attribute ); + if ( StringHelper.isNotEmpty( attributeValue ) ) { + String javaAttribute = getJavaAttributeNameFromXMLOne( attribute ); + descriptor.setValue( javaAttribute, Boolean.parseBoolean( attributeValue ) ); + } + } + + 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 000000000000..6991140d35f1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenMetadataProvider.java @@ -0,0 +1,230 @@ +/* + * 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.internal.ClassLoaderAccessImpl; +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; +import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.boot.spi.ClassLoaderAccess; +import org.hibernate.boot.spi.ClassLoaderAccessDelegateImpl; +import org.hibernate.boot.spi.MetadataBuildingOptions; + +import org.dom4j.Element; + +/** + * 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 boolean xmlMappingEnabled; + + private Map defaults; + private Map cache; + + /** + * @deprecated Use {@link JPAXMLOverriddenMetadataProvider#JPAXMLOverriddenMetadataProvider(BootstrapContext)} instead. + */ + @Deprecated + public JPAXMLOverriddenMetadataProvider(final MetadataBuildingOptions metadataBuildingOptions) { + this( new ClassLoaderAccessDelegateImpl() { + ClassLoaderAccess delegate; + + @Override + protected ClassLoaderAccess getDelegate() { + if ( delegate == null ) { + delegate = new ClassLoaderAccessImpl( + metadataBuildingOptions.getTempClassLoader(), + metadataBuildingOptions.getServiceRegistry().getService( ClassLoaderService.class ) + ); + } + return delegate; + } + }, + metadataBuildingOptions.isXmlMappingEnabled() ); + } + + public JPAXMLOverriddenMetadataProvider(BootstrapContext bootstrapContext) { + this( bootstrapContext.getClassLoaderAccess(), + bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled() ); + } + + JPAXMLOverriddenMetadataProvider(ClassLoaderAccess classLoaderAccess, boolean xmlMetadataEnabled) { + this.classLoaderAccess = classLoaderAccess; + this.xmlContext = new XMLContext( classLoaderAccess ); + this.xmlMappingEnabled = xmlMetadataEnabled; + } + + //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 ( xmlMappingEnabled == false ) { + 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 ( Element element : xmlContext.getAllDocuments() ) { + @SuppressWarnings( "unchecked" ) + List elements = element.elements( "sequence-generator" ); + List sequenceGenerators = ( List ) defaults.get( SequenceGenerator.class ); + if ( sequenceGenerators == null ) { + sequenceGenerators = new ArrayList<>(); + defaults.put( SequenceGenerator.class, sequenceGenerators ); + } + for ( Element subelement : elements ) { + sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) ); + } + + elements = element.elements( "table-generator" ); + List tableGenerators = ( List ) defaults.get( TableGenerator.class ); + if ( tableGenerators == null ) { + tableGenerators = new ArrayList<>(); + defaults.put( TableGenerator.class, tableGenerators ); + } + for ( Element subelement : elements ) { + tableGenerators.add( + JPAXMLOverriddenAnnotationReader.buildTableGeneratorAnnotation( + subelement, xmlDefaults + ) + ); + } + + List namedQueries = ( List ) defaults.get( NamedQuery.class ); + if ( namedQueries == null ) { + namedQueries = new ArrayList<>(); + defaults.put( NamedQuery.class, namedQueries ); + } + List currentNamedQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries( + element, + false, + 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.buildNamedQueries( + element, + true, + 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( + element, + 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( + element, + 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/XMLContext.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java new file mode 100644 index 000000000000..9dc7cdc537dd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/XMLContext.java @@ -0,0 +1,359 @@ +/* + * 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.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; + +import org.dom4j.Document; +import org.dom4j.Element; + +/** + * 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 Map classOverriding = new HashMap<>(); + private Map defaultsOverriding = new HashMap<>(); + private List defaultElements = new ArrayList<>(); + private List defaultEntityListeners = new ArrayList<>(); + private boolean hasContext = false; + + /** + * @deprecated Use {@link XMLContext#XMLContext(BootstrapContext)} instead. + */ + @Deprecated + public XMLContext(ClassLoaderAccess classLoaderAccess) { + this.classLoaderAccess = classLoaderAccess; + } + + public XMLContext(BootstrapContext bootstrapContext) { + this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); + } + + /** + * @param doc 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(Document doc) { + hasContext = true; + List addedClasses = new ArrayList<>(); + Element root = doc.getRootElement(); + //global defaults + Element metadata = root.element( "persistence-unit-metadata" ); + if ( metadata != null ) { + if ( globalDefaults == null ) { + globalDefaults = new Default(); + globalDefaults.setMetadataComplete( + metadata.element( "xml-mapping-metadata-complete" ) != null ? + Boolean.TRUE : + null + ); + Element defaultElement = metadata.element( "persistence-unit-defaults" ); + if ( defaultElement != null ) { + Element unitElement = defaultElement.element( "schema" ); + globalDefaults.setSchema( unitElement != null ? unitElement.getTextTrim() : null ); + unitElement = defaultElement.element( "catalog" ); + globalDefaults.setCatalog( unitElement != null ? unitElement.getTextTrim() : null ); + unitElement = defaultElement.element( "access" ); + setAccess( unitElement, globalDefaults ); + unitElement = defaultElement.element( "cascade-persist" ); + globalDefaults.setCascadePersist( unitElement != null ? Boolean.TRUE : null ); + unitElement = defaultElement.element( "delimited-identifiers" ); + globalDefaults.setDelimitedIdentifiers( unitElement != null ? Boolean.TRUE : null ); + defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement, null, addedClasses ) ); + } + } + else { + LOG.duplicateMetadata(); + } + } + + //entity mapping default + Default entityMappingDefault = new Default(); + Element unitElement = root.element( "package" ); + String packageName = unitElement != null ? unitElement.getTextTrim() : null; + entityMappingDefault.setPackageName( packageName ); + unitElement = root.element( "schema" ); + entityMappingDefault.setSchema( unitElement != null ? unitElement.getTextTrim() : null ); + unitElement = root.element( "catalog" ); + entityMappingDefault.setCatalog( unitElement != null ? unitElement.getTextTrim() : null ); + unitElement = root.element( "access" ); + setAccess( unitElement, entityMappingDefault ); + defaultElements.add( root ); + + setLocalAttributeConverterDefinitions( root.elements( "converter" ) ); + + List entities = root.elements( "entity" ); + addClass( entities, packageName, entityMappingDefault, addedClasses ); + + entities = root.elements( "mapped-superclass" ); + addClass( entities, packageName, entityMappingDefault, addedClasses ); + + entities = root.elements( "embeddable" ); + addClass( entities, packageName, entityMappingDefault, addedClasses ); + return addedClasses; + } + + private void setAccess(Element unitElement, Default defaultType) { + if ( unitElement != null ) { + String access = unitElement.getTextTrim(); + setAccess( access, defaultType ); + } + } + + private void setAccess( String access, Default defaultType) { + AccessType type; + if ( access != null ) { + try { + type = AccessType.valueOf( access ); + } + catch ( IllegalArgumentException e ) { + throw new AnnotationException( "Invalid access type " + access + " (check your xml configuration)" ); + } + defaultType.setAccess( type ); + } + } + + private void addClass(List entities, String packageName, Default defaults, List addedClasses) { + for (Element element : entities) { + String className = buildSafeClassName( element.attributeValue( "class" ), packageName ); + if ( classOverriding.containsKey( className ) ) { + //maybe switch it to warn? + throw new IllegalStateException( "Duplicate XML entry for " + className ); + } + addedClasses.add( className ); + classOverriding.put( className, element ); + Default localDefault = new Default(); + localDefault.override( defaults ); + String metadataCompleteString = element.attributeValue( "metadata-complete" ); + if ( metadataCompleteString != null ) { + localDefault.setMetadataComplete( Boolean.parseBoolean( metadataCompleteString ) ); + } + String access = element.attributeValue( "access" ); + setAccess( access, localDefault ); + defaultsOverriding.put( className, localDefault ); + + LOG.debugf( "Adding XML overriding information for %s", className ); + addEntityListenerClasses( element, packageName, addedClasses ); + } + } + + private List addEntityListenerClasses(Element element, String packageName, List addedClasses) { + List localAddedClasses = new ArrayList<>(); + Element listeners = element.element( "entity-listeners" ); + if ( listeners != null ) { + @SuppressWarnings( "unchecked" ) + List elements = listeners.elements( "entity-listener" ); + for (Element listener : elements) { + String listenerClassName = buildSafeClassName( listener.attributeValue( "class" ), packageName ); + if ( classOverriding.containsKey( listenerClassName ) ) { + //maybe switch it to warn? + if ( "entity-listener".equals( classOverriding.get( listenerClassName ).getName() ) ) { + LOG.duplicateListener( listenerClassName ); + continue; + } + throw new IllegalStateException("Duplicate XML entry for " + listenerClassName); + } + localAddedClasses.add( listenerClassName ); + classOverriding.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 ( Element converterElement : converterElements ) { + final String className = converterElement.attributeValue( "class" ); + final String autoApplyAttribute = converterElement.attributeValue( "auto-apply" ); + final boolean autoApply = autoApplyAttribute != null && Boolean.parseBoolean( autoApplyAttribute ); + + 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, XMLContext.Default defaults) { + return buildSafeClassName( className, defaults.getPackageName() ); + } + + public Default getDefault(String className) { + Default xmlDefault = new Default(); + xmlDefault.override( globalDefaults ); + if ( className != null ) { + Default entityMappingOverriding = defaultsOverriding.get( className ); + xmlDefault.override( entityMappingOverriding ); + } + return xmlDefault; + } + + public Element getXMLTree(String className ) { + return classOverriding.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/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java index 2eeb459eaeab..1ee923a43e55 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,8 @@ */ package org.hibernate.test.annotations.reflection; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; @@ -13,9 +15,15 @@ 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) { + // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings + super.prepareBootstrapRegistryBuilder( 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 000000000000..7024aad12492 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java @@ -0,0 +1,432 @@ +/* + * 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.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.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 SAXException, DocumentException, IOException { + XMLHelper xmlHelper = new XMLHelper(); + InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( ormfile ); + assertNotNull( "ORM.xml not found: " + ormfile, is ); + XMLContext context = new XMLContext( BootstrapContextImpl.INSTANCE ); + ErrorLogger errorLogger = new ErrorLogger(); + SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE ); + //saxReader.setValidation( false ); + 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 { + is.close(); + } + if ( errorLogger.hasErrors() ) { + System.out.println( errorLogger.getErrors().get( 0 ) ); + } + assertFalse( errorLogger.hasErrors() ); + context.addDocument( doc ); + 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 000000000000..70d559357b16 --- /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[] getXmlFiles() { + 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 c79a60e708d2..57561467ccea 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.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 000000000000..38ade26b4cae --- /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 235fe820e4a3..fb68e62a4604 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 @@ -17,16 +17,24 @@ import org.xml.sax.SAXNotSupportedException; import org.hibernate.cfg.EJB3DTDEntityResolver; -import org.hibernate.cfg.annotations.reflection.XMLContext; +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.testing.TestForIssue; import org.hibernate.testing.boot.BootstrapContextImpl; import org.hibernate.testing.boot.ClassLoaderServiceTestingImpl; /** + * 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 { 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 235b7e70a528..73d749f4f00b 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.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 01f6170000df..7f37ff436318 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.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 b0e3c43b2ad3..dfe16a7468f6 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.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 bca6d21c01ac..c1eda4f9d249 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.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 fb2c0f70b151..e513889c0fc1 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.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 2a4964ca0c2b..89bb7873120c 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 @@ -12,6 +12,7 @@ import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; +import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder; import org.hibernate.dialect.CockroachDB192Dialect; import org.hibernate.dialect.PostgreSQL81Dialect; import org.hibernate.dialect.PostgreSQLDialect; @@ -19,6 +20,7 @@ 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 +30,15 @@ /** * @author Emmanuel Bernard */ +@TestForIssue(jiraKey = "HHH-14529") public class Ejb3XmlTest extends BaseCoreFunctionalTestCase { + + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings + super.prepareBootstrapRegistryBuilder( builder ); + } + @Test @SkipForDialect(value = {PostgreSQL81Dialect.class, PostgreSQLDialect.class, CockroachDB192Dialect.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 3c96ba4df1cb..c4ebbed1d651 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 @@ -13,8 +13,8 @@ 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.cfg.annotations.reflection.internal.JPAXMLOverriddenAnnotationReader; +import org.hibernate.cfg.annotations.reflection.internal.XMLContext; import org.hibernate.testing.boot.BootstrapContextImpl; import org.hibernate.testing.junit4.BaseUnitTestCase; @@ -28,8 +28,8 @@ * 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 void assertAnnotationPresent(Class annotationType) { assertTrue( @@ -45,11 +45,11 @@ protected void assertAnnotationNotPresent(Class annotation ); } - 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 { 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 000000000000..7d46e662bfcd --- /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 000000000000..e80c00e08e8d --- /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 000000000000..e665be4f2ef4 --- /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 000000000000..b531fabecf21 --- /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 000000000000..45c5e5e8448b --- /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 000000000000..dae470e238c8 --- /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.CockroachDB192Dialect; +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, CockroachDB192Dialect.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[] getXmlFiles() { + 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 000000000000..e22dfea26ea4 --- /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 000000000000..74509b879ca3 --- /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 000000000000..763b79f605d2 --- /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[] getXmlFiles() { + 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 000000000000..9d3e4654b210 --- /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 986c9c077e2d..63e9df91b67b 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,6 +8,7 @@ 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.testing.TestForIssue; @@ -16,12 +17,13 @@ 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() { + // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings try { - new MetadataSources() + new MetadataSources( new BootstrapServiceRegistryBuilder().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 03658267dd2a..f56ccd780edf 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,6 +8,7 @@ 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; @@ -23,9 +24,15 @@ 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) { + // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings + super.prepareBootstrapRegistryBuilder( 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 index 04640901d860..0fa294fe0335 100644 --- 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 @@ -11,6 +11,7 @@ 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.testing.TestForIssue; @@ -19,9 +20,15 @@ import static org.assertj.core.api.Assertions.assertThat; -@TestForIssue(jiraKey = "HHH-14530") +@TestForIssue(jiraKey = {"HHH-14530", "HHH-14529"}) public class PreParsedOrmXmlTest extends BaseCoreFunctionalTestCase { + @Override + protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { + // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings + super.prepareBootstrapRegistryBuilder( builder ); + } + @Override protected void addMappings(Configuration configuration) { super.addMappings( configuration ); From ef6bb2679bc386a8eaa01ef415381476343cf370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 10:36:51 +0100 Subject: [PATCH 12/21] HHH-14529 Remove an unused orm.xml file from tests --- .../org/hibernate/test/cfg/orm-serializable.xml | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 hibernate-core/src/test/java/org/hibernate/test/cfg/orm-serializable.xml 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 c9620d83b8a0..000000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/orm-serializable.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - From e8cd9f891752c48f18ae65b435b7afc9a2164419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 11:40:03 +0100 Subject: [PATCH 13/21] HHH-14529 Clean up constructors in JPAXMLOverriddenMetadataProvider and related --- .../JPAXMLOverriddenAnnotationReader.java | 6 ++-- .../JPAXMLOverriddenMetadataProvider.java | 35 ++----------------- .../reflection/internal/XMLContext.java | 7 ++-- 3 files changed, 6 insertions(+), 42 deletions(-) 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 index 4e2a630952ee..bac75d36ad90 100644 --- 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 @@ -250,10 +250,7 @@ private enum PropertyType { private transient List elementsForProperty; private AccessibleObject mirroredAttribute; - /** - * @deprecated Use {@link #JPAXMLOverriddenAnnotationReader(AnnotatedElement, XMLContext, BootstrapContext)} instead. - */ - public JPAXMLOverriddenAnnotationReader( + JPAXMLOverriddenAnnotationReader( AnnotatedElement el, XMLContext xmlContext, ClassLoaderAccess classLoaderAccess) { @@ -318,6 +315,7 @@ else if ( methodName.startsWith( "is" ) ) { } } + // For tests only public JPAXMLOverriddenAnnotationReader( AnnotatedElement el, XMLContext xmlContext, 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 index 6991140d35f1..fc78a879be5a 100644 --- 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 @@ -23,13 +23,9 @@ 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.internal.ClassLoaderAccessImpl; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.ClassLoaderAccess; -import org.hibernate.boot.spi.ClassLoaderAccessDelegateImpl; -import org.hibernate.boot.spi.MetadataBuildingOptions; import org.dom4j.Element; @@ -55,37 +51,10 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider private Map defaults; private Map cache; - /** - * @deprecated Use {@link JPAXMLOverriddenMetadataProvider#JPAXMLOverriddenMetadataProvider(BootstrapContext)} instead. - */ - @Deprecated - public JPAXMLOverriddenMetadataProvider(final MetadataBuildingOptions metadataBuildingOptions) { - this( new ClassLoaderAccessDelegateImpl() { - ClassLoaderAccess delegate; - - @Override - protected ClassLoaderAccess getDelegate() { - if ( delegate == null ) { - delegate = new ClassLoaderAccessImpl( - metadataBuildingOptions.getTempClassLoader(), - metadataBuildingOptions.getServiceRegistry().getService( ClassLoaderService.class ) - ); - } - return delegate; - } - }, - metadataBuildingOptions.isXmlMappingEnabled() ); - } - public JPAXMLOverriddenMetadataProvider(BootstrapContext bootstrapContext) { - this( bootstrapContext.getClassLoaderAccess(), - bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled() ); - } - - JPAXMLOverriddenMetadataProvider(ClassLoaderAccess classLoaderAccess, boolean xmlMetadataEnabled) { - this.classLoaderAccess = classLoaderAccess; + this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); this.xmlContext = new XMLContext( classLoaderAccess ); - this.xmlMappingEnabled = xmlMetadataEnabled; + this.xmlMappingEnabled = bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled(); } //all of the above can be safely rebuilt from XMLContext: only XMLContext this object is serialized 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 index 9dc7cdc537dd..ffa00e83225d 100644 --- 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 @@ -46,14 +46,11 @@ public class XMLContext implements Serializable { private List defaultEntityListeners = new ArrayList<>(); private boolean hasContext = false; - /** - * @deprecated Use {@link XMLContext#XMLContext(BootstrapContext)} instead. - */ - @Deprecated - public XMLContext(ClassLoaderAccess classLoaderAccess) { + XMLContext(ClassLoaderAccess classLoaderAccess) { this.classLoaderAccess = classLoaderAccess; } + // For tests only public XMLContext(BootstrapContext bootstrapContext) { this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); } From 8ab3a2f7e925d267fe607d3990d7f0e6223ba5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 11:58:35 +0100 Subject: [PATCH 14/21] HHH-14529 Configuration and wiring to prefer JAXB over DOM4J for orm.xml handling --- .../boot/internal/BootstrapContextImpl.java | 11 ++- .../boot/internal/MetadataBuilderImpl.java | 13 ++- .../internal/DefaultXmlMappingOptions.java | 12 +++ .../boot/jaxb/internal/MappingBinder.java | 44 +++++++--- .../boot/jaxb/spi/XmlMappingOptions.java | 86 +++++++++++++++++++ .../process/spi/MetadataBuildingProcess.java | 20 ++--- ...AnnotationMetadataSourceProcessorImpl.java | 27 +++++- .../internal/StrategySelectorBuilder.java | 4 + ...ractDelegatingMetadataBuildingOptions.java | 5 +- .../boot/spi/MetadataBuildingOptions.java | 5 +- .../boot/spi/XmlMappingBinderAccess.java | 8 +- .../org/hibernate/cfg/AvailableSettings.java | 1 - .../reflection/JPAMetadataProvider.java | 26 ++++-- .../JPAOverriddenAnnotationReader.java | 12 ++- .../annotations/reflection/XMLContext.java | 7 +- .../JPAXMLOverriddenAnnotationReader.java | 3 +- .../JPAXMLOverriddenMetadataProvider.java | 9 +- .../reflection/internal/XMLContext.java | 12 +++ ...ngOptionsStrategyRegistrationProvider.java | 66 ++++++++++++++ .../ElementCollectionConverterTest.java | 3 +- .../annotations/xml/ejb3/Ejb3XmlTest.java | 3 +- .../xml/ejb3/NonExistentOrmVersionTest.java | 6 +- .../xml/ejb3/OrmVersion1SupportedTest.java | 3 +- .../xml/ejb3/PreParsedOrmXmlTest.java | 3 +- ...stry.selector.StrategyRegistrationProvider | 1 + .../AdditionalJaxbMappingProducerImpl.java | 14 ++- .../boot/internal/EnversServiceImpl.java | 9 -- 27 files changed, 341 insertions(+), 72 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/internal/DefaultXmlMappingOptions.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlMappingOptions.java create mode 100644 hibernate-core/src/test/java/org/hibernate/internal/util/xml/XmlMappingOptionsStrategyRegistrationProvider.java create mode 100644 hibernate-core/src/test/resources/META-INF/services/org.hibernate.boot.registry.selector.StrategyRegistrationProvider 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 476b9843f45c..6f05fafad590 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 @@ -31,6 +31,7 @@ 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.dialect.function.SQLFunction; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.jpa.internal.MutableJpaComplianceImpl; @@ -83,10 +84,10 @@ public BootstrapContextImpl( final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); this.classLoaderAccess = new ClassLoaderAccessImpl( classLoaderService ); - this.hcannReflectionManager = generateHcannReflectionManager(); final StrategySelector strategySelector = serviceRegistry.getService( StrategySelector.class ); final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class ); + this.hcannReflectionManager = generateHcannReflectionManager(); this.jpaCompliance = new MutableJpaComplianceImpl( configService.getSettings(), false ); this.scanOptions = new StandardScanOptions( @@ -309,7 +310,13 @@ public void addCacheRegionDefinition(CacheRegionDefinition cacheRegionDefinition 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 f5ebda41eda8..0b0195d2d97c 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 @@ -30,6 +30,7 @@ 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; @@ -622,7 +623,7 @@ public static class MetadataBuildingOptionsImpl private IdGeneratorInterpreterImpl idGenerationTypeInterpreter = new IdGeneratorInterpreterImpl(); private String schemaCharset; - private boolean xmlMappingEnabled; + private final XmlMappingOptions xmlMappingOptions; public MetadataBuildingOptionsImpl(StandardServiceRegistry serviceRegistry) { this.serviceRegistry = serviceRegistry; @@ -634,11 +635,7 @@ public MetadataBuildingOptionsImpl(StandardServiceRegistry serviceRegistry) { 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, @@ -926,8 +923,8 @@ public String getSchemaCharset() { } @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 000000000000..9812b21fc984 --- /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 8dd1e96b1ea0..ba0049988b75 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.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,18 +41,18 @@ public class MappingBinder extends AbstractBinder { private final XMLEventFactory xmlEventFactory = XMLEventFactory.newInstance(); - private JAXBContext hbmJaxbContext; + private final XmlMappingOptions options; - public MappingBinder(ClassLoaderService classLoaderService) { - this( classLoaderService, true ); - } + private JAXBContext hbmJaxbContext; + private JAXBContext entityMappingsJaxbContext; - public MappingBinder(ClassLoaderService classLoaderService, boolean validateXml) { + public MappingBinder(ClassLoaderService classLoaderService, boolean validateXml, XmlMappingOptions options) { super( classLoaderService, validateXml ); + this.options = options; } @Override - protected Binding doBind( + protected Binding doBind( XMLEventReader staxEventReader, StartElement rootElementStartEvent, Origin origin) { @@ -63,12 +65,20 @@ protected Binding doBind( 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 ); @@ -88,6 +98,18 @@ private JAXBContext hbmJaxbContext() { 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/spi/XmlMappingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/spi/XmlMappingOptions.java new file mode 100644 index 000000000000..5247ef82ccc0 --- /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 6b40eac9aee3..1457f3c12923 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.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.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; @@ -98,16 +96,11 @@ public static ManagedResources prepare( 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; } @@ -157,7 +150,7 @@ public static MetadataImplementor complete( final MetadataSourceProcessor processor = new MetadataSourceProcessor() { private final MetadataSourceProcessor hbmProcessor = - options.isXmlMappingEnabled() + options.getXmlMappingOptions().isEnabled() ? new HbmMetadataSourceProcessorImpl( managedResources, rootMetadataBuildingContext ) : new NoOpMetadataSourceProcessorImpl(); @@ -294,13 +287,14 @@ public void finishUp() { metadataCollector.processSecondPasses( rootMetadataBuildingContext ); - if ( options.isXmlMappingEnabled() ) { + XmlMappingOptions xmlMappingOptions = options.getXmlMappingOptions(); + if ( xmlMappingOptions.isEnabled() ) { 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 9fe1d5ed8342..f336e4551cf2 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.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,11 +79,29 @@ public AnnotationMetadataSourceProcessorImpl( 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() ) { @@ -94,6 +116,7 @@ public AnnotationMetadataSourceProcessorImpl( } } jpaMetadataProvider.getXMLContext().applyDiscoveredAttributeConverters( attributeConverterManager ); + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } 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 0d0f48c3c8e3..a09c90cc5f2e 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 @@ 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; @@ -100,6 +102,8 @@ public StrategySelector buildSelector(ClassLoaderService classLoaderService) { addMultiTableBulkIdStrategies( 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 3b8be18e370e..b2abe158798c 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 @@ -18,6 +18,7 @@ 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; @@ -203,8 +204,8 @@ public String getSchemaCharset() { } @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 65a964e92433..0fc02ca34293 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 @@ -17,6 +17,7 @@ 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; @@ -251,8 +252,8 @@ default String getSchemaCharset() { 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 8102865f2acd..fa7014840b71 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.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/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index cc547527859e..65ef3f9afa89 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -698,7 +698,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/annotations/reflection/JPAMetadataProvider.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/JPAMetadataProvider.java index 967523143fbd..5579a01c79f0 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 @@ -30,6 +30,7 @@ import org.hibernate.boot.spi.ClassLoaderAccess; import org.hibernate.boot.spi.ClassLoaderAccessDelegateImpl; import org.hibernate.boot.spi.MetadataBuildingOptions; +import org.hibernate.cfg.annotations.reflection.internal.JPAXMLOverriddenMetadataProvider; import org.dom4j.Element; @@ -37,7 +38,13 @@ * 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 { @@ -74,12 +81,12 @@ protected ClassLoaderAccess getDelegate() { 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) { @@ -147,7 +154,8 @@ public Map getDefaults() { defaults.put( SequenceGenerator.class, sequenceGenerators ); } for ( Element subelement : elements ) { - sequenceGenerators.add( JPAOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) ); + sequenceGenerators.add( JPAOverriddenAnnotationReader + .buildSequenceGeneratorAnnotation( subelement ) ); } elements = element.elements( "table-generator" ); @@ -169,7 +177,8 @@ public Map getDefaults() { namedQueries = new ArrayList<>(); defaults.put( NamedQuery.class, namedQueries ); } - List currentNamedQueries = JPAOverriddenAnnotationReader.buildNamedQueries( + List currentNamedQueries = JPAOverriddenAnnotationReader + .buildNamedQueries( element, false, xmlDefaults, @@ -182,7 +191,8 @@ public Map getDefaults() { namedNativeQueries = new ArrayList<>(); defaults.put( NamedNativeQuery.class, namedNativeQueries ); } - List currentNamedNativeQueries = JPAOverriddenAnnotationReader.buildNamedQueries( + List currentNamedNativeQueries = JPAOverriddenAnnotationReader + .buildNamedQueries( element, true, xmlDefaults, @@ -197,7 +207,8 @@ public Map getDefaults() { sqlResultSetMappings = new ArrayList<>(); defaults.put( SqlResultSetMapping.class, sqlResultSetMappings ); } - List currentSqlResultSetMappings = JPAOverriddenAnnotationReader.buildSqlResultsetMappings( + List currentSqlResultSetMappings = JPAOverriddenAnnotationReader + .buildSqlResultsetMappings( element, xmlDefaults, classLoaderAccess @@ -209,7 +220,8 @@ public Map getDefaults() { 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 b9adae8b41f2..895da45b37df 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.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; @@ -140,7 +141,13 @@ * @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 ); @@ -884,7 +891,7 @@ private JoinTable buildJoinTable(Element tree, XMLContext.Default defaults) { * 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 @@ -2691,7 +2698,8 @@ private IdClass getIdClass(Element tree, XMLContext.Default defaults) { 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 8aef32bc9bbe..9cde55fea7c0 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 @@ * * @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 static String buildSafeClassName(String className, String defaultPackageN 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 index bac75d36ad90..30cbae7208b4 100644 --- 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 @@ -142,6 +142,8 @@ * @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 ); @@ -323,7 +325,6 @@ public JPAXMLOverriddenAnnotationReader( this( el, xmlContext, bootstrapContext.getClassLoaderAccess() ); } - public T getAnnotation(Class annotationType) { initAnnotations(); return (T) annotationsMap.get( annotationType ); 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 index fc78a879be5a..6c65829d0999 100644 --- 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 @@ -23,6 +23,7 @@ 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.spi.XmlMappingOptions; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.ClassLoaderAccess; @@ -34,6 +35,8 @@ * * @author Emmanuel Bernard */ +// 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 final class JPAXMLOverriddenMetadataProvider implements MetadataProvider { @@ -46,7 +49,7 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider * We allow fully disabling XML sources so to improve the efficiency of * the boot process for those not using it. */ - private final boolean xmlMappingEnabled; + private final XmlMappingOptions xmlMappingOptions; private Map defaults; private Map cache; @@ -54,7 +57,7 @@ public final class JPAXMLOverriddenMetadataProvider implements MetadataProvider public JPAXMLOverriddenMetadataProvider(BootstrapContext bootstrapContext) { this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); this.xmlContext = new XMLContext( classLoaderAccess ); - this.xmlMappingEnabled = bootstrapContext.getMetadataBuildingOptions().isXmlMappingEnabled(); + this.xmlMappingOptions = bootstrapContext.getMetadataBuildingOptions().getXmlMappingOptions(); } //all of the above can be safely rebuilt from XMLContext: only XMLContext this object is serialized @@ -87,7 +90,7 @@ public void reset() { @Override public Map getDefaults() { - if ( xmlMappingEnabled == false ) { + if ( !xmlMappingOptions.isEnabled() ) { return Collections.emptyMap(); } else { 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 index ffa00e83225d..513fbd6aa87c 100644 --- 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 @@ -16,10 +16,12 @@ import org.hibernate.AnnotationException; import org.hibernate.boot.AttributeConverterInfo; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappings; 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.NotYetImplementedException; import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; @@ -34,6 +36,8 @@ * @author Emmanuel Bernard * @author Brett Meyer */ +// 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. public class XMLContext implements Serializable { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( XMLContext.class ); @@ -55,6 +59,14 @@ public XMLContext(BootstrapContext bootstrapContext) { this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); } + /** + * @param entityMappings The xml entity mappings to add + * @return Add an xml document to this context and return the list of added class names. + */ + public List addDocument(JaxbEntityMappings entityMappings) { + throw new NotYetImplementedException("HHH-14529 Implementation in progress"); + } + /** * @param doc The xml document to add * @return Add an xml document to this context and return the list of added class names. 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 000000000000..97c6a617ce42 --- /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/test/annotations/reflection/ElementCollectionConverterTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/ElementCollectionConverterTest.java index 1ee923a43e55..e0357054dd3c 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 @@ -7,6 +7,7 @@ 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; @@ -20,8 +21,8 @@ public class ElementCollectionConverterTest extends BaseCoreFunctionalTestCase { @Override protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { - // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); } @Override 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 89bb7873120c..96335a4d416d 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 @@ -17,6 +17,7 @@ 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; @@ -35,8 +36,8 @@ public class Ejb3XmlTest extends BaseCoreFunctionalTestCase { @Override protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { - // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); } @Test 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 63e9df91b67b..13a8535ca90a 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 @@ -10,6 +10,7 @@ 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; @@ -21,9 +22,10 @@ public class NonExistentOrmVersionTest extends BaseUnitTestCase { @Test public void testNonExistentOrmVersion() { - // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings try { - new MetadataSources( new BootstrapServiceRegistryBuilder().build() ) + 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 f56ccd780edf..ef227c81ae6d 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 @@ -11,6 +11,7 @@ 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; @@ -29,8 +30,8 @@ public class OrmVersion1SupportedTest extends BaseCoreFunctionalTestCase { @Override protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { - // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); } @Rule 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 index 0fa294fe0335..057c4ede5d60 100644 --- 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 @@ -13,6 +13,7 @@ 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; @@ -25,8 +26,8 @@ public class PreParsedOrmXmlTest extends BaseCoreFunctionalTestCase { @Override protected void prepareBootstrapRegistryBuilder(BootstrapServiceRegistryBuilder builder) { - // FIXME HHH-14529 configure the BootstrapServiceRegistry to use JAXB for orm.xml mappings super.prepareBootstrapRegistryBuilder( builder ); + XmlMappingOptionsStrategyRegistrationProvider.applyJaxbStrategy( builder ); } @Override 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 000000000000..98148e1689bf --- /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-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 37b0c95c9ecc..8a18b931ed98 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,11 +24,14 @@ 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.configuration.internal.MappingCollector; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.ServiceRegistry; import org.jboss.jandex.IndexView; @@ -39,6 +42,8 @@ import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; +import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; + /** * @author Steve Ebersole */ @@ -51,7 +56,8 @@ public Collection produceAdditionalMappings( 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() ) { @@ -59,6 +65,12 @@ public Collection produceAdditionalMappings( 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 f0ab2ad6f21e..05d749b3b195 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 @@ 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.jboss.logging.Logger; -import static org.hibernate.cfg.AvailableSettings.XML_MAPPING_ENABLED; - /** * Provides central access to Envers' configuration. * @@ -89,10 +84,6 @@ public void configure(Map configurationValues) { ); } 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 ); } From bbc25cf28bdcc0c1b765395961345dd2ac6f89c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 13:05:57 +0100 Subject: [PATCH 15/21] HHH-14529 Add common interface for JaxbEntity and JaxbMappedSuperclass --- .../mapping/spi/EntityOrMappedSuperclass.java | 66 +++++++++++++++++++ .../src/main/xjb/mapping-bindings.xjb | 2 + 2 files changed, 68 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/EntityOrMappedSuperclass.java 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 000000000000..5355dd8bbc26 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/EntityOrMappedSuperclass.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 . + */ +package org.hibernate.boot.jaxb.mapping.spi; + +/** + * Common interface for JAXB bindings representing entities and mapped-superclasses. + */ +public interface EntityOrMappedSuperclass extends ManagedType { + + String getDescription(); + + void setDescription(String value); + + 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/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index 8ee8f838c3fd..5bd32a491484 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -81,12 +81,14 @@ org.hibernate.boot.jaxb.mapping.spi.ManagedType + org.hibernate.boot.jaxb.mapping.spi.EntityOrMappedSuperclass org.hibernate.boot.jaxb.mapping.spi.ManagedType org.hibernate.boot.jaxb.mapping.spi.ManagedType + org.hibernate.boot.jaxb.mapping.spi.EntityOrMappedSuperclass From efe5424d3502c19546b7372e30fef598535e93a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 13:22:09 +0100 Subject: [PATCH 16/21] HHH-14529 Add a few missing enum mappings for orm.xml --- .../internal/ConstraintModeMarshalling.java | 24 +++++++++++++++++++ .../internal/GenerationTypeMarshalling.java | 24 +++++++++++++++++++ .../internal/InheritanceTypeMarshalling.java | 24 +++++++++++++++++++ .../src/main/xjb/mapping-bindings.xjb | 18 ++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/ConstraintModeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/GenerationTypeMarshalling.java create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/InheritanceTypeMarshalling.java 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 000000000000..48049d56a763 --- /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/GenerationTypeMarshalling.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/internal/GenerationTypeMarshalling.java new file mode 100644 index 000000000000..04e815d939a8 --- /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 000000000000..93668921b7e2 --- /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/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index 5bd32a491484..707a76e6681e 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -59,6 +59,24 @@ printMethod="org.hibernate.boot.jaxb.mapping.internal.TemporalTypeMarshalling.toXml" /> + + + + + + + + + + + + org.hibernate.boot.jaxb.mapping.spi.SchemaAware From b4dd2e272f4c57610ef274ecf646aea6a8f051d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 13:32:13 +0100 Subject: [PATCH 17/21] HHH-14529 Add a few missing methods to the JAXB representation of ManagedType --- .../boot/jaxb/mapping/spi/EntityOrMappedSuperclass.java | 4 ---- .../org/hibernate/boot/jaxb/mapping/spi/ManagedType.java | 9 +++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) 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 index 5355dd8bbc26..a09d57ff304c 100644 --- 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 @@ -10,10 +10,6 @@ * Common interface for JAXB bindings representing entities and mapped-superclasses. */ public interface EntityOrMappedSuperclass extends ManagedType { - - String getDescription(); - - void setDescription(String value); JaxbIdClass getIdClass(); 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 index ab70cbf6d3db..7d8da934bd0b 100644 --- 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 @@ -16,6 +16,11 @@ * @author Steve Ebersole */ public interface ManagedType { + + String getDescription(); + + void setDescription(String value); + String getClazz(); void setClazz(String className); @@ -25,4 +30,8 @@ public interface ManagedType { void setMetadataComplete(Boolean isMetadataComplete); AccessType getAccess(); + + void setAccess(AccessType value); + + AttributesContainer getAttributes(); } From 437af3b4da6ff00ea3cb798a2c4533ecaa4d7b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 15:47:03 +0100 Subject: [PATCH 18/21] HHH-14529 Add a common interface for JaxbEntity, JaxbMappedSuperclass and JaxbEntityListener --- .../mapping/spi/EntityOrMappedSuperclass.java | 2 +- .../spi/LifecycleCallbackContainer.java | 45 +++++++++++++++++++ .../src/main/xjb/mapping-bindings.xjb | 7 ++- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/LifecycleCallbackContainer.java 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 index a09d57ff304c..0c33cec1ae1b 100644 --- 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 @@ -9,7 +9,7 @@ /** * Common interface for JAXB bindings representing entities and mapped-superclasses. */ -public interface EntityOrMappedSuperclass extends ManagedType { +public interface EntityOrMappedSuperclass extends ManagedType, LifecycleCallbackContainer { JaxbIdClass getIdClass(); 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 000000000000..7c0550491e23 --- /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/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index 707a76e6681e..c17120a23da3 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -100,6 +100,7 @@ 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 @@ -107,6 +108,11 @@ 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 @@ -173,7 +179,6 @@ org.hibernate.boot.jaxb.mapping.spi.AttributesContainer - From 10aee2a9fa2b652ed42ff209f9abaf6226a28d9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Fri, 26 Mar 2021 16:59:09 +0100 Subject: [PATCH 19/21] HHH-14529 Add a common interface for Jaxb representations of associations --- .../mapping/spi/AssociationAttribute.java | 23 +++++++++++++++++++ .../src/main/xjb/mapping-bindings.xjb | 4 ++++ 2 files changed, 27 insertions(+) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/AssociationAttribute.java 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 000000000000..f9784577abb8 --- /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/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index c17120a23da3..1c611dcf0165 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -127,14 +127,17 @@ 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 @@ -145,6 +148,7 @@ 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 From ac4f4ff4ad3c260c5ec996ba668a7760c8c429bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 30 Mar 2021 16:20:03 +0200 Subject: [PATCH 20/21] HHH-14529 Implement the (opt-in) orm.xml handling using JAXB --- .../boot/jaxb/mapping/spi/NamedQuery.java | 31 + .../JPAXMLOverriddenAnnotationReader.java | 1762 ++++++++--------- .../JPAXMLOverriddenMetadataProvider.java | 34 +- .../PropertyMappingElementCollector.java | 214 ++ .../reflection/internal/XMLContext.java | 184 +- .../src/main/xjb/mapping-bindings.xjb | 6 +- .../internal/util/xml/XMLMappingHelper.java | 51 + .../JPAXMLOverriddenAnnotationReaderTest.java | 37 +- .../reflection/XMLContextTest.java | 55 +- .../annotations/xml/ejb3/Ejb3XmlTestCase.java | 32 +- 10 files changed, 1309 insertions(+), 1097 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/jaxb/mapping/spi/NamedQuery.java create mode 100644 hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/PropertyMappingElementCollector.java create mode 100644 hibernate-core/src/test/java/org/hibernate/internal/util/xml/XMLMappingHelper.java 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 000000000000..79ded21dc1d3 --- /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/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/reflection/internal/JPAXMLOverriddenAnnotationReader.java index 30cbae7208b4..c91855d43efa 100644 --- 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 @@ -14,13 +14,13 @@ 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.Iterator; import java.util.List; -import java.util.Locale; 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; @@ -123,6 +123,67 @@ 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; @@ -131,8 +192,8 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; -import org.dom4j.Attribute; -import org.dom4j.Element; +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, ...). @@ -249,7 +310,7 @@ private enum PropertyType { private final PropertyType propertyType; private transient Annotation[] annotations; private transient Map annotationsMap; - private transient List elementsForProperty; + private transient PropertyMappingElementCollector elementsForProperty; private AccessibleObject mirroredAttribute; JPAXMLOverriddenAnnotationReader( @@ -349,7 +410,7 @@ private void initAnnotations() { XMLContext.Default defaults = xmlContext.getDefault( className ); if ( className != null && propertyName == null ) { //is a class - Element tree = xmlContext.getXMLTree( className ); + ManagedType managedTypeOverride = xmlContext.getManagedTypeOverride( className ); Annotation[] annotations = getPhysicalAnnotations(); List annotationList = new ArrayList<>( annotations.length + 5 ); annotationsMap = new HashMap<>( annotations.length + 5 ); @@ -359,40 +420,41 @@ private void initAnnotations() { annotationList.add( annotation ); } } - addIfNotNull( annotationList, getEntity( tree, defaults ) ); - addIfNotNull( annotationList, getMappedSuperclass( tree, defaults ) ); - addIfNotNull( annotationList, getEmbeddable( tree, defaults ) ); - addIfNotNull( annotationList, getTable( tree, defaults ) ); - addIfNotNull( annotationList, getSecondaryTables( tree, defaults ) ); - addIfNotNull( annotationList, getPrimaryKeyJoinColumns( tree, defaults, true ) ); - addIfNotNull( annotationList, getIdClass( tree, defaults ) ); - addIfNotNull( annotationList, getCacheable( tree, defaults ) ); - addIfNotNull( annotationList, getInheritance( tree, defaults ) ); - addIfNotNull( annotationList, getDiscriminatorValue( tree, defaults ) ); - addIfNotNull( annotationList, getDiscriminatorColumn( tree, defaults ) ); - addIfNotNull( annotationList, getSequenceGenerator( tree, defaults ) ); - addIfNotNull( annotationList, getTableGenerator( tree, defaults ) ); - addIfNotNull( annotationList, getNamedQueries( tree, defaults ) ); - addIfNotNull( annotationList, getNamedNativeQueries( tree, defaults ) ); - addIfNotNull( annotationList, getNamedStoredProcedureQueries( tree, defaults ) ); - addIfNotNull( annotationList, getNamedEntityGraphs( tree, defaults ) ); - addIfNotNull( annotationList, getSqlResultSetMappings( tree, defaults ) ); - addIfNotNull( annotationList, getExcludeDefaultListeners( tree, defaults ) ); - addIfNotNull( annotationList, getExcludeSuperclassListeners( tree, defaults ) ); - addIfNotNull( annotationList, getAccessType( tree, defaults ) ); - addIfNotNull( annotationList, getAttributeOverrides( tree, defaults, true ) ); - addIfNotNull( annotationList, getAssociationOverrides( tree, defaults, true ) ); - addIfNotNull( annotationList, getEntityListeners( tree, defaults ) ); - addIfNotNull( annotationList, getConverts( tree, defaults ) ); + 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( tree ); + checkForOrphanProperties( managedTypeOverride ); } else if ( className != null ) { //&& propertyName != null ) { //always true but less confusing - Element tree = xmlContext.getXMLTree( className ); + 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 ); @@ -402,7 +464,7 @@ else if ( className != null ) { //&& propertyName != null ) { //always true but annotationList.add( annotation ); } } - preCalculateElementsForProperty( tree ); + preCalculateElementsForProperty( managedTypeOverride, entityListenerOverride ); Transient transientAnn = getTransient( defaults ); if ( transientAnn != null ) { annotationList.add( transientAnn ); @@ -417,12 +479,12 @@ else if ( className != null ) { //&& propertyName != null ) { //always true but getEmbedded( annotationList, defaults ); getBasic( annotationList, defaults ); getVersion( annotationList, defaults ); - getAssociation( ManyToOne.class, annotationList, defaults ); - getAssociation( OneToOne.class, annotationList, defaults ); - getAssociation( OneToMany.class, annotationList, defaults ); - getAssociation( ManyToMany.class, annotationList, defaults ); - getAssociation( Any.class, annotationList, defaults ); - getAssociation( ManyToAny.class, 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 ) ); @@ -445,7 +507,7 @@ else if ( className != null ) { //&& propertyName != null ) { //always true but } } - private Annotation getConvertsForAttribute(List elementsForProperty, XMLContext.Default defaults) { + 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 @@ -453,19 +515,18 @@ private Annotation getConvertsForAttribute(List elementsForProperty, XM final Map convertAnnotationsMap = new HashMap<>(); - for ( Element element : elementsForProperty ) { - final boolean isBasic = "basic".equals( element.getName() ); - final boolean isEmbedded = "embedded".equals( element.getName() ); - final boolean isElementCollection = "element-collection".equals(element.getName()); - - final boolean canHaveConverts = isBasic || isEmbedded || isElementCollection; - - if ( !canHaveConverts ) { - continue; + for ( JaxbBasic element : elementsForProperty.getBasic() ) { + JaxbConvert convert = element.getConvert(); + if ( convert != null ) { + applyXmlDefinedConverts( Collections.singletonList( convert ), defaults, null, + convertAnnotationsMap ); } - - final String attributeNamePrefix = isBasic ? null : propertyName; - applyXmlDefinedConverts( element, defaults, attributeNamePrefix, 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 @@ -485,13 +546,13 @@ private Annotation getConvertsForAttribute(List elementsForProperty, XM return null; } - private Converts getConverts(Element tree, XMLContext.Default defaults) { + 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 ( tree != null ) { - applyXmlDefinedConverts( tree, defaults, null, convertAnnotationsMap ); + 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 @@ -511,20 +572,19 @@ private Converts getConverts(Element tree, XMLContext.Default defaults) { } private void applyXmlDefinedConverts( - Element containingElement, + List elements, XMLContext.Default defaults, String attributeNamePrefix, Map convertAnnotationsMap) { - final List convertElements = containingElement.elements( "convert" ); - for ( Element convertElement : convertElements ) { + for ( JaxbConvert convertElement : elements ) { final AnnotationDescriptor convertAnnotationDescriptor = new AnnotationDescriptor( Convert.class ); - copyStringAttribute( convertAnnotationDescriptor, convertElement, "attribute-name", false ); - copyBooleanAttribute( convertAnnotationDescriptor, convertElement, "disable-conversion" ); + copyAttribute( convertAnnotationDescriptor, "attribute-name", convertElement.getAttributeName(), false ); + copyAttribute( convertAnnotationDescriptor, "disable-conversion", convertElement.isDisableConversion(), false ); - final Attribute converterClassAttr = convertElement.attribute( "converter" ); - if ( converterClassAttr != null ) { + final String converter = convertElement.getConverter(); + if ( converter != null ) { final String converterClassName = XMLContext.buildSafeClassName( - converterClassAttr.getValue(), + converter, defaults ); try { @@ -584,7 +644,7 @@ private void applyPhysicalConvertAnnotations( } } - private void checkForOrphanProperties(Element tree) { + private void checkForOrphanProperties(ManagedType root) { Class clazz; try { clazz = classLoaderAccess.classForName( className ); @@ -592,9 +652,9 @@ private void checkForOrphanProperties(Element tree) { catch ( ClassLoadingException e ) { return; //a primitive type most likely } - Element element = tree != null ? tree.element( "attributes" ) : null; + AttributesContainer container = root != null ? root.getAttributes() : null; //put entity.attributes elements - if ( element != null ) { + if ( container != null ) { //precompute the list of properties //TODO is it really useful... Set properties = new HashSet<>(); @@ -610,12 +670,38 @@ else if ( name.startsWith( "is" ) ) { properties.add( Introspector.decapitalize( name.substring( "is".length() ) ) ); } } - for ( Element subelement : (List) element.elements() ) { - String propertyName = subelement.attributeValue( "name" ); - if ( !properties.contains( propertyName ) ) { - LOG.propertyNotFound( StringHelper.qualify( className, propertyName ) ); - } + 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 ) ); } } @@ -635,14 +721,14 @@ private Annotation addIfNotNull(List annotationList, Annotation anno } //TODO mutualize the next 2 methods - private Annotation getTableGenerator(List elementsForProperty, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - Element subelement = element != null ? element.element( annotationToXml.get( TableGenerator.class ) ) : null; + 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.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { return getPhysicalAnnotation( TableGenerator.class ); } else { @@ -650,14 +736,14 @@ private Annotation getTableGenerator(List elementsForProperty, XMLConte } } - private Annotation getSequenceGenerator(List elementsForProperty, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - Element subelement = element != null ? element.element( annotationToXml.get( SequenceGenerator.class ) ) : 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.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { return getPhysicalAnnotation( SequenceGenerator.class ); } else { @@ -667,43 +753,40 @@ private Annotation getSequenceGenerator(List elementsForProperty, XMLCo private void processEventAnnotations(List annotationList, XMLContext.Default defaults) { boolean eventElement = false; - for ( Element element : elementsForProperty ) { - String elementName = element.getName(); - if ( "pre-persist".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PrePersist.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "pre-remove".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PreRemove.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "pre-update".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PreUpdate.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "post-persist".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PostPersist.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "post-remove".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PostRemove.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "post-update".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PostUpdate.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } - else if ( "post-load".equals( elementName ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( PostLoad.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - eventElement = true; - } + 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 ); @@ -723,12 +806,12 @@ else if ( "post-load".equals( elementName ) ) { } } - private EntityListeners getEntityListeners(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( "entity-listeners" ) : null; + 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 ( Element subelement : (List) element.elements( "entity-listener" ) ) { - String className = subelement.attributeValue( "class" ); + for ( JaxbEntityListener subelement : element.getEntityListener() ) { + String className = subelement.getClazz(); try { entityListenerClasses.add( classLoaderAccess.classForName( @@ -738,7 +821,7 @@ private EntityListeners getEntityListeners(Element tree, XMLContext.Default defa } catch ( ClassLoadingException e ) { throw new AnnotationException( - "Unable to find " + element.getPath() + ".class: " + className, e + "Unable to find class: " + className, e ); } } @@ -844,39 +927,68 @@ else if ( annotationType == OneToOne.class ) { return annotation; } - private void getJoinTable(List annotationList, Element tree, XMLContext.Default defaults) { - addIfNotNull( annotationList, buildJoinTable( tree, defaults ) ); + private void getJoinTable(List annotationList, AssociationAttribute associationAttribute, + XMLContext.Default defaults) { + addIfNotNull( annotationList, buildJoinTable( associationAttribute.getJoinTable(), defaults ) ); } /* * no partial overriding possible */ - private JoinTable buildJoinTable(Element tree, XMLContext.Default defaults) { - Element subelement = tree == null ? null : tree.element( "join-table" ); + 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 ); - copyStringAttribute( annotation, subelement, "name", false ); - copyStringAttribute( annotation, subelement, "catalog", false ); + 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() ); } - copyStringAttribute( annotation, subelement, "schema", false ); + 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 ); - buildIndex( annotation, subelement ); - annotation.setValue( "joinColumns", getJoinColumns( subelement, false ) ); - annotation.setValue( "inverseJoinColumns", getJoinColumns( subelement, true ) ); + 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, @@ -886,40 +998,100 @@ private JoinTable buildJoinTable(Element tree, XMLContext.Default defaults) { * * @see #getElementCollection(List, XMLContext.Default) */ - private void getAssociation( - Class annotationType, List annotationList, XMLContext.Default defaults - ) { - String xmlName = annotationToXml.get( annotationType ); - for ( Element element : elementsForProperty ) { - if ( xmlName.equals( element.getName() ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( annotationType ); - addTargetClass( element, ad, "target-entity", defaults ); - getFetchType( ad, element ); - getCascades( ad, element, defaults ); - getJoinTable( annotationList, element, defaults ); - buildJoinColumns( annotationList, element ); - Annotation annotation = getPrimaryKeyJoinColumns( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - copyBooleanAttribute( ad, element, "optional" ); - copyBooleanAttribute( ad, element, "orphan-removal" ); - copyStringAttribute( ad, element, "mapped-by", false ); - getOrderBy( annotationList, element ); - getMapKey( annotationList, element ); - getMapKeyClass( annotationList, element, defaults ); - getMapKeyColumn( annotationList, element ); - getOrderColumn( annotationList, element ); - getMapKeyTemporal( annotationList, element ); - getMapKeyEnumerated( annotationList, element ); - annotation = getMapKeyAttributeOverrides( element, defaults ); - addIfNotNull( annotationList, annotation ); - buildMapKeyJoinColumns( annotationList, element ); - getAssociationId( annotationList, element ); - getMapsId( annotationList, element ); - annotationList.add( AnnotationFactory.create( ad ) ); - getAccessType( annotationList, element ); - } + 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() ); } - if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + 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 ); @@ -1016,8 +1188,8 @@ else if ( isPhysicalAnnotationPresent( ElementCollection.class ) ) { //JPA2 } } - private void buildMapKeyJoinColumns(List annotationList, Element element) { - MapKeyJoinColumn[] joinColumns = getMapKeyJoinColumns( element ); + 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 ); @@ -1025,37 +1197,36 @@ private void buildMapKeyJoinColumns(List annotationList, Element ele } } - private MapKeyJoinColumn[] getMapKeyJoinColumns(Element element) { - List subelements = element != null ? element.elements( "map-key-join-column" ) : null; + private MapKeyJoinColumn[] buildMapKeyJoinColumns(List elements) { List joinColumns = new ArrayList<>(); - if ( subelements != null ) { - for ( Element subelement : subelements ) { + if ( elements != null ) { + for ( JaxbMapKeyJoinColumn element : elements ) { AnnotationDescriptor column = new AnnotationDescriptor( MapKeyJoinColumn.class ); - copyStringAttribute( column, subelement, "name", false ); - copyStringAttribute( column, subelement, "referenced-column-name", false ); - copyBooleanAttribute( column, subelement, "unique" ); - copyBooleanAttribute( column, subelement, "nullable" ); - copyBooleanAttribute( column, subelement, "insertable" ); - copyBooleanAttribute( column, subelement, "updatable" ); - copyStringAttribute( column, subelement, "column-definition", false ); - copyStringAttribute( column, subelement, "table", false ); + 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(Element tree, XMLContext.Default defaults) { - List attributes = buildAttributeOverrides( tree, "map-key-attribute-override" ); + private AttributeOverrides getMapKeyAttributeOverrides(List elements, XMLContext.Default defaults) { + List attributes = buildAttributeOverrides( elements, "map-key-attribute-override" ); return mergeAttributeOverrides( defaults, attributes, false ); } - private Cacheable getCacheable(Element element, XMLContext.Default defaults){ - if ( element != null ) { - String attValue = element.attributeValue( "cacheable" ); + 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", Boolean.valueOf( attValue ) ); + ad.setValue( "value", attValue ); return AnnotationFactory.create( ad ); } } @@ -1071,12 +1242,10 @@ private Cacheable getCacheable(Element element, XMLContext.Default defaults){ * 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, Element element) { - Element subelement = element != null ? element.element( "map-key-enumerated" ) : null; - if ( subelement != null ) { + private void getMapKeyEnumerated(List annotationList, EnumType enumType) { + if ( enumType != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyEnumerated.class ); - EnumType value = EnumType.valueOf( subelement.getTextTrim() ); - ad.setValue( "value", value ); + ad.setValue( "value", enumType ); annotationList.add( AnnotationFactory.create( ad ) ); } } @@ -1086,12 +1255,10 @@ private void getMapKeyEnumerated(List annotationList, Element elemen * 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, Element element) { - Element subelement = element != null ? element.element( "map-key-temporal" ) : null; - if ( subelement != null ) { + private void getMapKeyTemporal(List annotationList, TemporalType temporalType) { + if ( temporalType != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyTemporal.class ); - TemporalType value = TemporalType.valueOf( subelement.getTextTrim() ); - ad.setValue( "value", value ); + ad.setValue( "value", temporalType ); annotationList.add( AnnotationFactory.create( ad ) ); } } @@ -1101,15 +1268,14 @@ private void getMapKeyTemporal(List annotationList, Element 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, Element element) { - Element subelement = element != null ? element.element( "order-column" ) : null; - if ( subelement != null ) { + private void getOrderColumn(List annotationList, JaxbOrderColumn element) { + if ( element != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( OrderColumn.class ); - copyStringAttribute( ad, subelement, "name", false ); - copyBooleanAttribute( ad, subelement, "nullable" ); - copyBooleanAttribute( ad, subelement, "insertable" ); - copyBooleanAttribute( ad, subelement, "updatable" ); - copyStringAttribute( ad, subelement, "column-definition", false ); + 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 ) ); } } @@ -1119,11 +1285,10 @@ private void getOrderColumn(List annotationList, Element element) { * maps-id attribute set. This should only be the case for many-to-one or one-to-one * associations. */ - private void getMapsId(List annotationList, Element element) { - String attrVal = element.attributeValue( "maps-id" ); - if ( attrVal != null ) { + private void getMapsId(List annotationList, String mapsId) { + if ( mapsId != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( MapsId.class ); - ad.setValue( "value", attrVal ); + ad.setValue( "value", mapsId ); annotationList.add( AnnotationFactory.create( ad ) ); } } @@ -1133,24 +1298,22 @@ private void getMapsId(List annotationList, Element element) { * attribute set to true. This should only be the case for many-to-one or one-to-one * associations. */ - private void getAssociationId(List annotationList, Element element) { - String attrVal = element.attributeValue( "id" ); - if ( "true".equals( attrVal ) ) { + 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(Element element, AnnotationDescriptor ad, String nodeName, XMLContext.Default defaults) { - String className = element.attributeValue( nodeName ); + private void addTargetClass(String className, AnnotationDescriptor ad, String nodeName, XMLContext.Default defaults) { if ( className != null ) { - Class clazz; + Class clazz; try { clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( className, defaults ) ); } catch ( ClassLoadingException e ) { throw new AnnotationException( - "Unable to find " + element.getPath() + " " + nodeName + ": " + className, e + "Unable to find " + nodeName + ": " + className, e ); } ad.setValue( getJavaAttributeNameFromXMLOne( nodeName ), clazz ); @@ -1165,82 +1328,76 @@ private void addTargetClass(Element element, AnnotationDescriptor ad, String nod * context. */ private void getElementCollection(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "element-collection".equals( element.getName() ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( ElementCollection.class ); - addTargetClass( element, ad, "target-class", defaults ); - getFetchType( ad, element ); - getOrderBy( annotationList, element ); - getOrderColumn( annotationList, element ); - getMapKey( annotationList, element ); - getMapKeyClass( annotationList, element, defaults ); - getMapKeyTemporal( annotationList, element ); - getMapKeyEnumerated( annotationList, element ); - getMapKeyColumn( annotationList, element ); - buildMapKeyJoinColumns( annotationList, element ); - Annotation annotation = getColumn( element.element( "column" ), false, element ); - addIfNotNull( annotationList, annotation ); - getTemporal( annotationList, element ); - getEnumerated( annotationList, element ); - getLob( annotationList, element ); - //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, "map-key-attribute-override" ) ); - attributes.addAll( buildAttributeOverrides( element, "attribute-override" ) ); - annotation = mergeAttributeOverrides( defaults, attributes, false ); - addIfNotNull( annotationList, annotation ); - annotation = getAssociationOverrides( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - getCollectionTable( annotationList, element, defaults ); - annotationList.add( AnnotationFactory.create( ad ) ); - getAccessType( annotationList, element ); - } + 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, Element element) { - Element subelement = element != null ? element.element( "order-by" ) : null; - if ( subelement != null ) { + private void getOrderBy(List annotationList, String orderBy) { + if ( orderBy != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( OrderBy.class ); - copyStringElement( subelement, ad, "value" ); + ad.setValue( "value", orderBy ); annotationList.add( AnnotationFactory.create( ad ) ); } } - private void getMapKey(List annotationList, Element element) { - Element subelement = element != null ? element.element( "map-key" ) : null; - if ( subelement != null ) { + private void getMapKey(List annotationList, JaxbMapKey element) { + if ( element != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( MapKey.class ); - copyStringAttribute( ad, subelement, "name", false ); + copyAttribute( ad, "name", element.getName(), false ); annotationList.add( AnnotationFactory.create( ad ) ); } } - private void getMapKeyColumn(List annotationList, Element element) { - Element subelement = element != null ? element.element( "map-key-column" ) : null; - if ( subelement != null ) { + private void getMapKeyColumn(List annotationList, JaxbMapKeyColumn element) { + if ( element != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyColumn.class ); - copyStringAttribute( ad, subelement, "name", false ); - copyBooleanAttribute( ad, subelement, "unique" ); - copyBooleanAttribute( ad, subelement, "nullable" ); - copyBooleanAttribute( ad, subelement, "insertable" ); - copyBooleanAttribute( ad, subelement, "updatable" ); - copyStringAttribute( ad, subelement, "column-definition", false ); - copyStringAttribute( ad, subelement, "table", false ); - copyIntegerAttribute( ad, subelement, "length" ); - copyIntegerAttribute( ad, subelement, "precision" ); - copyIntegerAttribute( ad, subelement, "scale" ); + 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, Element element, XMLContext.Default defaults) { + private void getMapKeyClass(List annotationList, JaxbMapKeyClass element, XMLContext.Default defaults) { String nodeName = "map-key-class"; - Element subelement = element != null ? element.element( nodeName ) : null; - if ( subelement != null ) { - String mapKeyClassName = subelement.attributeValue( "class" ); + if ( element != null ) { + String mapKeyClassName = element.getClazz(); AnnotationDescriptor ad = new AnnotationDescriptor( MapKeyClass.class ); if ( StringHelper.isNotEmpty( mapKeyClassName ) ) { Class clazz; @@ -1251,7 +1408,7 @@ private void getMapKeyClass(List annotationList, Element element, XM } catch ( ClassLoadingException e ) { throw new AnnotationException( - "Unable to find " + element.getPath() + " " + nodeName + ": " + mapKeyClassName, e + "Unable to find " + nodeName + ": " + mapKeyClassName, e ); } ad.setValue( "value", clazz ); @@ -1260,33 +1417,32 @@ private void getMapKeyClass(List annotationList, Element element, XM } } - private void getCollectionTable(List annotationList, Element element, XMLContext.Default defaults) { - Element subelement = element != null ? element.element( "collection-table" ) : null; - if ( subelement != null ) { + private void getCollectionTable(List annotationList, JaxbCollectionTable element, XMLContext.Default defaults) { + if ( element != null ) { AnnotationDescriptor annotation = new AnnotationDescriptor( CollectionTable.class ); - copyStringAttribute( annotation, subelement, "name", false ); - copyStringAttribute( annotation, subelement, "catalog", false ); + 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() ); } - copyStringAttribute( annotation, subelement, "schema", false ); + 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( subelement, false ); + JoinColumn[] joinColumns = getJoinColumns( element.getJoinColumn(), false ); if ( joinColumns.length > 0 ) { annotation.setValue( "joinColumns", joinColumns ); } - buildUniqueConstraints( annotation, subelement ); - buildIndex( annotation, subelement ); + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); annotationList.add( AnnotationFactory.create( annotation ) ); } } - private void buildJoinColumns(List annotationList, Element element) { - JoinColumn[] joinColumns = getJoinColumns( element, false ); + 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 ); @@ -1294,26 +1450,25 @@ private void buildJoinColumns(List annotationList, Element element) } } - private void getCascades(AnnotationDescriptor ad, Element element, XMLContext.Default defaults) { - List elements = element != null ? element.elements( "cascade" ) : new ArrayList<>( 0 ); + private void getCascades(AnnotationDescriptor ad, JaxbCascadeType element, XMLContext.Default defaults) { List cascades = new ArrayList<>(); - for ( Element subelement : elements ) { - if ( subelement.element( "cascade-all" ) != null ) { + if ( element != null ) { + if ( element.getCascadeAll() != null ) { cascades.add( CascadeType.ALL ); } - if ( subelement.element( "cascade-persist" ) != null ) { + if ( element.getCascadePersist() != null ) { cascades.add( CascadeType.PERSIST ); } - if ( subelement.element( "cascade-merge" ) != null ) { + if ( element.getCascadeMerge() != null ) { cascades.add( CascadeType.MERGE ); } - if ( subelement.element( "cascade-remove" ) != null ) { + if ( element.getCascadeRemove() != null ) { cascades.add( CascadeType.REMOVE ); } - if ( subelement.element( "cascade-refresh" ) != null ) { + if ( element.getCascadeRefresh() != null ) { cascades.add( CascadeType.REFRESH ); } - if ( subelement.element( "cascade-detach" ) != null ) { + if ( element.getCascadeDetach() != null ) { cascades.add( CascadeType.DETACH ); } } @@ -1327,18 +1482,16 @@ private void getCascades(AnnotationDescriptor ad, Element element, XMLContext.De } private void getEmbedded(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "embedded".equals( element.getName() ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( Embedded.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - Annotation annotation = getAttributeOverrides( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - annotation = getAssociationOverrides( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - getAccessType( annotationList, element ); - } + 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.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { Annotation annotation = getPhysicalAnnotation( Embedded.class ); if ( annotation != null ) { annotationList.add( annotation ); @@ -1355,13 +1508,11 @@ private void getEmbedded(List annotationList, XMLContext.Default def } private Transient getTransient(XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "transient".equals( element.getName() ) ) { - AnnotationDescriptor ad = new AnnotationDescriptor( Transient.class ); - return AnnotationFactory.create( ad ); - } + if ( !elementsForProperty.getTransient().isEmpty() ) { + AnnotationDescriptor ad = new AnnotationDescriptor( Transient.class ); + return AnnotationFactory.create( ad ); } - if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { return getPhysicalAnnotation( Transient.class ); } else { @@ -1370,17 +1521,15 @@ private Transient getTransient(XMLContext.Default defaults) { } private void getVersion(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "version".equals( element.getName() ) ) { - Annotation annotation = buildColumns( element ); - addIfNotNull( annotationList, annotation ); - getTemporal( annotationList, element ); - AnnotationDescriptor basic = new AnnotationDescriptor( Version.class ); - annotationList.add( AnnotationFactory.create( basic ) ); - getAccessType( annotationList, element ); - } + 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.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { //we have nothing, so Java annotations might occur Annotation annotation = getPhysicalAnnotation( Version.class ); if ( annotation != null ) { @@ -1396,21 +1545,19 @@ private void getVersion(List annotationList, XMLContext.Default defa } private void getBasic(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "basic".equals( element.getName() ) ) { - Annotation annotation = buildColumns( element ); - addIfNotNull( annotationList, annotation ); - getAccessType( annotationList, element ); - getTemporal( annotationList, element ); - getLob( annotationList, element ); - getEnumerated( annotationList, element ); - AnnotationDescriptor basic = new AnnotationDescriptor( Basic.class ); - getFetchType( basic, element ); - copyBooleanAttribute( basic, element, "optional" ); - annotationList.add( AnnotationFactory.create( basic ) ); - } - } - if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + 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 ); @@ -1435,58 +1582,38 @@ private void getBasic(List annotationList, XMLContext.Default defaul } } - private void getEnumerated(List annotationList, Element element) { - Element subElement = element != null ? element.element( "enumerated" ) : null; - if ( subElement != null ) { + private void getEnumerated(List annotationList, EnumType enumType) { + if ( enumType != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( Enumerated.class ); - String enumerated = subElement.getTextTrim(); - if ( "ORDINAL".equalsIgnoreCase( enumerated ) ) { - ad.setValue( "value", EnumType.ORDINAL ); - } - else if ( "STRING".equalsIgnoreCase( enumerated ) ) { - ad.setValue( "value", EnumType.STRING ); - } - else if ( StringHelper.isNotEmpty( enumerated ) ) { - throw new AnnotationException( "Unknown EnumType: " + enumerated + ". " + SCHEMA_VALIDATION ); - } + ad.setValue( "value", enumType ); annotationList.add( AnnotationFactory.create( ad ) ); } } - private void getLob(List annotationList, Element element) { - Element subElement = element != null ? element.element( "lob" ) : null; - if ( subElement != null ) { + private void getLob(List annotationList, JaxbLob element) { + if ( element != null ) { annotationList.add( AnnotationFactory.create( new AnnotationDescriptor( Lob.class ) ) ); } } - private void getFetchType(AnnotationDescriptor descriptor, Element element) { - String fetchString = element != null ? element.attributeValue( "fetch" ) : null; - if ( fetchString != null ) { - if ( "eager".equalsIgnoreCase( fetchString ) ) { - descriptor.setValue( "fetch", FetchType.EAGER ); - } - else if ( "lazy".equalsIgnoreCase( fetchString ) ) { - descriptor.setValue( "fetch", FetchType.LAZY ); - } + private void getFetchType(AnnotationDescriptor descriptor, FetchType type) { + if ( type != null ) { + descriptor.setValue( "fetch", type ); } } private void getEmbeddedId(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "embedded-id".equals( element.getName() ) ) { - if ( isProcessingId( defaults ) ) { - Annotation annotation = getAttributeOverrides( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - annotation = getAssociationOverrides( element, defaults, false ); - addIfNotNull( annotationList, annotation ); - AnnotationDescriptor ad = new AnnotationDescriptor( EmbeddedId.class ); - annotationList.add( AnnotationFactory.create( ad ) ); - getAccessType( annotationList, element ); - } + 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.size() == 0 && defaults.canUseJavaAnnotations() ) { + if ( elementsForProperty.isEmpty() && defaults.canUseJavaAnnotations() ) { Annotation annotation = getPhysicalAnnotation( EmbeddedId.class ); if ( annotation != null ) { annotationList.add( annotation ); @@ -1514,49 +1641,42 @@ private void getEmbeddedId(List annotationList, XMLContext.Default d } } - private void preCalculateElementsForProperty(Element tree) { - elementsForProperty = new ArrayList<>(); - Element element = tree != null ? tree.element( "attributes" ) : null; + private void preCalculateElementsForProperty(ManagedType managedType, JaxbEntityListener entityListener) { + elementsForProperty = new PropertyMappingElementCollector( propertyName ); + AttributesContainer attributes = managedType == null ? null : managedType.getAttributes(); //put entity.attributes elements - if ( element != null ) { - for ( Element subelement : (List) element.elements() ) { - if ( propertyName.equals( subelement.attributeValue( "name" ) ) ) { - elementsForProperty.add( subelement ); - } - } + if ( attributes != null ) { + elementsForProperty.collectPersistentAttributesIfMatching( attributes ); } //add pre-* etc from entity and pure entity listener classes - if ( tree != null ) { - for ( Element subelement : (List) tree.elements() ) { - if ( propertyName.equals( subelement.attributeValue( "method-name" ) ) ) { - elementsForProperty.add( subelement ); - } - } + if ( managedType instanceof LifecycleCallbackContainer ) { + elementsForProperty.collectLifecycleCallbacksIfMatching( (LifecycleCallbackContainer) managedType ); + } + if ( entityListener != null ) { + elementsForProperty.collectLifecycleCallbacksIfMatching( entityListener ); } } private void getId(List annotationList, XMLContext.Default defaults) { - for ( Element element : elementsForProperty ) { - if ( "id".equals( element.getName() ) ) { - boolean processId = isProcessingId( defaults ); - if ( processId ) { - Annotation annotation = buildColumns( element ); - addIfNotNull( annotationList, annotation ); - annotation = buildGeneratedValue( element ); - addIfNotNull( annotationList, annotation ); - getTemporal( annotationList, element ); - //FIXME: fix the priority of xml over java for generator names - annotation = getTableGenerator( element, defaults ); - addIfNotNull( annotationList, annotation ); - annotation = getSequenceGenerator( element, defaults ); - addIfNotNull( annotationList, annotation ); - AnnotationDescriptor id = new AnnotationDescriptor( Id.class ); - annotationList.add( AnnotationFactory.create( id ) ); - getAccessType( annotationList, element ); - } - } - } - if ( elementsForProperty.size() == 0 && defaults.canUseJavaAnnotations() ) { + 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 ); @@ -1602,11 +1722,26 @@ private boolean isProcessingId(XMLContext.Default defaults) { return correctAccess || ( !isExplicit && hasId ) || ( !isExplicit && propertyIsDefault ); } - private Columns buildColumns(Element element) { - List subelements = element.elements( "column" ); - List columns = new ArrayList<>( subelements.size() ); - for ( Element subelement : subelements ) { - columns.add( getColumn( subelement, false, element ) ); + 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 ); @@ -1618,27 +1753,14 @@ private Columns buildColumns(Element element) { } } - private GeneratedValue buildGeneratedValue(Element element) { - Element subElement = element != null ? element.element( "generated-value" ) : null; - if ( subElement != null ) { + private GeneratedValue buildGeneratedValue(JaxbGeneratedValue element) { + if ( element != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( GeneratedValue.class ); - String strategy = subElement.attributeValue( "strategy" ); - if ( "TABLE".equalsIgnoreCase( strategy ) ) { - ad.setValue( "strategy", GenerationType.TABLE ); - } - else if ( "SEQUENCE".equalsIgnoreCase( strategy ) ) { - ad.setValue( "strategy", GenerationType.SEQUENCE ); + GenerationType strategy = element.getStrategy(); + if ( strategy != null ) { + ad.setValue( "strategy", strategy ); } - else if ( "IDENTITY".equalsIgnoreCase( strategy ) ) { - ad.setValue( "strategy", GenerationType.IDENTITY ); - } - else if ( "AUTO".equalsIgnoreCase( strategy ) ) { - ad.setValue( "strategy", GenerationType.AUTO ); - } - else if ( StringHelper.isNotEmpty( strategy ) ) { - throw new AnnotationException( "Unknown GenerationType: " + strategy + ". " + SCHEMA_VALIDATION ); - } - copyStringAttribute( ad, subElement, "generator", false ); + copyAttribute( ad, "generator", element.getGenerator(), false ); return AnnotationFactory.create( ad ); } else { @@ -1646,41 +1768,20 @@ else if ( StringHelper.isNotEmpty( strategy ) ) { } } - private void getTemporal(List annotationList, Element element) { - Element subElement = element != null ? element.element( "temporal" ) : null; - if ( subElement != null ) { + private void getTemporal(List annotationList, TemporalType type) { + if ( type != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( Temporal.class ); - String temporal = subElement.getTextTrim(); - if ( "DATE".equalsIgnoreCase( temporal ) ) { - ad.setValue( "value", TemporalType.DATE ); - } - else if ( "TIME".equalsIgnoreCase( temporal ) ) { - ad.setValue( "value", TemporalType.TIME ); - } - else if ( "TIMESTAMP".equalsIgnoreCase( temporal ) ) { - ad.setValue( "value", TemporalType.TIMESTAMP ); - } - else if ( StringHelper.isNotEmpty( temporal ) ) { - throw new AnnotationException( "Unknown TemporalType: " + temporal + ". " + SCHEMA_VALIDATION ); - } + ad.setValue( "value", type ); annotationList.add( AnnotationFactory.create( ad ) ); } } - private void getAccessType(List annotationList, Element element) { + private void getAccessType(List annotationList, AccessType type) { if ( element == null ) { return; } - String access = element.attributeValue( "access" ); - if ( access != null ) { + if ( type != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( Access.class ); - AccessType type; - try { - type = AccessType.valueOf( access ); - } - catch ( IllegalArgumentException e ) { - throw new AnnotationException( access + " is not a valid access type. Check you xml confguration." ); - } if ( ( AccessType.PROPERTY.equals( type ) && this.element instanceof Method ) || ( AccessType.FIELD.equals( type ) && this.element instanceof Field ) ) { @@ -1692,14 +1793,20 @@ private void getAccessType(List annotationList, Element element) { } } + 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 - * with annotations is never allowed. */ - private AssociationOverrides getAssociationOverrides(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) { - List attributes = buildAssociationOverrides( tree, defaults ); + 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 ); @@ -1720,15 +1827,14 @@ private AssociationOverrides getAssociationOverrides(Element tree, XMLContext.De } } - private List buildAssociationOverrides(Element element, XMLContext.Default defaults) { - List subelements = element == null ? null : element.elements( "association-override" ); + private List buildAssociationOverrides(List elements, XMLContext.Default defaults) { List overrides = new ArrayList<>(); - if ( subelements != null && subelements.size() > 0 ) { - for ( Element current : subelements ) { + if ( elements != null && elements.size() > 0 ) { + for ( JaxbAssociationOverride current : elements ) { AnnotationDescriptor override = new AnnotationDescriptor( AssociationOverride.class ); - copyStringAttribute( override, current, "name", true ); - override.setValue( "joinColumns", getJoinColumns( current, false ) ); - JoinTable joinTable = buildJoinTable( current, defaults ); + 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 ); } @@ -1738,22 +1844,19 @@ private List buildAssociationOverrides(Element element, XML return overrides; } - private JoinColumn[] getJoinColumns(Element element, boolean isInverse) { - List subelements = element != null ? - element.elements( isInverse ? "inverse-join-column" : "join-column" ) : - null; + private JoinColumn[] getJoinColumns(List subelements, boolean isInverse) { List joinColumns = new ArrayList<>(); if ( subelements != null ) { - for ( Element subelement : subelements ) { + for ( JaxbJoinColumn subelement : subelements ) { AnnotationDescriptor column = new AnnotationDescriptor( JoinColumn.class ); - copyStringAttribute( column, subelement, "name", false ); - copyStringAttribute( column, subelement, "referenced-column-name", false ); - copyBooleanAttribute( column, subelement, "unique" ); - copyBooleanAttribute( column, subelement, "nullable" ); - copyBooleanAttribute( column, subelement, "insertable" ); - copyBooleanAttribute( column, subelement, "updatable" ); - copyStringAttribute( column, subelement, "column-definition", false ); - copyStringAttribute( column, subelement, "table", false ); + 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 ) ); } } @@ -1776,14 +1879,21 @@ private void addAssociationOverrideIfNeeded(AssociationOverride annotation, List } } + 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(Element tree, XMLContext.Default defaults, boolean mergeWithAnnotations) { - List attributes = buildAttributeOverrides( tree, "attribute-override" ); + private AttributeOverrides getAttributeOverrides(List elements, XMLContext.Default defaults, + boolean mergeWithAnnotations) { + List attributes = buildAttributeOverrides( elements, "attribute-override" ); return mergeAttributeOverrides( defaults, attributes, mergeWithAnnotations ); } @@ -1814,47 +1924,38 @@ private AttributeOverrides mergeAttributeOverrides(XMLContext.Default defaults, } } - private List buildAttributeOverrides(Element element, String nodeName) { - List subelements = element == null ? null : element.elements( nodeName ); - return buildAttributeOverrides( subelements, nodeName ); - } - - private List buildAttributeOverrides(List subelements, String nodeName) { + private List buildAttributeOverrides(List subelements, String nodeName) { List overrides = new ArrayList<>(); if ( subelements != null && subelements.size() > 0 ) { - for ( Element current : subelements ) { - if ( !current.getName().equals( nodeName ) ) { - continue; - } + for ( JaxbAttributeOverride current : subelements ) { AnnotationDescriptor override = new AnnotationDescriptor( AttributeOverride.class ); - copyStringAttribute( override, current, "name", true ); - Element column = current.element( "column" ); - override.setValue( "column", getColumn( column, true, current ) ); + 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(Element element, boolean isMandatory, Element current) { - //Element subelement = element != null ? element.element( "column" ) : null; + private Column getColumn(JaxbColumn element, boolean isMandatory, String nodeName) { if ( element != null ) { AnnotationDescriptor column = new AnnotationDescriptor( Column.class ); - copyStringAttribute( column, element, "name", false ); - copyBooleanAttribute( column, element, "unique" ); - copyBooleanAttribute( column, element, "nullable" ); - copyBooleanAttribute( column, element, "insertable" ); - copyBooleanAttribute( column, element, "updatable" ); - copyStringAttribute( column, element, "column-definition", false ); - copyStringAttribute( column, element, "table", false ); - copyIntegerAttribute( column, element, "length" ); - copyIntegerAttribute( column, element, "precision" ); - copyIntegerAttribute( column, element, "scale" ); + 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( current.getPath() + ".column is mandatory. " + SCHEMA_VALIDATION ); + throw new AnnotationException( nodeName + ".column is mandatory. " + SCHEMA_VALIDATION ); } return null; } @@ -1876,18 +1977,11 @@ private void addAttributeOverrideIfNeeded(AttributeOverride annotation, List clazz, Element element, XMLContext.Default defaults - ) { - Element subelement = element == null ? null : element.element( annotationToXml.get( clazz ) ); - if ( subelement != null ) { + private Annotation getMarkerAnnotation(Class clazz, JaxbEmptyType element, + XMLContext.Default defaults) { + if ( element != null ) { return AnnotationFactory.create( new AnnotationDescriptor( clazz ) ); } else if ( defaults.canUseJavaAnnotations() ) { @@ -1927,8 +2027,10 @@ else if ( defaults.canUseJavaAnnotations() ) { } } - private SqlResultSetMappings getSqlResultSetMappings(Element tree, XMLContext.Default defaults) { - List results = buildSqlResultsetMappings( tree, defaults, classLoaderAccess ); + 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 ); @@ -1950,26 +2052,18 @@ private SqlResultSetMappings getSqlResultSetMappings(Element tree, XMLContext.De } public static List buildNamedEntityGraph( - Element element, + List elements, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { - if ( element == null ) { - return new ArrayList<>(); - } List namedEntityGraphList = new ArrayList<>(); - List namedEntityGraphElements = element.elements( "named-entity-graph" ); - for ( Element subElement : namedEntityGraphElements ) { + for ( JaxbNamedEntityGraph element : elements ) { AnnotationDescriptor ann = new AnnotationDescriptor( NamedEntityGraph.class ); - copyStringAttribute( ann, subElement, "name", false ); - copyBooleanAttribute( ann, subElement, "include-all-attributes" ); - bindNamedAttributeNodes( subElement, ann ); + copyAttribute( ann, "name", element.getName(), false ); + copyAttribute( ann, "include-all-attributes", element.isIncludeAllAttributes(), false ); + bindNamedAttributeNodes( element.getNamedAttributeNode(), ann ); - List subgraphNodes = subElement.elements( "subgraph" ); - List subclassSubgraphNodes = subElement.elements( "subclass-subgraph" ); - if(!subclassSubgraphNodes.isEmpty()) { - subgraphNodes.addAll( subclassSubgraphNodes ); - } - bindNamedSubgraph( defaults, ann, subgraphNodes, classLoaderAccess ); + bindNamedSubgraph( defaults, ann, "subgraphs", element.getSubgraph(), classLoaderAccess ); + bindNamedSubgraph( defaults, ann, "subclass-subgraph", element.getSubclassSubgraph(), classLoaderAccess ); namedEntityGraphList.add( AnnotationFactory.create( ann ) ); } //TODO @@ -1979,13 +2073,14 @@ public static List buildNamedEntityGraph( private static void bindNamedSubgraph( XMLContext.Default defaults, AnnotationDescriptor ann, - List subgraphNodes, + String annotationAttributeName, + List subgraphNodes, ClassLoaderAccess classLoaderAccess) { List annSubgraphNodes = new ArrayList<>( ); - for(Element subgraphNode : subgraphNodes){ + for(JaxbNamedSubgraph subgraphNode : subgraphNodes){ AnnotationDescriptor annSubgraphNode = new AnnotationDescriptor( NamedSubgraph.class ); - copyStringAttribute( annSubgraphNode, subgraphNode, "name", true ); - String clazzName = subgraphNode.attributeValue( "class" ); + copyAttribute( annSubgraphNode, "name", subgraphNode.getName(), true ); + String clazzName = subgraphNode.getClazz(); Class clazz; try { clazz = classLoaderAccess.classForName( @@ -1996,56 +2091,49 @@ private static void bindNamedSubgraph( throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); } annSubgraphNode.setValue( "type", clazz ); - bindNamedAttributeNodes(subgraphNode, annSubgraphNode); + bindNamedAttributeNodes(subgraphNode.getNamedAttributeNode(), annSubgraphNode); annSubgraphNodes.add( AnnotationFactory.create( annSubgraphNode ) ); } - ann.setValue( "subgraphs", annSubgraphNodes.toArray( new NamedSubgraph[annSubgraphNodes.size()] ) ); + ann.setValue( annotationAttributeName, annSubgraphNodes.toArray( new NamedSubgraph[annSubgraphNodes.size()] ) ); } - private static void bindNamedAttributeNodes(Element subElement, AnnotationDescriptor ann) { - List namedAttributeNodes = subElement.elements("named-attribute-node"); + private static void bindNamedAttributeNodes(List elements, AnnotationDescriptor ann) { List annNamedAttributeNodes = new ArrayList<>( ); - for(Element namedAttributeNode : namedAttributeNodes){ + for( JaxbNamedAttributeNode element : elements){ AnnotationDescriptor annNamedAttributeNode = new AnnotationDescriptor( NamedAttributeNode.class ); - copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "value", "name", true ); - copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "subgraph", false ); - copyStringAttribute( annNamedAttributeNode, namedAttributeNode, "key-subgraph", false ); + 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( - Element element, + List elements, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { - if ( element == null ) { - return new ArrayList<>(); - } - List namedStoredProcedureElements = element.elements( "named-stored-procedure-query" ); List namedStoredProcedureQueries = new ArrayList<>(); - for ( Object obj : namedStoredProcedureElements ) { - Element subElement = (Element) obj; + for ( JaxbNamedStoredProcedureQuery element : elements ) { AnnotationDescriptor ann = new AnnotationDescriptor( NamedStoredProcedureQuery.class ); - copyStringAttribute( ann, subElement, "name", true ); - copyStringAttribute( ann, subElement, "procedure-name", true ); + copyAttribute( ann, "name", element.getName(), true ); + copyAttribute( ann, "procedure-name", element.getProcedureName(), true ); - List elements = subElement.elements( "parameter" ); List storedProcedureParameters = new ArrayList<>(); - for ( Element parameterElement : elements ) { + for ( JaxbStoredProcedureParameter parameterElement : element.getParameter() ) { AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class ); - copyStringAttribute( parameterDescriptor, parameterElement, "name", false ); - String modeValue = parameterElement.attributeValue( "mode" ); + copyAttribute( parameterDescriptor, "name", parameterElement.getName(), false ); + ParameterMode modeValue = parameterElement.getMode(); if ( modeValue == null ) { parameterDescriptor.setValue( "mode", ParameterMode.IN ); } else { - parameterDescriptor.setValue( "mode", ParameterMode.valueOf( modeValue.toUpperCase(Locale.ROOT) ) ); + parameterDescriptor.setValue( "mode", modeValue ); } - String clazzName = parameterElement.attributeValue( "class" ); - Class clazz; + String clazzName = parameterElement.getClazz(); + Class clazz; try { clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( clazzName, defaults ) @@ -2063,11 +2151,9 @@ public static List buildNamedStoreProcedureQueries( storedProcedureParameters.toArray( new StoredProcedureParameter[storedProcedureParameters.size()] ) ); - elements = subElement.elements( "result-class" ); - List returnClasses = new ArrayList<>(); - for ( Element classElement : elements ) { - String clazzName = classElement.getTextTrim(); - Class clazz; + List> returnClasses = new ArrayList<>(); + for ( String clazzName : element.getResultClass() ) { + Class clazz; try { clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( clazzName, defaults ) @@ -2081,14 +2167,8 @@ public static List buildNamedStoreProcedureQueries( ann.setValue( "resultClasses", returnClasses.toArray( new Class[returnClasses.size()] ) ); - elements = subElement.elements( "result-set-mapping" ); - List resultSetMappings = new ArrayList<>(); - for ( Element resultSetMappingElement : elements ) { - resultSetMappings.add( resultSetMappingElement.getTextTrim() ); - } - ann.setValue( "resultSetMappings", resultSetMappings.toArray( new String[resultSetMappings.size()] ) ); - elements = subElement.elements( "hint" ); - buildQueryHints( elements, ann ); + ann.setValue( "resultSetMappings", element.getResultSetMapping().toArray( new String[0] ) ); + buildQueryHints( element.getHint(), ann ); namedStoredProcedureQueries.add( AnnotationFactory.create( ann ) ); } return namedStoredProcedureQueries; @@ -2096,20 +2176,15 @@ public static List buildNamedStoreProcedureQueries( } public static List buildSqlResultsetMappings( - Element element, + List elements, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { final List builtResultSetMappings = new ArrayList<>(); - if ( element == null ) { - return builtResultSetMappings; - } // iterate over each element - for ( Object resultSetMappingElementObject : element.elements( "sql-result-set-mapping" ) ) { - final Element resultSetMappingElement = (Element) resultSetMappingElementObject; - + for ( JaxbSqlResultSetMapping resultSetMappingElement : elements ) { final AnnotationDescriptor resultSetMappingAnnotation = new AnnotationDescriptor( SqlResultSetMapping.class ); - copyStringAttribute( resultSetMappingAnnotation, resultSetMappingElement, "name", true ); + copyAttribute( resultSetMappingAnnotation, "name", resultSetMappingElement.getName(), true ); // iterate over the sub-elements, which should include: // * @@ -2120,48 +2195,24 @@ public static List buildSqlResultsetMappings( List columnResultAnnotations = null; List constructorResultAnnotations = null; - for ( Object resultElementObject : resultSetMappingElement.elements() ) { - final Element resultElement = (Element) resultElementObject; - - if ( "entity-result".equals( resultElement.getName() ) ) { - if ( entityResultAnnotations == null ) { - entityResultAnnotations = new ArrayList<>(); - } - // process the - entityResultAnnotations.add( buildEntityResult( resultElement, defaults, classLoaderAccess ) ); + for ( JaxbEntityResult resultElement : resultSetMappingElement.getEntityResult() ) { + if ( entityResultAnnotations == null ) { + entityResultAnnotations = new ArrayList<>(); } - else if ( "column-result".equals( resultElement.getName() ) ) { - if ( columnResultAnnotations == null ) { - columnResultAnnotations = new ArrayList<>(); - } - columnResultAnnotations.add( buildColumnResult( resultElement, defaults, classLoaderAccess ) ); - } - else if ( "constructor-result".equals( resultElement.getName() ) ) { - if ( constructorResultAnnotations == null ) { - constructorResultAnnotations = new ArrayList<>(); - } - constructorResultAnnotations.add( buildConstructorResult( resultElement, defaults, classLoaderAccess ) ); + // process the + entityResultAnnotations.add( buildEntityResult( resultElement, defaults, classLoaderAccess ) ); + } + for ( JaxbColumnResult resultElement : resultSetMappingElement.getColumnResult() ) { + if ( columnResultAnnotations == null ) { + columnResultAnnotations = new ArrayList<>(); } - else { - // most likely the this code used to handle. I have left the code here, - // but commented it out for now. I'll just log a warning for now. - LOG.debug( "Encountered unrecognized sql-result-set-mapping sub-element : " + resultElement.getName() ); - -// String clazzName = subelement.attributeValue( "result-class" ); -// if ( StringHelper.isNotEmpty( clazzName ) ) { -// Class clazz; -// try { -// clazz = ReflectHelper.classForName( -// XMLContext.buildSafeClassName( clazzName, defaults ), -// JPAOverriddenAnnotationReader.class -// ); -// } -// catch ( ClassNotFoundException e ) { -// throw new AnnotationException( "Unable to find entity-class: " + clazzName, e ); -// } -// ann.setValue( "resultClass", clazz ); -// } + 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() ) { @@ -2183,10 +2234,6 @@ else if ( "constructor-result".equals( resultElement.getName() ) ) { ); } - - // this was part of the old code too, but could never figure out what it is supposed to do... - // copyStringAttribute( ann, subelement, "result-set-mapping", false ); - builtResultSetMappings.add( AnnotationFactory.create( resultSetMappingAnnotation ) ); } @@ -2194,22 +2241,22 @@ else if ( "constructor-result".equals( resultElement.getName() ) ) { } private static EntityResult buildEntityResult( - Element entityResultElement, + JaxbEntityResult entityResultElement, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { final AnnotationDescriptor entityResultDescriptor = new AnnotationDescriptor( EntityResult.class ); - final Class entityClass = resolveClassReference( entityResultElement.attributeValue( "entity-class" ), defaults, classLoaderAccess ); + final Class entityClass = resolveClassReference( entityResultElement.getEntityClass(), defaults, classLoaderAccess ); entityResultDescriptor.setValue( "entityClass", entityClass ); - copyStringAttribute( entityResultDescriptor, entityResultElement, "discriminator-column", false ); + copyAttribute( entityResultDescriptor, "discriminator-column", entityResultElement.getDiscriminatorColumn(), false ); // process the sub-elements List fieldResultAnnotations = new ArrayList<>(); - for ( Element fieldResult : (List) entityResultElement.elements( "field-result" ) ) { + for ( JaxbFieldResult fieldResult : entityResultElement.getFieldResult() ) { AnnotationDescriptor fieldResultDescriptor = new AnnotationDescriptor( FieldResult.class ); - copyStringAttribute( fieldResultDescriptor, fieldResult, "name", true ); - copyStringAttribute( fieldResultDescriptor, fieldResult, "column", true ); + copyAttribute( fieldResultDescriptor, "name", fieldResult.getName(), true ); + copyAttribute( fieldResultDescriptor, "column", fieldResult.getColumn(), true ); fieldResultAnnotations.add( AnnotationFactory.create( fieldResultDescriptor ) ); } entityResultDescriptor.setValue( @@ -2236,16 +2283,12 @@ private static Class resolveClassReference( } private static ColumnResult buildColumnResult( - Element columnResultElement, + JaxbColumnResult columnResultElement, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { -// AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor( ColumnResult.class ); -// copyStringAttribute( columnResultDescriptor, columnResultElement, "name", true ); -// return AnnotationFactory.create( columnResultDescriptor ); - AnnotationDescriptor columnResultDescriptor = new AnnotationDescriptor( ColumnResult.class ); - copyStringAttribute( columnResultDescriptor, columnResultElement, "name", true ); - final String columnTypeName = columnResultElement.attributeValue( "class" ); + copyAttribute( columnResultDescriptor, "name", columnResultElement.getName(), true ); + final String columnTypeName = columnResultElement.getClazz(); if ( StringHelper.isNotEmpty( columnTypeName ) ) { columnResultDescriptor.setValue( "type", resolveClassReference( columnTypeName, defaults, classLoaderAccess ) ); } @@ -2253,16 +2296,16 @@ private static ColumnResult buildColumnResult( } private static ConstructorResult buildConstructorResult( - Element constructorResultElement, + JaxbConstructorResult constructorResultElement, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { AnnotationDescriptor constructorResultDescriptor = new AnnotationDescriptor( ConstructorResult.class ); - final Class entityClass = resolveClassReference( constructorResultElement.attributeValue( "target-class" ), defaults, classLoaderAccess ); + final Class entityClass = resolveClassReference( constructorResultElement.getTargetClass(), defaults, classLoaderAccess ); constructorResultDescriptor.setValue( "targetClass", entityClass ); List columnResultAnnotations = new ArrayList<>(); - for ( Element columnResultElement : (List) constructorResultElement.elements( "column" ) ) { + for ( JaxbColumnResult columnResultElement : constructorResultElement.getColumn() ) { columnResultAnnotations.add( buildColumnResult( columnResultElement, defaults, classLoaderAccess ) ); } constructorResultDescriptor.setValue( @@ -2289,9 +2332,11 @@ private void addSqlResultsetMappingIfNeeded(SqlResultSetMapping annotation, List } } - private NamedQueries getNamedQueries(Element tree, XMLContext.Default defaults) { + private NamedQueries getNamedQueries(ManagedType root, XMLContext.Default defaults) { //TODO avoid the Proxy Creation (@NamedQueries) when possible - List queries = (List) buildNamedQueries( tree, false, defaults, classLoaderAccess ); + List queries = root instanceof JaxbEntity + ? buildNamedQueries( ( (JaxbEntity) root ).getNamedQuery(), defaults, classLoaderAccess ) + : new ArrayList<>(); if ( defaults.canUseJavaAnnotations() ) { NamedQuery annotation = getPhysicalAnnotation( NamedQuery.class ); addNamedQueryIfNeeded( annotation, queries ); @@ -2328,8 +2373,10 @@ private void addNamedQueryIfNeeded(NamedQuery annotation, List queri } } - private NamedEntityGraphs getNamedEntityGraphs(Element tree, XMLContext.Default defaults) { - List queries = buildNamedEntityGraph( tree, defaults, classLoaderAccess ); + 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 ); @@ -2367,8 +2414,10 @@ private void addNamedEntityGraphIfNeeded(NamedEntityGraph annotation, List queries = buildNamedStoreProcedureQueries( tree, defaults, classLoaderAccess ); + 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 ); @@ -2407,9 +2456,11 @@ private void addNamedStoredProcedureQueryIfNeeded(NamedStoredProcedureQuery anno private NamedNativeQueries getNamedNativeQueries( - Element tree, + ManagedType root, XMLContext.Default defaults) { - List queries = (List) buildNamedQueries( tree, true, defaults, classLoaderAccess ); + List queries = root instanceof JaxbEntity + ? buildNamedNativeQueries( ( (JaxbEntity) root ).getNamedNativeQuery(), defaults, classLoaderAccess ) + : new ArrayList<>(); if ( defaults.canUseJavaAnnotations() ) { NamedNativeQuery annotation = getPhysicalAnnotation( NamedNativeQuery.class ); addNamedNativeQueryIfNeeded( annotation, queries ); @@ -2446,16 +2497,16 @@ private void addNamedNativeQueryIfNeeded(NamedNativeQuery annotation, List elements, AnnotationDescriptor ann){ + private static void buildQueryHints(List elements, AnnotationDescriptor ann){ List queryHints = new ArrayList<>( elements.size() ); - for ( Element hint : elements ) { + for ( JaxbQueryHint hint : elements ) { AnnotationDescriptor hintDescriptor = new AnnotationDescriptor( QueryHint.class ); - String value = hint.attributeValue( "name" ); + String value = hint.getName(); if ( value == null ) { throw new AnnotationException( " without name. " + SCHEMA_VALIDATION ); } hintDescriptor.setValue( "name", value ); - value = hint.attributeValue( "value" ); + value = hint.getValue(); if ( value == null ) { throw new AnnotationException( " without value. " + SCHEMA_VALIDATION ); } @@ -2465,32 +2516,33 @@ private static void buildQueryHints(List elements, AnnotationDescriptor ann.setValue( "hints", queryHints.toArray( new QueryHint[queryHints.size()] ) ); } - public static List buildNamedQueries( - Element element, - boolean isNative, + public static List buildNamedQueries( + List elements, XMLContext.Default defaults, ClassLoaderAccess classLoaderAccess) { - if ( element == null ) { - return new ArrayList(); - } - List namedQueryElementList = isNative ? - element.elements( "named-native-query" ) : - element.elements( "named-query" ); - List namedQueries = new ArrayList(); - for ( Object aNamedQueryElementList : namedQueryElementList ) { - Element subelement = (Element) aNamedQueryElementList; - AnnotationDescriptor ann = new AnnotationDescriptor( - isNative ? NamedNativeQuery.class : NamedQuery.class - ); - copyStringAttribute( ann, subelement, "name", false ); - Element queryElt = subelement.element( "query" ); - if ( queryElt == null ) { - throw new AnnotationException( "No element found." + SCHEMA_VALIDATION ); - } - copyStringElement( queryElt, ann, "query" ); - List elements = subelement.elements( "hint" ); - buildQueryHints( elements, ann ); - String clazzName = subelement.attributeValue( "result-class" ); + 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 { @@ -2503,14 +2555,17 @@ public static List buildNamedQueries( } ann.setValue( "resultClass", clazz ); } - copyStringAttribute( ann, subelement, "result-set-mapping", false ); + copyAttribute( ann, "result-set-mapping", element.getResultSetMapping(), false ); namedQueries.add( AnnotationFactory.create( ann ) ); } return namedQueries; } - private TableGenerator getTableGenerator(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( annotationToXml.get( TableGenerator.class ) ) : null; + 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 ); } @@ -2548,18 +2603,18 @@ else if ( defaults.canUseJavaAnnotations() && isPhysicalAnnotationPresent( Table } } - public static TableGenerator buildTableGeneratorAnnotation(Element element, XMLContext.Default defaults) { + public static TableGenerator buildTableGeneratorAnnotation(JaxbTableGenerator element, XMLContext.Default defaults) { AnnotationDescriptor ad = new AnnotationDescriptor( TableGenerator.class ); - copyStringAttribute( ad, element, "name", false ); - copyStringAttribute( ad, element, "table", false ); - copyStringAttribute( ad, element, "catalog", false ); - copyStringAttribute( ad, element, "schema", false ); - copyStringAttribute( ad, element, "pk-column-name", false ); - copyStringAttribute( ad, element, "value-column-name", false ); - copyStringAttribute( ad, element, "pk-column-value", false ); - copyIntegerAttribute( ad, element, "initial-value" ); - copyIntegerAttribute( ad, element, "allocation-size" ); - buildUniqueConstraints( ad, element ); + 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() ); @@ -2571,8 +2626,12 @@ public static TableGenerator buildTableGeneratorAnnotation(Element element, XMLC return AnnotationFactory.create( ad ); } - private SequenceGenerator getSequenceGenerator(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( annotationToXml.get( SequenceGenerator.class ) ) : null; + 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 ); } @@ -2584,13 +2643,13 @@ else if ( defaults.canUseJavaAnnotations() ) { } } - public static SequenceGenerator buildSequenceGeneratorAnnotation(Element element) { + public static SequenceGenerator buildSequenceGeneratorAnnotation(JaxbSequenceGenerator element) { if ( element != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( SequenceGenerator.class ); - copyStringAttribute( ad, element, "name", false ); - copyStringAttribute( ad, element, "sequence-name", false ); - copyIntegerAttribute( ad, element, "initial-value" ); - copyIntegerAttribute( ad, element, "allocation-size" ); + 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 { @@ -2598,32 +2657,17 @@ public static SequenceGenerator buildSequenceGeneratorAnnotation(Element element } } - private DiscriminatorColumn getDiscriminatorColumn(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( "discriminator-column" ) : 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 ); - copyStringAttribute( ad, element, "name", false ); - copyStringAttribute( ad, element, "column-definition", false ); - String value = element.attributeValue( "discriminator-type" ); - DiscriminatorType type = DiscriminatorType.STRING; - if ( value != null ) { - if ( "STRING".equals( value ) ) { - type = DiscriminatorType.STRING; - } - else if ( "CHAR".equals( value ) ) { - type = DiscriminatorType.CHAR; - } - else if ( "INTEGER".equals( value ) ) { - type = DiscriminatorType.INTEGER; - } - else { - throw new AnnotationException( - "Unknown DiscriminatorType in XML: " + value + " (" + SCHEMA_VALIDATION + ")" - ); - } + copyAttribute( ad, "name", element.getName(), false ); + copyAttribute( ad, "column-definition", element.getColumnDefinition(), false ); + DiscriminatorType type = element.getDiscriminatorType(); + if ( type != null ) { + ad.setValue( "discriminatorType", type ); } - ad.setValue( "discriminatorType", type ); - copyIntegerAttribute( ad, element, "length" ); + copyAttribute( ad, "length", element.getLength(), false ); return AnnotationFactory.create( ad ); } else if ( defaults.canUseJavaAnnotations() ) { @@ -2634,11 +2678,11 @@ else if ( defaults.canUseJavaAnnotations() ) { } } - private DiscriminatorValue getDiscriminatorValue(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( "discriminator-value" ) : 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 ); - copyStringElement( element, ad, "value" ); + ad.setValue( "value", element ); return AnnotationFactory.create( ad ); } else if ( defaults.canUseJavaAnnotations() ) { @@ -2649,30 +2693,14 @@ else if ( defaults.canUseJavaAnnotations() ) { } } - private Inheritance getInheritance(Element tree, XMLContext.Default defaults) { - Element element = tree != null ? tree.element( "inheritance" ) : 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 ); - Attribute attr = element.attribute( "strategy" ); - InheritanceType strategy = InheritanceType.SINGLE_TABLE; - if ( attr != null ) { - String value = attr.getValue(); - if ( "SINGLE_TABLE".equals( value ) ) { - strategy = InheritanceType.SINGLE_TABLE; - } - else if ( "JOINED".equals( value ) ) { - strategy = InheritanceType.JOINED; - } - else if ( "TABLE_PER_CLASS".equals( value ) ) { - strategy = InheritanceType.TABLE_PER_CLASS; - } - else { - throw new AnnotationException( - "Unknown InheritanceType in XML: " + value + " (" + SCHEMA_VALIDATION + ")" - ); - } + InheritanceType strategy = element.getStrategy(); + if ( strategy != null ) { + ad.setValue( "strategy", strategy ); } - ad.setValue( "strategy", strategy ); return AnnotationFactory.create( ad ); } else if ( defaults.canUseJavaAnnotations() ) { @@ -2683,19 +2711,19 @@ else if ( defaults.canUseJavaAnnotations() ) { } } - private IdClass getIdClass(Element tree, XMLContext.Default defaults) { - Element element = tree == null ? null : tree.element( "id-class" ); + private IdClass getIdClass(ManagedType root, XMLContext.Default defaults) { + JaxbIdClass element = root instanceof EntityOrMappedSuperclass ? + ( (EntityOrMappedSuperclass) root ).getIdClass() : null; if ( element != null ) { - Attribute attr = element.attribute( "class" ); - if ( attr != null ) { + String className = element.getClazz(); + if ( className != null ) { AnnotationDescriptor ad = new AnnotationDescriptor( IdClass.class ); - Class clazz; + Class clazz; try { - clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( attr.getValue(), defaults ) - ); + clazz = classLoaderAccess.classForName( XMLContext.buildSafeClassName( className, defaults ) ); } catch ( ClassLoadingException e ) { - throw new AnnotationException( "Unable to find id-class: " + attr.getValue(), e ); + throw new AnnotationException( "Unable to find id-class: " + className, e ); } ad.setValue( "value", clazz ); return AnnotationFactory.create( ad ); @@ -2712,14 +2740,20 @@ else if ( defaults.canUseJavaAnnotations() ) { } } + 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 - * annotations is never allowed. */ - private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(Element element, XMLContext.Default defaults, boolean mergeWithAnnotations) { - PrimaryKeyJoinColumn[] columns = buildPrimaryKeyJoinColumns( element ); + 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 ); @@ -2742,14 +2776,15 @@ private PrimaryKeyJoinColumns getPrimaryKeyJoinColumns(Element element, XMLConte } } - private Entity getEntity(Element tree, XMLContext.Default defaults) { - if ( tree == null ) { + private Entity getEntity(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Entity.class ) : null; } else { - if ( "entity".equals( tree.getName() ) ) { + if ( element instanceof JaxbEntity ) { + JaxbEntity entityElement = (JaxbEntity) element; AnnotationDescriptor entity = new AnnotationDescriptor( Entity.class ); - copyStringAttribute( entity, tree, "name", false ); + copyAttribute( entity, "name", entityElement.getName(), false ); if ( defaults.canUseJavaAnnotations() && StringHelper.isEmpty( (String) entity.valueOf( "name" ) ) ) { Entity javaAnn = getPhysicalAnnotation( Entity.class ); @@ -2765,12 +2800,12 @@ private Entity getEntity(Element tree, XMLContext.Default defaults) { } } - private MappedSuperclass getMappedSuperclass(Element tree, XMLContext.Default defaults) { - if ( tree == null ) { + private MappedSuperclass getMappedSuperclass(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( MappedSuperclass.class ) : null; } else { - if ( "mapped-superclass".equals( tree.getName() ) ) { + if ( element instanceof JaxbMappedSuperclass ) { AnnotationDescriptor entity = new AnnotationDescriptor( MappedSuperclass.class ); return AnnotationFactory.create( entity ); } @@ -2780,12 +2815,12 @@ private MappedSuperclass getMappedSuperclass(Element tree, XMLContext.Default de } } - private Embeddable getEmbeddable(Element tree, XMLContext.Default defaults) { - if ( tree == null ) { + private Embeddable getEmbeddable(ManagedType element, XMLContext.Default defaults) { + if ( element == null ) { return defaults.canUseJavaAnnotations() ? getPhysicalAnnotation( Embeddable.class ) : null; } else { - if ( "embeddable".equals( tree.getName() ) ) { + if ( element instanceof JaxbEmbeddable ) { AnnotationDescriptor entity = new AnnotationDescriptor( Embeddable.class ); return AnnotationFactory.create( entity ); } @@ -2795,9 +2830,9 @@ private Embeddable getEmbeddable(Element tree, XMLContext.Default defaults) { } } - private Table getTable(Element tree, XMLContext.Default defaults) { - Element subelement = tree == null ? null : tree.element( "table" ); - if ( subelement == null ) { + 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() ) ) { @@ -2832,44 +2867,44 @@ else if ( defaults.canUseJavaAnnotations() ) { else { //ignore java annotation, an element is defined AnnotationDescriptor annotation = new AnnotationDescriptor( Table.class ); - copyStringAttribute( annotation, subelement, "name", false ); - copyStringAttribute( annotation, subelement, "catalog", false ); + 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() ); } - copyStringAttribute( annotation, subelement, "schema", false ); + copyAttribute( annotation, "schema", element.getSchema(), false ); if ( StringHelper.isNotEmpty( defaults.getSchema() ) && StringHelper.isEmpty( (String) annotation.valueOf( "schema" ) ) ) { annotation.setValue( "schema", defaults.getSchema() ); } - buildUniqueConstraints( annotation, subelement ); - buildIndex( annotation, subelement ); + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); return AnnotationFactory.create( annotation ); } } - private SecondaryTables getSecondaryTables(Element tree, XMLContext.Default defaults) { - List elements = tree == null ? - new ArrayList<>() : - (List) tree.elements( "secondary-table" ); + private SecondaryTables getSecondaryTables(ManagedType root, XMLContext.Default defaults) { + List elements = root instanceof JaxbEntity ? + ( (JaxbEntity) root ).getSecondaryTable() : Collections.emptyList(); List secondaryTables = new ArrayList<>( 3 ); - for ( Element element : elements ) { + for ( JaxbSecondaryTable element : elements ) { AnnotationDescriptor annotation = new AnnotationDescriptor( SecondaryTable.class ); - copyStringAttribute( annotation, element, "name", false ); - copyStringAttribute( annotation, element, "catalog", false ); + 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() ); } - copyStringAttribute( annotation, element, "schema", false ); + 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 ); - buildIndex( annotation, element ); - annotation.setValue( "pkJoinColumns", buildPrimaryKeyJoinColumns( element ) ); + buildUniqueConstraints( annotation, element.getUniqueConstraint() ); + buildIndex( annotation, element.getIndex() ); + annotation.setValue( "pkJoinColumns", + buildPrimaryKeyJoinColumns( element.getPrimaryKeyJoinColumn() ) ); secondaryTables.add( AnnotationFactory.create( annotation ) ); } /* @@ -2924,120 +2959,91 @@ private void overridesDefaultInSecondaryTable( } } } - private static void buildIndex(AnnotationDescriptor annotation, Element element){ - List indexElementList = element.elements( "index" ); - Index[] indexes = new Index[indexElementList.size()]; - for(int i=0;i elements) { + Index[] indexes = new Index[elements.size()]; + int i = 0; + for ( JaxbIndex element : elements ) { AnnotationDescriptor indexAnn = new AnnotationDescriptor( Index.class ); - copyStringAttribute( indexAnn, subelement, "name", false ); - copyStringAttribute( indexAnn, subelement, "column-list", true ); - copyBooleanAttribute( indexAnn, subelement, "unique" ); - indexes[i] = AnnotationFactory.create( indexAnn ); + 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, Element element) { - List uniqueConstraintElementList = element.elements( "unique-constraint" ); - UniqueConstraint[] uniqueConstraints = new UniqueConstraint[uniqueConstraintElementList.size()]; - int ucIndex = 0; - Iterator ucIt = uniqueConstraintElementList.listIterator(); - while ( ucIt.hasNext() ) { - Element subelement = (Element) ucIt.next(); - List columnNamesElements = subelement.elements( "column-name" ); - String[] columnNames = new String[columnNamesElements.size()]; - int columnNameIndex = 0; - Iterator it = columnNamesElements.listIterator(); - while ( it.hasNext() ) { - Element columnNameElt = (Element) it.next(); - columnNames[columnNameIndex++] = columnNameElt.getTextTrim(); - } + + 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 ); - copyStringAttribute( ucAnn, subelement, "name", false ); + copyAttribute( ucAnn, "name", element.getName(), false ); ucAnn.setValue( "columnNames", columnNames ); - uniqueConstraints[ucIndex++] = AnnotationFactory.create( ucAnn ); + uniqueConstraints[i++] = AnnotationFactory.create( ucAnn ); } annotation.setValue( "uniqueConstraints", uniqueConstraints ); } - private PrimaryKeyJoinColumn[] buildPrimaryKeyJoinColumns(Element element) { - if ( element == null ) { - return new PrimaryKeyJoinColumn[] { }; - } - List pkJoinColumnElementList = element.elements( "primary-key-join-column" ); - PrimaryKeyJoinColumn[] pkJoinColumns = new PrimaryKeyJoinColumn[pkJoinColumnElementList.size()]; - int index = 0; - Iterator pkIt = pkJoinColumnElementList.listIterator(); - while ( pkIt.hasNext() ) { - Element subelement = (Element) pkIt.next(); + private PrimaryKeyJoinColumn[] buildPrimaryKeyJoinColumns(List elements) { + PrimaryKeyJoinColumn[] pkJoinColumns = new PrimaryKeyJoinColumn[elements.size()]; + int i = 0; + for ( JaxbPrimaryKeyJoinColumn element : elements ) { AnnotationDescriptor pkAnn = new AnnotationDescriptor( PrimaryKeyJoinColumn.class ); - copyStringAttribute( pkAnn, subelement, "name", false ); - copyStringAttribute( pkAnn, subelement, "referenced-column-name", false ); - copyStringAttribute( pkAnn, subelement, "column-definition", false ); - pkJoinColumns[index++] = AnnotationFactory.create( pkAnn ); + 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 a string attribute from an XML element to an annotation descriptor. The name of the annotation attribute is + * 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 element XML element from where to copy 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 copyStringAttribute( - final AnnotationDescriptor annotation, final Element element, - final String attributeName, final boolean mandatory) { - copyStringAttribute( + private static void copyAttribute( + final AnnotationDescriptor annotation, + final String attributeName, final Object attributeValue, + final boolean mandatory) { + copyAttribute( annotation, - element, getJavaAttributeNameFromXMLOne( attributeName ), attributeName, + attributeValue, mandatory ); } /** - * Copy a string attribute from an XML element to an annotation descriptor. The name of the annotation attribute is + * 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 element XML element from where to copy the attribute. * @param annotationAttributeName name of the annotation attribute where to copy. - * @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 copyStringAttribute( - final AnnotationDescriptor annotation, final Element element, - final String annotationAttributeName, final String attributeName, boolean mandatory) { - String attribute = element.attributeValue( attributeName ); - if ( attribute != null ) { - annotation.setValue( annotationAttributeName, attribute ); + 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( - element.getName() + "." + attributeName + " is mandatory in XML overriding. " + SCHEMA_VALIDATION - ); - } - } - } - - private static void copyIntegerAttribute(AnnotationDescriptor annotation, Element element, String attributeName) { - String attribute = element.attributeValue( attributeName ); - if ( attribute != null ) { - String annotationAttributeName = getJavaAttributeNameFromXMLOne( attributeName ); - annotation.setValue( annotationAttributeName, attribute ); - try { - int length = Integer.parseInt( attribute ); - annotation.setValue( annotationAttributeName, length ); - } - catch ( NumberFormatException e ) { - throw new AnnotationException( - element.getPath() + attributeName + " not parseable: " + attribute + " (" + SCHEMA_VALIDATION + ")" + annotationToXml.getOrDefault( annotation.type(), annotation.type().getName() ) + + "." + attributeName + + " is mandatory in XML overriding. " + SCHEMA_VALIDATION ); } } @@ -3056,19 +3062,6 @@ private static String getJavaAttributeNameFromXMLOne(String attributeName) { return annotationAttributeName.toString(); } - private static void copyStringElement(Element element, AnnotationDescriptor ad, String annotationAttribute) { - String discr = element.getTextTrim(); - ad.setValue( annotationAttribute, discr ); - } - - private static void copyBooleanAttribute(AnnotationDescriptor descriptor, Element element, String attribute) { - String attributeValue = element.attributeValue( attribute ); - if ( StringHelper.isNotEmpty( attributeValue ) ) { - String javaAttribute = getJavaAttributeNameFromXMLOne( attribute ); - descriptor.setValue( javaAttribute, Boolean.parseBoolean( attributeValue ) ); - } - } - private T getPhysicalAnnotation(Class annotationType) { return element.getAnnotation( annotationType ); } @@ -3080,4 +3073,5 @@ private boolean isPhysicalAnnotationPresent(Class anno 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 index 6c65829d0999..99bf8efd5484 100644 --- 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 @@ -23,20 +23,19 @@ 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; -import org.dom4j.Element; - /** * MetadataProvider aware of the JPA Deployment descriptor (orm.xml, ...). * * @author Emmanuel Bernard */ -// 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 final class JPAXMLOverriddenMetadataProvider implements MetadataProvider { @@ -112,28 +111,27 @@ public Map getDefaults() { } } defaults.put( EntityListeners.class, entityListeners ); - for ( Element element : xmlContext.getAllDocuments() ) { - @SuppressWarnings( "unchecked" ) - List elements = element.elements( "sequence-generator" ); + 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 ( Element subelement : elements ) { - sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( subelement ) ); + for ( JaxbSequenceGenerator element : jaxbSequenceGenerators ) { + sequenceGenerators.add( JPAXMLOverriddenAnnotationReader.buildSequenceGeneratorAnnotation( element ) ); } - elements = element.elements( "table-generator" ); + List jaxbTableGenerators = entityMappings.getTableGenerator(); List tableGenerators = ( List ) defaults.get( TableGenerator.class ); if ( tableGenerators == null ) { tableGenerators = new ArrayList<>(); defaults.put( TableGenerator.class, tableGenerators ); } - for ( Element subelement : elements ) { + for ( JaxbTableGenerator element : jaxbTableGenerators ) { tableGenerators.add( JPAXMLOverriddenAnnotationReader.buildTableGeneratorAnnotation( - subelement, xmlDefaults + element, xmlDefaults ) ); } @@ -144,8 +142,7 @@ public Map getDefaults() { defaults.put( NamedQuery.class, namedQueries ); } List currentNamedQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries( - element, - false, + entityMappings.getNamedQuery(), xmlDefaults, classLoaderAccess ); @@ -156,9 +153,8 @@ public Map getDefaults() { namedNativeQueries = new ArrayList<>(); defaults.put( NamedNativeQuery.class, namedNativeQueries ); } - List currentNamedNativeQueries = JPAXMLOverriddenAnnotationReader.buildNamedQueries( - element, - true, + List currentNamedNativeQueries = JPAXMLOverriddenAnnotationReader.buildNamedNativeQueries( + entityMappings.getNamedNativeQuery(), xmlDefaults, classLoaderAccess ); @@ -172,7 +168,7 @@ public Map getDefaults() { defaults.put( SqlResultSetMapping.class, sqlResultSetMappings ); } List currentSqlResultSetMappings = JPAXMLOverriddenAnnotationReader.buildSqlResultsetMappings( - element, + entityMappings.getSqlResultSetMapping(), xmlDefaults, classLoaderAccess ); @@ -185,7 +181,7 @@ public Map getDefaults() { } List currentNamedStoredProcedureQueries = JPAXMLOverriddenAnnotationReader .buildNamedStoreProcedureQueries( - element, + entityMappings.getNamedStoredProcedureQuery(), xmlDefaults, classLoaderAccess ); 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 000000000000..97bf6b89ea23 --- /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 index 513fbd6aa87c..f009b708c4cb 100644 --- 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 @@ -16,89 +16,81 @@ 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.NotYetImplementedException; import org.hibernate.cfg.annotations.reflection.AttributeConverterDefinitionCollector; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; -import org.dom4j.Document; -import org.dom4j.Element; - /** * A helper for consuming orm.xml mappings. * * @author Emmanuel Bernard * @author Brett Meyer */ -// 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. public class XMLContext implements Serializable { private static final CoreMessageLogger LOG = CoreLogging.messageLogger( XMLContext.class ); private final ClassLoaderAccess classLoaderAccess; private Default globalDefaults; - private Map classOverriding = new HashMap<>(); - private Map defaultsOverriding = new HashMap<>(); - private List defaultElements = new ArrayList<>(); - private List defaultEntityListeners = new ArrayList<>(); + 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; - XMLContext(ClassLoaderAccess classLoaderAccess) { + /** + * @deprecated Use {@link org.hibernate.cfg.annotations.reflection.XMLContext#XMLContext(BootstrapContext)} instead. + */ + @Deprecated + public XMLContext(ClassLoaderAccess classLoaderAccess) { this.classLoaderAccess = classLoaderAccess; } - // For tests only public XMLContext(BootstrapContext bootstrapContext) { this.classLoaderAccess = bootstrapContext.getClassLoaderAccess(); } /** - * @param entityMappings The xml entity mappings to add - * @return Add an xml document to this context and return the list of added class names. - */ - public List addDocument(JaxbEntityMappings entityMappings) { - throw new NotYetImplementedException("HHH-14529 Implementation in progress"); - } - - /** - * @param doc The xml document to add + * @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(Document doc) { + public List addDocument(JaxbEntityMappings entityMappings) { hasContext = true; List addedClasses = new ArrayList<>(); - Element root = doc.getRootElement(); //global defaults - Element metadata = root.element( "persistence-unit-metadata" ); + JaxbPersistenceUnitMetadata metadata = entityMappings.getPersistenceUnitMetadata(); if ( metadata != null ) { if ( globalDefaults == null ) { globalDefaults = new Default(); globalDefaults.setMetadataComplete( - metadata.element( "xml-mapping-metadata-complete" ) != null ? + metadata.getXmlMappingMetadataComplete() != null ? Boolean.TRUE : null ); - Element defaultElement = metadata.element( "persistence-unit-defaults" ); + JaxbPersistenceUnitDefaults defaultElement = metadata.getPersistenceUnitDefaults(); if ( defaultElement != null ) { - Element unitElement = defaultElement.element( "schema" ); - globalDefaults.setSchema( unitElement != null ? unitElement.getTextTrim() : null ); - unitElement = defaultElement.element( "catalog" ); - globalDefaults.setCatalog( unitElement != null ? unitElement.getTextTrim() : null ); - unitElement = defaultElement.element( "access" ); - setAccess( unitElement, globalDefaults ); - unitElement = defaultElement.element( "cascade-persist" ); - globalDefaults.setCascadePersist( unitElement != null ? Boolean.TRUE : null ); - unitElement = defaultElement.element( "delimited-identifiers" ); - globalDefaults.setDelimitedIdentifiers( unitElement != null ? Boolean.TRUE : null ); - defaultEntityListeners.addAll( addEntityListenerClasses( defaultElement, null, addedClasses ) ); + 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 { @@ -108,92 +100,61 @@ public List addDocument(Document doc) { //entity mapping default Default entityMappingDefault = new Default(); - Element unitElement = root.element( "package" ); - String packageName = unitElement != null ? unitElement.getTextTrim() : null; + String packageName = entityMappings.getPackage(); entityMappingDefault.setPackageName( packageName ); - unitElement = root.element( "schema" ); - entityMappingDefault.setSchema( unitElement != null ? unitElement.getTextTrim() : null ); - unitElement = root.element( "catalog" ); - entityMappingDefault.setCatalog( unitElement != null ? unitElement.getTextTrim() : null ); - unitElement = root.element( "access" ); - setAccess( unitElement, entityMappingDefault ); - defaultElements.add( root ); + entityMappingDefault.setSchema( entityMappings.getSchema() ); + entityMappingDefault.setCatalog( entityMappings.getCatalog() ); + entityMappingDefault.setAccess( entityMappings.getAccess() ); + defaultElements.add( entityMappings ); - setLocalAttributeConverterDefinitions( root.elements( "converter" ) ); + setLocalAttributeConverterDefinitions( entityMappings.getConverter() ); - List entities = root.elements( "entity" ); - addClass( entities, packageName, entityMappingDefault, addedClasses ); + addClass( entityMappings.getEntity(), packageName, entityMappingDefault, addedClasses ); - entities = root.elements( "mapped-superclass" ); - addClass( entities, packageName, entityMappingDefault, addedClasses ); + addClass( entityMappings.getMappedSuperclass(), packageName, entityMappingDefault, addedClasses ); - entities = root.elements( "embeddable" ); - addClass( entities, packageName, entityMappingDefault, addedClasses ); - return addedClasses; - } + addClass( entityMappings.getEmbeddable(), packageName, entityMappingDefault, addedClasses ); - private void setAccess(Element unitElement, Default defaultType) { - if ( unitElement != null ) { - String access = unitElement.getTextTrim(); - setAccess( access, defaultType ); - } - } - - private void setAccess( String access, Default defaultType) { - AccessType type; - if ( access != null ) { - try { - type = AccessType.valueOf( access ); - } - catch ( IllegalArgumentException e ) { - throw new AnnotationException( "Invalid access type " + access + " (check your xml configuration)" ); - } - defaultType.setAccess( type ); - } + return addedClasses; } - private void addClass(List entities, String packageName, Default defaults, List addedClasses) { - for (Element element : entities) { - String className = buildSafeClassName( element.attributeValue( "class" ), packageName ); - if ( classOverriding.containsKey( className ) ) { + 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 ); - classOverriding.put( className, element ); + managedTypeOverride.put( className, element ); Default localDefault = new Default(); localDefault.override( defaults ); - String metadataCompleteString = element.attributeValue( "metadata-complete" ); - if ( metadataCompleteString != null ) { - localDefault.setMetadataComplete( Boolean.parseBoolean( metadataCompleteString ) ); - } - String access = element.attributeValue( "access" ); - setAccess( access, localDefault ); - defaultsOverriding.put( className, localDefault ); + localDefault.setMetadataComplete( element.isMetadataComplete() ); + localDefault.setAccess( element.getAccess() ); + defaultsOverride.put( className, localDefault ); LOG.debugf( "Adding XML overriding information for %s", className ); - addEntityListenerClasses( element, packageName, addedClasses ); + if ( element instanceof JaxbEntity ) { + addEntityListenerClasses( ( (JaxbEntity) element ).getEntityListeners(), packageName, addedClasses ); + } + else if ( element instanceof JaxbMappedSuperclass ) { + addEntityListenerClasses( ( (JaxbMappedSuperclass) element ).getEntityListeners(), packageName, addedClasses ); + } } } - private List addEntityListenerClasses(Element element, String packageName, List addedClasses) { + private List addEntityListenerClasses(JaxbEntityListeners listeners, String packageName, List addedClasses) { List localAddedClasses = new ArrayList<>(); - Element listeners = element.element( "entity-listeners" ); if ( listeners != null ) { - @SuppressWarnings( "unchecked" ) - List elements = listeners.elements( "entity-listener" ); - for (Element listener : elements) { - String listenerClassName = buildSafeClassName( listener.attributeValue( "class" ), packageName ); - if ( classOverriding.containsKey( listenerClassName ) ) { - //maybe switch it to warn? - if ( "entity-listener".equals( classOverriding.get( listenerClassName ).getName() ) ) { - LOG.duplicateListener( listenerClassName ); - continue; - } - throw new IllegalStateException("Duplicate XML entry for " + listenerClassName); + 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 ); - classOverriding.put( listenerClassName, listener ); + entityListenerOverride.put( listenerClassName, listener ); } } LOG.debugf( "Adding XML overriding information for listeners: %s", localAddedClasses ); @@ -202,11 +163,10 @@ private List addEntityListenerClasses(Element element, String packageNam } @SuppressWarnings("unchecked") - private void setLocalAttributeConverterDefinitions(List converterElements) { - for ( Element converterElement : converterElements ) { - final String className = converterElement.attributeValue( "class" ); - final String autoApplyAttribute = converterElement.attributeValue( "auto-apply" ); - final boolean autoApply = autoApplyAttribute != null && Boolean.parseBoolean( autoApplyAttribute ); + 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( @@ -232,7 +192,7 @@ public static String buildSafeClassName(String className, String defaultPackageN return className; } - public static String buildSafeClassName(String className, XMLContext.Default defaults) { + public static String buildSafeClassName(String className, Default defaults) { return buildSafeClassName( className, defaults.getPackageName() ); } @@ -240,17 +200,21 @@ public Default getDefault(String className) { Default xmlDefault = new Default(); xmlDefault.override( globalDefaults ); if ( className != null ) { - Default entityMappingOverriding = defaultsOverriding.get( className ); + Default entityMappingOverriding = defaultsOverride.get( className ); xmlDefault.override( entityMappingOverriding ); } return xmlDefault; } - public Element getXMLTree(String className ) { - return classOverriding.get( className ); + public ManagedType getManagedTypeOverride(String className) { + return managedTypeOverride.get( className ); + } + + public JaxbEntityListener getEntityListenerOverride(String className) { + return entityListenerOverride.get( className ); } - public List getAllDocuments() { + public List getAllDocuments() { return defaultElements; } diff --git a/hibernate-core/src/main/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index 1c611dcf0165..3ca2d1a54cf9 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -72,9 +72,9 @@ - + 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 000000000000..11d3628fe415 --- /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/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/reflection/JPAXMLOverriddenAnnotationReaderTest.java index 7024aad12492..1704cce53e96 100644 --- 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 @@ -9,12 +9,15 @@ 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; @@ -401,32 +404,16 @@ public void testEntityListeners() throws Exception { assertEquals( OtherLogListener.class.getName(), context.getDefaultEntityListeners().get( 0 ) ); } - private XMLContext buildContext(String ormfile) throws SAXException, DocumentException, IOException { - XMLHelper xmlHelper = new XMLHelper(); - InputStream is = ClassLoaderServiceTestingImpl.INSTANCE.locateResourceStream( ormfile ); - assertNotNull( "ORM.xml not found: " + ormfile, is ); + 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 ); - ErrorLogger errorLogger = new ErrorLogger(); - SAXReader saxReader = xmlHelper.createSAXReader( errorLogger, EJB3DTDEntityResolver.INSTANCE ); - //saxReader.setValidation( false ); - 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 { - is.close(); - } - if ( errorLogger.hasErrors() ) { - System.out.println( errorLogger.getErrors().get( 0 ) ); - } - assertFalse( errorLogger.hasErrors() ); - context.addDocument( doc ); + context.addDocument( mappings ); return context; } } 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 fb68e62a4604..39ed92d42f96 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,24 +6,14 @@ */ 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.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.ErrorLogger; -import org.hibernate.internal.util.xml.XMLHelper; +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}, @@ -38,36 +28,15 @@ 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/Ejb3XmlTestCase.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/Ejb3XmlTestCase.java index c4ebbed1d651..ea1d1fee35e0 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.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; -import org.dom4j.Document; -import org.dom4j.io.SAXReader; - +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; @@ -29,8 +29,12 @@ * database is used. Thus, no schema generation or cleanup will be performed. */ public abstract class Ejb3XmlTestCase extends BaseUnitTestCase { + protected JPAXMLOverriddenAnnotationReader reader; + protected Ejb3XmlTestCase() { + } + protected void assertAnnotationPresent(Class annotationType) { assertTrue( "Expected annotation " + annotationType.getSimpleName() + " was not present", @@ -59,17 +63,19 @@ protected AnnotatedElement getAnnotatedElement(Class entityClass, String fiel 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; } } From 2aa2bee255f443ea05508968d58e9acf19d51c8b Mon Sep 17 00:00:00 2001 From: Karel Maesen Date: Thu, 15 Apr 2021 20:02:48 +0200 Subject: [PATCH 21/21] HHH-11490 Fix for UUID mapping conflict * HHH-11490 Fix for UUID mapping conflict * HHH-11490 Custom ValueBinder implementation --- .../postgis/PGGeometryTypeDescriptor.java | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) 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 af4ebc86b3c2..d4a589789688 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 @@ -17,7 +17,6 @@ import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.descriptor.sql.BasicBinder; import org.hibernate.type.descriptor.sql.BasicExtractor; import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; @@ -39,12 +38,12 @@ 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 +78,7 @@ private static Geometry parseWkt(String pgValue) { @Override public int getSqlType() { - return Types.OTHER; + return 5432; } @Override @@ -89,15 +88,36 @@ public boolean canBeRemapped() { @Override public ValueBinder getBinder(final JavaTypeDescriptor javaTypeDescriptor) { - return new BasicBinder( javaTypeDescriptor, this ) { + return new ValueBinder() { + + @Override + public final void bind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + if ( value == null ) { + st.setNull( index, Types.OTHER ); + } + else { + doBind( st, value, index, options ); + } + } + @Override + public final void bind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + if ( value == null ) { + st.setNull( name, Types.OTHER ); + } + else { + doBind( st, value, name, options ); + } + } + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { final PGobject obj = toPGobject( value, options ); st.setObject( index, obj ); } - @Override protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException { final PGobject obj = toPGobject( value, options ); @@ -106,7 +126,7 @@ protected void doBind(CallableStatement st, X value, String name, WrapperOptions private PGobject toPGobject(X value, WrapperOptions options) throws SQLException { final WkbEncoder encoder = Wkb.newEncoder( Wkb.Dialect.POSTGIS_EWKB_1 ); - final Geometry geometry = getJavaDescriptor().unwrap( value, Geometry.class, options ); + final Geometry geometry = javaTypeDescriptor.unwrap( value, Geometry.class, options ); final String hexString = encoder.encode( geometry, ByteOrder.NDR ).toString(); final PGobject obj = new PGobject(); obj.setType( "geometry" );