You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/07/01 09:21:04 UTC
[causeway] branch master updated: CAUSEWAY-2297: convenience: makes entity ORM metadata available through MM
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/master by this push:
new 8dcc71fdb6 CAUSEWAY-2297: convenience: makes entity ORM metadata available through MM
8dcc71fdb6 is described below
commit 8dcc71fdb62495963eb10db3944950ce2b0a7d17
Author: andi-huber <ah...@apache.org>
AuthorDate: Sat Jul 1 11:20:58 2023 +0200
CAUSEWAY-2297: convenience: makes entity ORM metadata available through
MM
- given an ObjectSpecification one can now access ORM metadata via the
EntityFacet
---
.../facets/object/entity/EntityFacet.java | 5 +
.../facets/object/entity/EntityOrmMetadata.java | 54 ++++++++++
.../object/entity/_EntityFacetForTesting.java | 5 +
.../metamodel/facets/entity/JdoEntityFacet.java | 47 ++-------
.../metamodel/facets/entity/_MetadataUtil.java | 114 +++++++++++++++++++++
.../jpa/integration/entity/JpaEntityFacet.java | 30 ++----
.../jpa/integration/entity/_MetadataUtil.java | 83 +++++++++++++++
7 files changed, 275 insertions(+), 63 deletions(-)
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityFacet.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityFacet.java
index b90372c98e..0d910321d7 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityFacet.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityFacet.java
@@ -162,6 +162,11 @@ public interface EntityFacet extends Facet {
PersistenceStack getPersistenceStack();
+ /**
+ * Provides associated table and column names.
+ */
+ EntityOrmMetadata getOrmMetadata();
+
// -- JUNIT SUPPORT
static EntityFacet forTesting(
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityOrmMetadata.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityOrmMetadata.java
new file mode 100644
index 0000000000..c3cf95192a
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/EntityOrmMetadata.java
@@ -0,0 +1,54 @@
+/*
+ * 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.core.metamodel.facets.object.entity;
+
+import java.util.Optional;
+
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.core.config.beans.PersistenceStack;
+
+import lombok.NonNull;
+import lombok.experimental.Accessors;
+
+// record candidate
+@lombok.Value @Accessors(fluent=true)
+public class EntityOrmMetadata {
+
+ @lombok.Value @Accessors(fluent=true)
+ public static class ColumnOrmMetadata {
+
+ final @NonNull String name;
+ final @NonNull String memberId;
+ final @NonNull String javaType;
+ //final @NonNull String sqlType;
+
+ final @NonNull Object vendorColumnMetadata;
+
+ }
+
+ final @NonNull PersistenceStack persistenceStack;
+ final @NonNull Optional<String> table;
+ final @NonNull Optional<String> schema;
+ final @NonNull Class<?> primaryKeyClass;
+
+ final @NonNull Can<ColumnOrmMetadata> columns;
+
+ final @NonNull Object vendorEntityMetadata;
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/_EntityFacetForTesting.java b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
index 7b197c7a3c..b94dc4026a 100644
--- a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
+++ b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/entity/_EntityFacetForTesting.java
@@ -106,4 +106,9 @@ class _EntityFacetForTesting implements EntityFacet {
throw _Exceptions.unsupportedOperation();
}
+ @Override
+ public EntityOrmMetadata getOrmMetadata() {
+ throw _Exceptions.unsupportedOperation();
+ }
+
}
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java b/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
index bb7d638e72..6e09b8a921 100644
--- a/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/JdoEntityFacet.java
@@ -29,10 +29,8 @@ import javax.inject.Inject;
import javax.jdo.FetchGroup;
import javax.jdo.PersistenceManager;
-import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
import org.datanucleus.api.jdo.JDOQuery;
import org.datanucleus.enhancement.Persistable;
-import org.datanucleus.identity.SCOID;
import org.datanucleus.store.rdbms.RDBMSPropertyNames;
import org.springframework.lang.Nullable;
@@ -55,6 +53,7 @@ import org.apache.causeway.core.config.beans.PersistenceStack;
import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.objectmanager.ObjectManager;
import org.apache.causeway.core.metamodel.services.idstringifier.IdStringifierLookupService;
@@ -99,7 +98,12 @@ implements EntityFacet {
// lazily looks up the primaryKeyTypeFor (needs a PersistenceManager)
@Getter(lazy=true, value = AccessLevel.PROTECTED) @Accessors(fluent = true)
private final PrimaryKeyType<?> primaryKeyTypeForDecoding = idStringifierLookupService()
- .primaryKeyTypeFor(entityClass, primaryKeyTypeFor(entityClass));
+ .primaryKeyTypeFor(entityClass, getOrmMetadata().primaryKeyClass());
+
+ // lazily looks up the ORM metadata (needs a PersistenceManager)
+ @Getter(lazy=true)
+ private final EntityOrmMetadata ormMetadata =
+ _MetadataUtil.ormMetadataFor(getPersistenceManager(), entityClass);
public JdoEntityFacet(
final FacetHolder holder, final Class<?> entityClass) {
@@ -208,43 +212,6 @@ implements EntityFacet {
return Optional.ofNullable(entityPojo);
}
- private Map<Class<?>, Class<?>> primaryKeyClassByEntityClass = new ConcurrentHashMap<>();
-
- private Class<?> primaryKeyTypeFor(final Class<?> entityClass) {
- return primaryKeyClassByEntityClass.computeIfAbsent(entityClass, this::lookupPrimaryKeyTypeFor);
- }
-
- private Class<?> lookupPrimaryKeyTypeFor(final Class<?> entityClass) {
-
- val persistenceManager = getPersistenceManager();
- val pmf = (JDOPersistenceManagerFactory) persistenceManager.getPersistenceManagerFactory();
- val nucleusContext = pmf.getNucleusContext();
-
- val contextLoader = Thread.currentThread().getContextClassLoader();
- val clr = nucleusContext.getClassLoaderResolver(contextLoader);
-
- val typeMetadata = pmf.getMetadata(entityClass.getName());
-
- val identityType = typeMetadata.getIdentityType();
- switch (identityType) {
- case APPLICATION:
- String objectIdClass = typeMetadata.getObjectIdClass();
- return clr.classForName(objectIdClass);
- case DATASTORE:
- return nucleusContext.getIdentityManager().getDatastoreIdClass();
- case NONDURABLE:
- return SCOID.class;
- case UNSPECIFIED:
- default:
- throw new IllegalStateException(String.format(
- "JdoEntityFacet was installed on '%s', "
- + "yet this entity has IdentityType '%s', "
- + "which is not supported by the framework's "
- + "JDO implementation.",
- entityClass.getName(), identityType));
- }
- }
-
@Override
public Can<ManagedObject> fetchByQuery(final Query<?> query) {
diff --git a/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/_MetadataUtil.java b/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/_MetadataUtil.java
new file mode 100644
index 0000000000..c65cb46570
--- /dev/null
+++ b/persistence/jdo/datanucleus/src/main/java/org/apache/causeway/persistence/jdo/datanucleus/metamodel/facets/entity/_MetadataUtil.java
@@ -0,0 +1,114 @@
+/*
+ * 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.jdo.datanucleus.metamodel.facets.entity;
+
+import java.util.Optional;
+
+import javax.jdo.PersistenceManager;
+import javax.jdo.metadata.MemberMetadata;
+import javax.jdo.metadata.TypeMetadata;
+
+import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
+import org.datanucleus.identity.SCOID;
+
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.base._NullSafe;
+import org.apache.causeway.core.config.beans.PersistenceStack;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata.ColumnOrmMetadata;
+
+import lombok.NonNull;
+import lombok.val;
+import lombok.experimental.UtilityClass;
+import lombok.extern.log4j.Log4j2;
+
+@UtilityClass
+@Log4j2
+class _MetadataUtil {
+
+ EntityOrmMetadata ormMetadataFor(
+ final @NonNull PersistenceManager persistenceManager,
+ final @NonNull Class<?> entityClass) {
+
+ val pmf = (JDOPersistenceManagerFactory) persistenceManager.getPersistenceManagerFactory();
+ val typeMetadata = pmf.getMetadata(entityClass.getName());
+
+ return new EntityOrmMetadata(
+ PersistenceStack.JDO,
+ Optional.ofNullable(typeMetadata.getTable()),
+ Optional.ofNullable(typeMetadata.getSchema()),
+ primaryKeyTypeFor(pmf, typeMetadata, entityClass),
+ columns(typeMetadata),
+ typeMetadata);
+ }
+
+ // -- HELPER
+
+ private Can<ColumnOrmMetadata> columns(final TypeMetadata typeMetadata) {
+ return _NullSafe.stream(typeMetadata.getMembers())
+ .map(_MetadataUtil::column)
+ .collect(Can.toCan());
+ }
+
+ private ColumnOrmMetadata column(final MemberMetadata c) {
+ return Optional.ofNullable(c.getColumn()) // null if is plural
+ .map(colName->new ColumnOrmMetadata(colName, c.getName(), fieldType(c), c))
+ .orElse(null);
+ }
+
+ private String fieldType(final MemberMetadata c) {
+ return Optional.ofNullable(c.getFieldType())
+ .orElseGet(()->{
+ log.warn("failed to get java field type from JDO MemberMetadata in {}", c.getParent());
+ return Object.class.getName();
+ });
+ }
+
+ private Class<?> primaryKeyTypeFor(
+ final @NonNull JDOPersistenceManagerFactory pmf,
+ final @NonNull TypeMetadata typeMetadata,
+ final @NonNull Class<?> entityClass) {
+
+ val identityType = typeMetadata.getIdentityType();
+ switch (identityType) {
+ case APPLICATION: {
+ val contextLoader = Thread.currentThread().getContextClassLoader();
+ val nucleusContext = pmf.getNucleusContext();
+ val clr = nucleusContext.getClassLoaderResolver(contextLoader);
+ final String objectIdClass = typeMetadata.getObjectIdClass();
+ return clr.classForName(objectIdClass);
+ }
+ case DATASTORE: {
+ val nucleusContext = pmf.getNucleusContext();
+ return nucleusContext.getIdentityManager().getDatastoreIdClass();
+ }
+ case NONDURABLE:
+ return SCOID.class;
+ case UNSPECIFIED:
+ default:
+ throw new IllegalStateException(String.format(
+ "JdoEntityFacet was installed on '%s', "
+ + "yet this entity has IdentityType '%s', "
+ + "which is not supported by the framework's "
+ + "JDO implementation.",
+ entityClass.getName(), identityType));
+ }
+ }
+
+}
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 82cfb7ea22..af8083f23c 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,7 +25,6 @@ import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnitUtil;
-import javax.persistence.metamodel.EntityType;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.springframework.data.jpa.repository.JpaContext;
@@ -39,15 +38,16 @@ import org.apache.causeway.applib.services.repository.EntityState;
import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.commons.internal.assertions._Assert;
import org.apache.causeway.commons.internal.base._Casts;
-import org.apache.causeway.commons.internal.base._Lazy;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
import org.apache.causeway.core.config.beans.PersistenceStack;
import org.apache.causeway.core.metamodel.facetapi.FacetAbstract;
import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
import org.apache.causeway.core.metamodel.facets.object.entity.EntityFacet;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.services.idstringifier.IdStringifierLookupService;
+import lombok.Getter;
import lombok.NonNull;
import lombok.val;
import lombok.extern.log4j.Log4j2;
@@ -117,7 +117,7 @@ public class JpaEntityFacet
}
private Class<?> getPrimaryKeyType() {
- return getJpaEntityType().getIdType().getJavaType();
+ return getOrmMetadata().primaryKeyClass();
}
@Override
@@ -292,26 +292,10 @@ public class JpaEntityFacet
// -- JPA METAMODEL
- private final _Lazy<Optional<EntityType<?>>> jpaEntityTypeRef = _Lazy.threadSafe(this::queryJpaMetamodel);
-
- /**
- * get the JPA meta-model associated with this (corresponding) entity
- */
- private EntityType<?> getJpaEntityType() {
- return jpaEntityTypeRef.get().orElseThrow(_Exceptions::noSuchElement);
- }
-
- /**
- * find the JPA meta-model associated with this (corresponding) entity
- */
- private Optional<EntityType<?>> queryJpaMetamodel() {
- return getEntityManager().getMetamodel().getEntities()
- .stream()
- .filter(type -> type.getJavaType().equals(entityClass))
- .findFirst();
- }
-
-
+ // lazily looks up the ORM metadata (needs an EntityManager)
+ @Getter(lazy=true)
+ private final EntityOrmMetadata ormMetadata =
+ _MetadataUtil.ormMetadataFor(getEntityManager(), entityClass);
// -- DEPENDENCIES
diff --git a/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java
new file mode 100644
index 0000000000..37432cc01b
--- /dev/null
+++ b/persistence/jpa/integration/src/main/java/org/apache/causeway/persistence/jpa/integration/entity/_MetadataUtil.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.Optional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.metamodel.EntityType;
+import javax.persistence.metamodel.SingularAttribute;
+
+import org.apache.causeway.commons.collections.Can;
+import org.apache.causeway.commons.internal.base._NullSafe;
+import org.apache.causeway.commons.internal.exceptions._Exceptions;
+import org.apache.causeway.core.config.beans.PersistenceStack;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata;
+import org.apache.causeway.core.metamodel.facets.object.entity.EntityOrmMetadata.ColumnOrmMetadata;
+
+import lombok.NonNull;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+class _MetadataUtil {
+
+ EntityOrmMetadata ormMetadataFor(
+ final @NonNull EntityManager entityManager,
+ final @NonNull Class<?> entityClass) {
+
+ final EntityType<?> typeMetadata = lookupJpaMetamodel(entityManager, entityClass)
+ .orElseThrow(()->
+ _Exceptions.noSuchElement("cannot find JPA metadata for entity %s", entityClass));
+
+
+ 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
+ typeMetadata.getIdType().getJavaType(),
+ columns(typeMetadata),
+ typeMetadata);
+ }
+
+ // -- HELPER
+
+ private Can<ColumnOrmMetadata> columns(final EntityType<?> typeMetadata) {
+ return _NullSafe.stream(typeMetadata.getSingularAttributes())
+ .map(_MetadataUtil::column)
+ .collect(Can.toCan());
+ }
+
+ private ColumnOrmMetadata column(final SingularAttribute sa) {
+ // if somebody knows how to implement this, feel free to inform us
+ return null;
+ }
+
+ /**
+ * find the JPA meta-model associated with this (corresponding) entity
+ */
+ private Optional<EntityType<?>> lookupJpaMetamodel(
+ final EntityManager entityManager,
+ final Class<?> entityClass) {
+ return entityManager.getMetamodel().getEntities()
+ .stream()
+ .filter(type -> type.getJavaType().equals(entityClass))
+ .findFirst();
+ }
+
+}