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

Reply via email to