navs2 = et.getDeclaredSingularAttributes()
+ .stream()
+ .filter(attr -> attr.getPersistentAttributeType() == PersistentAttributeType.MANY_TO_ONE)
+ .map(attr -> buildNavAttribute(et, attr))
+ .collect(Collectors.toList());
+
+ result.getNavigationProperties().addAll(navs2);
+
return result;
}
@@ -225,13 +235,26 @@ public class JpaEdmProvider extends CsdlAbstractEdmProvider {
return p;
}
+ // Build NavProperty for 1:N or M:N associations
private CsdlNavigationProperty buildNavAttribute(EntityType> et, PluralAttribute, ?, ?> attr) {
CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName())
.setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName()))
- .setNullable(false); // for now
+ .setCollection(true)
+ .setNullable(false);
return p;
}
+ // Build NavProperty for N:1 associations
+ private CsdlNavigationProperty buildNavAttribute(EntityType> et, SingularAttribute, ?> attr) {
+
+ CsdlNavigationProperty p = new CsdlNavigationProperty().setName(attr.getName())
+ .setType(new FullQualifiedName(NAMESPACE, attr.getBindableJavaType().getSimpleName()))
+ .setCollection(false)
+ .setNullable(attr.isOptional());
+
+ return p;
+ }
+
}
diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java
index ed921484c1..6bca0e9896 100644
--- a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java
+++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityCollectionProcessor.java
@@ -48,10 +48,12 @@ public class JpaEntityCollectionProcessor implements CountEntityCollectionProces
private ServiceMetadata serviceMetadata;
private EntityManagerFactory emf;
private RepositoryRegistry repositoryRegistry;
+ private JpaEntityMapper entityMapper;
- public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry) {
+ public JpaEntityCollectionProcessor(EntityManagerFactory emf, RepositoryRegistry repositoryRegistry, JpaEntityMapper entityMapper) {
this.emf = emf;
this.repositoryRegistry = repositoryRegistry;
+ this.entityMapper = entityMapper;
}
@Override
@@ -115,71 +117,40 @@ public class JpaEntityCollectionProcessor implements CountEntityCollectionProces
}
- private EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo) {
+ /**
+ * Helper method to retrieve all entities of an entity set from an the backend database
+ * @param edmEntitySet
+ * @param uriInfo
+ * @return
+ */
+ protected EntityCollection getData(EdmEntitySet edmEntitySet, UriInfo uriInfo) {
EdmEntityType type = edmEntitySet.getEntityType();
- JpaRepository, ?> repo = repositoryRegistry.getRepositoryForEntity(type);
+ JpaRepository, ?> repo = (JpaRepository, ?>)repositoryRegistry.getRepositoryForEntity(type);
EntityCollection result = new EntityCollection();
repo.findAll()
.stream()
.forEach((it) -> result.getEntities()
- .add(map2entity(edmEntitySet, it)));
+ .add(entityMapper.map2entity(edmEntitySet, it)));
return result;
}
- private Entity map2entity(EdmEntitySet edmEntitySet, Object entry) {
-
- EntityType> et = emf.getMetamodel()
- .entity(entry.getClass());
-
-
- Entity e = new Entity();
- try {
- // First, we set the entity's primary key
-
- // Now map all properties
- et.getDeclaredSingularAttributes().stream()
- .forEach( (attr) -> {
- if ( !attr.isAssociation()) {
- Object v = getPropertyValue(entry,attr.getName());
- Property p = new Property(null, attr.getName(),ValueType.PRIMITIVE,v);
- e.addProperty(p);
-
- if ( attr.isId()) {
- e.setId(createId(edmEntitySet.getName(),v));
- }
- }
-
- });
-
- } catch (Exception ex) {
- throw new ODataRuntimeException("[E141] Unable to create OData entity", ex);
- }
-
- return e;
- }
-
- private Object getPropertyValue(Object entry, String name) {
- try {
- return PropertyUtils.getProperty(entry,name);
- } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
- throw new ODataRuntimeException("[E141] Unable to read property from entity, property=" + name, e);
- }
- }
-
- private URI createId(String entitySetName, Object id) {
- try {
- return new URI(entitySetName + "(" + String.valueOf(id) + ")");
- } catch (URISyntaxException e) {
- throw new ODataRuntimeException("[E177] Unable to create URI", e);
- }
- }
+ /**
+ * Helper method to get the total size of an entity set
+ * @param edmEntitySet
+ * @param uriInfo
+ * @return
+ */
private Long getCount(EdmEntitySet edmEntitySet, UriInfo uriInfo) {
- // TODO Auto-generated method stub
- return null;
+
+ EdmEntityType type = edmEntitySet.getEntityType();
+ JpaRepository, ?> repo = (JpaRepository, ?>)repositoryRegistry.getRepositoryForEntity(type);
+ EntityCollection result = new EntityCollection();
+
+ return repo.count();
}
}
diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java
new file mode 100644
index 0000000000..5fd863124f
--- /dev/null
+++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityMapper.java
@@ -0,0 +1,84 @@
+/**
+ *
+ */
+package org.baeldung.examples.olingo4.processor;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.metamodel.EntityType;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ValueType;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.springframework.stereotype.Component;
+
+/**
+ * Helper class that converts a JPA entity into an OData entity using
+ * available metadata from the JPA's EntityManagerFactory.
+ *
+ * @author Philippe
+ *
+ */
+@Component
+public class JpaEntityMapper {
+
+ private EntityManagerFactory emf;
+
+ public JpaEntityMapper(EntityManagerFactory emf) {
+ this.emf = emf;
+ }
+
+
+ public Entity map2entity(EdmEntitySet edmEntitySet, Object entry) {
+
+ EntityType> et = emf.getMetamodel()
+ .entity(entry.getClass());
+
+
+ Entity e = new Entity();
+ try {
+ et.getDeclaredSingularAttributes().stream()
+ .forEach( (attr) -> {
+ if ( !attr.isAssociation()) {
+ Object v = getPropertyValue(entry,attr.getName());
+ Property p = new Property(null, attr.getName(),ValueType.PRIMITIVE,v);
+ e.addProperty(p);
+
+ if ( attr.isId()) {
+ e.setId(createId(edmEntitySet.getName(),v));
+ }
+ }
+ });
+ } catch (Exception ex) {
+ throw new ODataRuntimeException("[E141] Unable to create OData entity", ex);
+ }
+
+ return e;
+ }
+
+
+ private Object getPropertyValue(Object entry, String name) {
+ try {
+ return PropertyUtils.getProperty(entry,name);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new ODataRuntimeException("[E141] Unable to read property from entity, property=" + name, e);
+ }
+ }
+
+ private URI createId(String entitySetName, Object id) {
+ try {
+ return new URI(entitySetName + "(" + String.valueOf(id) + ")");
+ } catch (URISyntaxException e) {
+ throw new ODataRuntimeException("[E177] Unable to create URI", e);
+ }
+ }
+
+
+
+}
diff --git a/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java
new file mode 100644
index 0000000000..23a5a7ac4b
--- /dev/null
+++ b/apache-olingo/olingo4/src/main/java/org/baeldung/examples/olingo4/processor/JpaEntityProcessor.java
@@ -0,0 +1,378 @@
+/**
+ *
+ */
+package org.baeldung.examples.olingo4.processor;
+
+import java.io.InputStream;
+import java.lang.reflect.Member;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+import javax.persistence.metamodel.SingularAttribute;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntityCollection;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.ex.ODataRuntimeException;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataLibraryException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.processor.EntityProcessor;
+import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.serializer.SerializerResult;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResource;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.baeldung.examples.olingo4.repository.EdmEntityRepository;
+import org.baeldung.examples.olingo4.repository.RepositoryRegistry;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.stereotype.Component;
+
+/**
+ * JpaEntityProcessor adapter.
+ * This implementation is heavily based on the Tutorial available
+ * at Olingo's site. It is meant to be an starting point for an actual implementation.
+ * Please note that many features from a full-fledged are missing
+ * @author Philippe
+ *
+ */
+@Component
+public class JpaEntityProcessor implements EntityProcessor {
+
+ private EntityManagerFactory emf;
+ private OData odata;
+ private ServiceMetadata serviceMetadata;
+ private RepositoryRegistry registry;
+ private JpaEntityMapper entityMapper;
+
+ public JpaEntityProcessor(EntityManagerFactory emf, RepositoryRegistry registry, JpaEntityMapper entityMapper) {
+ this.emf = emf;
+ this.registry = registry;
+ this.entityMapper = entityMapper;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.olingo.server.api.processor.Processor#init(org.apache.olingo.server.api.OData, org.apache.olingo.server.api.ServiceMetadata)
+ */
+ @Override
+ public void init(OData odata, ServiceMetadata serviceMetadata) {
+ this.odata = odata;
+ this.serviceMetadata = serviceMetadata;
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.olingo.server.api.processor.EntityProcessor#readEntity(org.apache.olingo.server.api.ODataRequest, org.apache.olingo.server.api.ODataResponse, org.apache.olingo.server.api.uri.UriInfo, org.apache.olingo.commons.api.format.ContentType)
+ */
+ @Override
+ public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
+
+ // First, we have to figure out which entity is requested
+ List resourceParts = uriInfo.getUriResourceParts();
+ InputStream entityStream;
+
+ UriResourceEntitySet rootResourceEntitySet = (UriResourceEntitySet) resourceParts.get(0);
+ EdmEntitySet rootEntitySet = rootResourceEntitySet.getEntitySet();
+ List rootPredicates = rootResourceEntitySet.getKeyPredicates();
+ EdmEntityType rootEntityType = rootEntitySet.getEntityType();
+
+
+ if ( resourceParts.size() == 1 ) {
+ entityStream = readEntity(rootEntitySet,rootPredicates,responseFormat);
+ }
+ else if ( resourceParts.size() == 2 ) {
+ UriResource part = resourceParts.get(1);
+ if ( !(part instanceof UriResourceNavigation)) {
+ throw new ODataRuntimeException("[E103] part type not supported: class=" + part.getClass().getName());
+ }
+
+ UriResourceNavigation navSegment = (UriResourceNavigation)part;
+
+ // We have three scenarios we must handle:
+ // Entity(x)/Related, where Related is a 1:N or M:N relationship => result is a collection
+ // Entity(x)/Related, where Related is a N:1 or 1:1 relationship => result is a single entity
+ // Entity(x)/Related(z), where Related is a 1:N or M:N relationship => result is a single entity
+ if (navSegment.getKeyPredicates().isEmpty()) {
+ if ( isOne2ManyProperty(rootEntityType,navSegment.getProperty())) {
+ entityStream = readRelatedEntities(rootEntitySet,rootPredicates,navSegment.getProperty(),responseFormat);
+ }
+ else {
+ // The relation must point to another entity type, so casting should be safe here
+ EdmEntityType resultType = (EdmEntityType)rootEntityType.getNavigationProperty(navSegment.getProperty().getName()).getType();
+ EdmEntitySet resultEntitySet = entitySetFromType(resultType);
+
+ entityStream = readEntity(resultEntitySet, navSegment.getKeyPredicates(), responseFormat);
+ }
+ }
+ else {
+ entityStream = readRelatedEntity(request, rootEntitySet,rootPredicates,navSegment.getProperty(),navSegment.getKeyPredicates(),responseFormat);
+ }
+
+ }
+ else {
+ // For now, we'll only allow navigation just to directly linked navs
+ throw new ODataApplicationException("[E109] Multi-level navigation not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
+ }
+
+ //4. configure the response object
+ response.setContent(entityStream);
+ response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+ response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
+
+ }
+
+
+ // Lookup the EntitySet associated with an EntityType
+ // In our example, we assume we have only one entityset for each entity type
+ private EdmEntitySet entitySetFromType(EdmEntityType type) {
+ return serviceMetadata
+ .getEdm()
+ .getEntityContainer()
+ .getEntitySets()
+ .stream()
+ .filter((s) -> s.getEntityType().getName().equals(type.getName()))
+ .findFirst()
+ .orElseThrow(() -> new ODataRuntimeException("[E144] No entity set found for type " + type.getFullQualifiedName()));
+ }
+
+ //
+ private boolean isOne2ManyProperty(EdmEntityType entityType, EdmNavigationProperty property) {
+ return entityType.getProperty(property.getName()) != null && property.isCollection();
+ }
+
+ private InputStream readEntity(EdmEntitySet entitySet, List keyPredicates,ContentType responseFormat) throws ODataApplicationException, ODataLibraryException {
+
+ Entity entity = readEntityData(entitySet,keyPredicates);
+
+ ContextURL contextUrl = ContextURL.with().entitySet(entitySet).build();
+ // expand and select currently not supported
+ EntitySerializerOptions options = EntitySerializerOptions
+ .with()
+ .contextURL(contextUrl)
+ .build();
+
+ ODataSerializer serializer = odata.createSerializer(responseFormat);
+
+ SerializerResult serializerResult = serializer.entity(serviceMetadata, entitySet.getEntityType(), entity, options);
+ return serializerResult.getContent();
+ }
+
+ private InputStream readRelatedEntities(EdmEntitySet rootEntitySet, List rootPredicates, EdmNavigationProperty property, ContentType responseFormat) throws ODataApplicationException {
+
+ Object jpaEntity = readJPAEntity(rootEntitySet, rootPredicates);
+ try {
+ Object set = PropertyUtils.getProperty(jpaEntity, property.getName());
+ EdmEntitySet entitySet = entitySetFromType(property.getType());
+ ContextURL contextUrl = ContextURL
+ .with()
+ .entitySet(entitySet)
+ .build();
+
+ EntityCollectionSerializerOptions options = EntityCollectionSerializerOptions
+ .with()
+ .contextURL(contextUrl)
+ .build();
+
+ EntityCollection result = new EntityCollection();
+
+ ((Collection