This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/main by this push:
new 5b885d856cb CAUSEWAY-3884: tabs into EclipseLink internals to gather
OrmMetadata
5b885d856cb is described below
commit 5b885d856cb296f4a645a802d679fc53c2407f00
Author: andi-huber <[email protected]>
AuthorDate: Sat Apr 19 09:50:04 2025 +0200
CAUSEWAY-3884: tabs into EclipseLink internals to gather OrmMetadata
---
.../causeway/commons/tabular/TabularModelTest.java | 27 ++++++++++
.../jpa/eclipselink/src/main/java/module-info.java | 2 +
.../CausewayModulePersistenceJpaEclipselink.java | 5 +-
.../metamodel/EclipseLinkMetadataService.java | 41 +++++++++++++++
.../metamodel/EclipseLinkMetadataUtils.java} | 58 +++++++++++++--------
.../metamodel/_EclipseLinkInternals.java | 60 ++++++++++++++++++++++
.../jpa/integration/entity/JpaEntityFacet.java | 8 +--
.../integration/entity/OrmMetadataProvider.java | 33 ++++++++++++
8 files changed, 208 insertions(+), 26 deletions(-)
diff --git
a/commons/src/test/java/org/apache/causeway/commons/tabular/TabularModelTest.java
b/commons/src/test/java/org/apache/causeway/commons/tabular/TabularModelTest.java
new file mode 100644
index 00000000000..ef014d2575e
--- /dev/null
+++
b/commons/src/test/java/org/apache/causeway/commons/tabular/TabularModelTest.java
@@ -0,0 +1,27 @@
+package org.apache.causeway.commons.tabular;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import org.apache.causeway.commons.collections.Can;
+
+class TabularModelTest {
+
+ @Test
+ void test() {
+
+ var col1 = new TabularModel.TabularColumn(0, "Col-1", "Column
Description 1");
+ var col2 = new TabularModel.TabularColumn(1, "Col-2", "Column
Description 2");
+
+ var row1 = new TabularModel.TabularRow(Can.of(
+ TabularModel.TabularCell.single("cell1-1"),
+ TabularModel.TabularCell.single("cell1-2")));
+ var row2 = new TabularModel.TabularRow(Can.of(
+ TabularModel.TabularCell.single("cell1-1"),
+ TabularModel.TabularCell.single("cell1-2")));
+
+ var sheet = new TabularModel.TabularSheet("sheet-1", Can.of(col1,
col2), Can.of(row1, row2));
+
+ }
+}
diff --git a/persistence/jpa/eclipselink/src/main/java/module-info.java
b/persistence/jpa/eclipselink/src/main/java/module-info.java
index 9a0444e8e79..eea72f7851d 100644
--- a/persistence/jpa/eclipselink/src/main/java/module-info.java
+++ b/persistence/jpa/eclipselink/src/main/java/module-info.java
@@ -34,6 +34,7 @@
requires org.apache.causeway.applib;
requires org.apache.causeway.commons;
requires org.apache.causeway.core.config;
+ requires org.apache.causeway.core.metamodel;
requires org.apache.causeway.persistence.jpa.integration;
requires org.apache.logging.log4j;
requires org.eclipse.persistence.core;
@@ -45,5 +46,6 @@
requires spring.jdbc;
requires spring.orm;
requires spring.tx;
+ requires org.eclipse.persistence.jpa;
}
\ No newline at end of file
diff --git
a/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/CausewayModulePersistenceJpaEclipselink.java
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/CausewayModulePersistenceJpaEclipselink.java
index 4a51d91c981..974642d7d0d 100644
---
a/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/CausewayModulePersistenceJpaEclipselink.java
+++
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/CausewayModulePersistenceJpaEclipselink.java
@@ -20,6 +20,7 @@
import java.sql.SQLException;
import java.util.Map;
+
import javax.sql.DataSource;
import jakarta.inject.Inject;
@@ -44,6 +45,7 @@
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.config.CausewayConfiguration;
import org.apache.causeway.persistence.jpa.eclipselink.config.ElSettings;
+import
org.apache.causeway.persistence.jpa.eclipselink.metamodel.EclipseLinkMetadataService;
import
org.apache.causeway.persistence.jpa.integration.CausewayModulePersistenceJpaIntegration;
import lombok.SneakyThrows;
@@ -61,7 +63,8 @@
*/
@Configuration
@Import({
- CausewayModulePersistenceJpaIntegration.class
+ CausewayModulePersistenceJpaIntegration.class,
+ EclipseLinkMetadataService.class
})
@EnableConfigurationProperties(ElSettings.class)
@Log4j2
diff --git
a/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataService.java
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataService.java
new file mode 100644
index 00000000000..b69ffa4ccde
--- /dev/null
+++
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataService.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.persistence.jpa.eclipselink.metamodel;
+
+import jakarta.persistence.EntityManager;
+
+import org.jspecify.annotations.NonNull;
+
+import org.springframework.stereotype.Service;
+
+import
org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
+import
org.apache.causeway.persistence.jpa.integration.entity.OrmMetadataProvider;
+
+/**
+ * Service that acts as {@link OrmMetadataProvider}, which provides vendor
independent ORM metadata {@link EntityOrmMetadata}.
+ * @since 3.4.0
+ */
+@Service
+public class EclipseLinkMetadataService implements OrmMetadataProvider {
+
+ @Override
+ public EntityOrmMetadata ormMetadataFor(@NonNull EntityManager
entityManager, @NonNull Class<?> entityClass) {
+ return EclipseLinkMetadataUtils.ormMetadataFor(entityManager,
entityClass);
+ }
+}
diff --git
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataUtils.java
similarity index 58%
rename from
persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java
rename to
persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataUtils.java
index 0626a919439..30d07dcac8a 100644
---
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java
+++
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/EclipseLinkMetadataUtils.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.causeway.persistence.jpa.integration.entity;
+package org.apache.causeway.persistence.jpa.eclipselink.metamodel;
import java.util.Optional;
@@ -24,59 +24,73 @@
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.SingularAttribute;
+import org.eclipse.persistence.mappings.foundation.AbstractColumnMapping;
+import org.jspecify.annotations.NonNull;
+
import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.functional.Try;
import org.apache.causeway.commons.internal.base._NullSafe;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import
org.apache.causeway.core.config.beans.CausewayBeanMetaData.PersistenceStack;
import
org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
import
org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata.ColumnOrmMetadata;
-import org.jspecify.annotations.NonNull;
+import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
+/**
+ * Utility that provides vendor independent ORM metadata {@link
EntityOrmMetadata}.
+ * @since 3.4.0
+ */
@UtilityClass
-class _MetadataUtil {
-
- EntityOrmMetadata ormMetadataFor(
+public class EclipseLinkMetadataUtils {
+
+ public EntityOrmMetadata ormMetadataFor(
final @NonNull EntityManager entityManager,
final @NonNull Class<?> entityClass) {
- final EntityType<?> typeMetadata = lookupJpaMetamodel(entityManager,
entityClass)
+ final EntityType<?> typeMetadata = lookupJpaMetadata(entityManager,
entityClass)
.orElseThrow(()->
_Exceptions.noSuchElement("cannot find JPA metadata for
entity %s", entityClass));
+ var classDescriptor =
_EclipseLinkInternals.getClassDescriptor(typeMetadata);
return new EntityOrmMetadata(
PersistenceStack.JPA,
- Optional.empty(), // if somebody knows how to implement this,
feel free to inform us
- Optional.empty(), // if somebody knows how to implement this,
feel free to inform us
+ Optional.of(classDescriptor.getTableName()),
+ //TODO not sure if this is the correct method to lookup the
schema String
+
Optional.of(classDescriptor.getDefaultTable().getTableQualifier()),
typeMetadata.getIdType().getJavaType(),
columns(typeMetadata),
- typeMetadata);
+ classDescriptor);
}
// -- HELPER
-
+
private Can<ColumnOrmMetadata> columns(final EntityType<?> typeMetadata) {
return _NullSafe.stream(typeMetadata.getSingularAttributes())
- .map(_MetadataUtil::column)
+ .map(EclipseLinkMetadataUtils::column)
.collect(Can.toCan());
}
-
- private ColumnOrmMetadata column(final SingularAttribute sa) {
- // if somebody knows how to implement this, feel free to inform us
- return null;
+
+ @SneakyThrows
+ private ColumnOrmMetadata column(final SingularAttribute<?, ?> sa) {
+ var databaseMapping = _EclipseLinkInternals.getDatabaseMapping(sa);
+ String colName = (databaseMapping instanceof AbstractColumnMapping
abstractColumnMapping)
+ ? abstractColumnMapping.getField().getName()
+ : "?";
+ return new ColumnOrmMetadata(colName, sa.getName(),
sa.getBindableJavaType().getName(), databaseMapping);
}
/**
* find the JPA meta-model associated with this (corresponding) entity
*/
- private Optional<EntityType<?>> lookupJpaMetamodel(
+ private <T> Optional<EntityType<T>> lookupJpaMetadata(
final EntityManager entityManager,
- final Class<?> entityClass) {
- return entityManager.getMetamodel().getEntities()
- .stream()
- .filter(type -> type.getJavaType().equals(entityClass))
- .findFirst();
+ final Class<T> entityClass) {
+
+ var entityType =
Try.call(()->entityManager.getMetamodel().entity(entityClass))
+ .getValue();
+ return entityType;
}
-
+
}
diff --git
a/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/_EclipseLinkInternals.java
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/_EclipseLinkInternals.java
new file mode 100644
index 00000000000..33745c8b35b
--- /dev/null
+++
b/persistence/jpa/eclipselink/src/main/java/org/apache/causeway/persistence/jpa/eclipselink/metamodel/_EclipseLinkInternals.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.persistence.jpa.eclipselink.metamodel;
+
+import java.lang.reflect.Method;
+
+import jakarta.persistence.metamodel.EntityType;
+import jakarta.persistence.metamodel.SingularAttribute;
+
+import org.eclipse.persistence.descriptors.ClassDescriptor;
+import org.eclipse.persistence.mappings.DatabaseMapping;
+
+import org.apache.causeway.commons.internal.base._Lazy;
+import org.apache.causeway.commons.internal.context._Context;
+
+import lombok.SneakyThrows;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+class _EclipseLinkInternals {
+
+ @SneakyThrows
+ ClassDescriptor getClassDescriptor(final EntityType<?> entityType) {
+ return (ClassDescriptor)
managedTypeImplGetDescriptor.get().invoke(entityType, new Object[0]);
+ }
+
+ @SneakyThrows
+ DatabaseMapping getDatabaseMapping(final SingularAttribute<?, ?> sa) {
+ return (DatabaseMapping) attributeImplGetMappingLazy.get().invoke(sa,
new Object[0]);
+ }
+
+ // -- HELPER
+
+ private _Lazy<Method> managedTypeImplGetDescriptor = _Lazy.threadSafe(()->{
+ var x =
_Context.loadClass("org.eclipse.persistence.internal.jpa.metamodel.ManagedTypeImpl");
+ return x.getMethod("getDescriptor", new Class[0]);
+ });
+
+ private _Lazy<Method> attributeImplGetMappingLazy = _Lazy.threadSafe(()->{
+ var x =
_Context.loadClass("org.eclipse.persistence.internal.jpa.metamodel.AttributeImpl");
+ return x.getMethod("getMapping", new Class[0]);
+ });
+
+}
diff --git
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
index b1c91b5dafe..8991e1bd4c5 100644
---
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
+++
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/JpaEntityFacet.java
@@ -25,9 +25,11 @@
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceUnitUtil;
-import org.springframework.data.jpa.repository.JpaContext;
+import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
+import org.springframework.data.jpa.repository.JpaContext;
+
import org.apache.causeway.applib.query.AllInstancesQuery;
import org.apache.causeway.applib.query.NamedQuery;
import org.apache.causeway.applib.query.Query;
@@ -47,7 +49,6 @@
import org.apache.causeway.persistence.jpa.applib.integration.HasVersion;
import lombok.Getter;
-import org.jspecify.annotations.NonNull;
import lombok.extern.log4j.Log4j2;
@Log4j2
@@ -58,6 +59,7 @@ class JpaEntityFacet
// self managed injections via constructor
@Inject private JpaContext jpaContext;
@Inject private IdStringifierLookupService idStringifierLookupService;
+ @Inject private OrmMetadataProvider ormMetadataProvider;
private final Class<?> entityClass;
private PrimaryKeyType<?> primaryKeyType;
@@ -267,7 +269,7 @@ public <T> T detach(final T pojo) {
// lazily looks up the ORM metadata (needs an EntityManager)
@Getter(lazy=true)
private final EntityOrmMetadata ormMetadata =
- _MetadataUtil.ormMetadataFor(getEntityManager(), entityClass);
+ ormMetadataProvider.ormMetadataFor(getEntityManager(),
entityClass);
// -- DEPENDENCIES
diff --git
a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/OrmMetadataProvider.java
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/OrmMetadataProvider.java
new file mode 100644
index 00000000000..b9ba42a8a60
--- /dev/null
+++
b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/OrmMetadataProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.causeway.persistence.jpa.integration.entity;
+
+import jakarta.persistence.EntityManager;
+
+import org.jspecify.annotations.NonNull;
+
+import
org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
+
+@FunctionalInterface
+public interface OrmMetadataProvider {
+
+ EntityOrmMetadata ormMetadataFor(
+ @NonNull EntityManager entityManager,
+ @NonNull Class<?> entityClass);
+}