1.什么是olingo?
Apache Olingo 是个 Java 库,用来实现 Open Data Protocol (OData)。 Apache Olingo 包括服务客户端和 OData 服务器方面。
Open Data Protocol (开放数据协议,OData)
是用来查询和更新数据的一种Web协议,其提供了把存在于应用程序中的数据暴露出来的方式。OData运用且构建于很多 Web技术之上,比如HTTP、Atom Publishing Protocol(AtomPub)和JSON,提供了从各种应用程序、服务和存储库中访问信息的能力。OData被用来从各种数据源中暴露和访问信息, 这些数据源包括但不限于:关系数据库、文件系统、内容管理系统和传统Web站点。
2.代码工程
实验目标
利用olingo实现oData协议
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>olingo</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.apache.olingo</groupId>
<artifactId>olingo-odata2-core</artifactId>
<version>2.0.11</version>
<exclusions>
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.olingo</groupId>
<artifactId>olingo-odata2-jpa-processor-core</artifactId>
<version>2.0.11</version>
</dependency>
<dependency>
<groupId>org.apache.olingo</groupId>
<artifactId>olingo-odata2-jpa-processor-ref</artifactId>
<version>2.0.11</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<activatedProperties>local</activatedProperties>
</properties>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>cloud</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<properties>
<activatedProperties>cloud</activatedProperties>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
config
package com.et.olingo.config;
import com.et.olingo.service.OdataJpaServiceFactory;
import org.apache.olingo.odata2.api.ODataServiceFactory;
import org.apache.olingo.odata2.core.rest.ODataRootLocator;
import org.apache.olingo.odata2.core.rest.app.ODataApplication;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
@Component
@ApplicationPath("/odata")
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(OdataJpaServiceFactory serviceFactory, EntityManagerFactory entityManagerFactory) {
ODataApplication oDataApplication = new ODataApplication();
oDataApplication
.getClasses()
.forEach( c -> {
if ( !ODataRootLocator.class.isAssignableFrom(c)) {
register(c);
}
});
register(new ODataServiceRootLocator(serviceFactory));
register(new EntityManagerFilter(entityManagerFactory));
}
@Path("/")
public static class ODataServiceRootLocator extends ODataRootLocator {
private OdataJpaServiceFactory serviceFactory;
@Inject
public ODataServiceRootLocator (OdataJpaServiceFactory serviceFactory) {
this.serviceFactory = serviceFactory;
}
@Override
public ODataServiceFactory getServiceFactory() {
return this.serviceFactory;
}
}
@Provider
public static class EntityManagerFilter implements ContainerRequestFilter,
ContainerResponseFilter {
public static final String EM_REQUEST_ATTRIBUTE =
EntityManagerFilter.class.getName() + "_ENTITY_MANAGER";
private final EntityManagerFactory entityManagerFactory;
@Context
private HttpServletRequest httpRequest;
public EntityManagerFilter(EntityManagerFactory entityManagerFactory) {
this.entityManagerFactory = entityManagerFactory;
}
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
EntityManager entityManager = this.entityManagerFactory.createEntityManager();
httpRequest.setAttribute(EM_REQUEST_ATTRIBUTE, entityManager);
if (!"GET".equalsIgnoreCase(containerRequestContext.getMethod())) {
entityManager.getTransaction().begin();
}
}
@Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
EntityManager entityManager = (EntityManager) httpRequest.getAttribute(EM_REQUEST_ATTRIBUTE);
if (!"GET".equalsIgnoreCase(requestContext.getMethod())) {
EntityTransaction entityTransaction = entityManager.getTransaction(); //we do not commit because it's just a READ
if (entityTransaction.isActive() && !entityTransaction.getRollbackOnly()) {
entityTransaction.commit();
}
}
entityManager.close();
}
}
}
controller
package com.et.olingo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class HelloWorldController {
@RequestMapping("/hello")
public Map<String, Object> showHelloWorld(){
Map<String, Object> map = new HashMap<>();
map.put("msg", "HelloWorld");
return map;
}
}
entity
service
package com.et.olingo.service;
import com.et.olingo.entity.Child;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.apache.olingo.odata2.api.ep.EntityProviderException;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataNotFoundException;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import org.apache.olingo.odata2.api.uri.info.*;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPADefaultProcessor;
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPAModelException;
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.util.List;
public class CustomODataJpaProcessor extends ODataJPADefaultProcessor {
private Logger logger = LoggerFactory.getLogger(getClass());
public CustomODataJpaProcessor(ODataJPAContext oDataJPAContext) {
super(oDataJPAContext);
}
@Override
public ODataResponse readEntitySet(final GetEntitySetUriInfo uriParserResultView, final String contentType) throws ODataJPAModelException, ODataJPARuntimeException, EdmException {
logger.info("READ: Entity Set {} called", uriParserResultView.getTargetEntitySet().getName());
try {
List<Object> jpaEntities = jpaProcessor.process(uriParserResultView);
return responseBuilder.build(uriParserResultView, jpaEntities, contentType);
} finally {
this.close();
}
}
@Override
public ODataResponse readEntity(final GetEntityUriInfo uriParserResultView, final String contentType) throws ODataJPAModelException, ODataJPARuntimeException, ODataNotFoundException, EdmException {
ODataResponse response = null;
if (uriParserResultView.getKeyPredicates().size() > 1) {
logger.info("READ: Entity {} called with key {} and key {}", uriParserResultView.getTargetEntitySet().getName(), uriParserResultView.getKeyPredicates().get(0).getLiteral(), uriParserResultView.getKeyPredicates().get(1).getLiteral());
} else {
logger.info("READ: Entity {} called with key {}", uriParserResultView.getTargetEntitySet().getName(), uriParserResultView.getKeyPredicates().get(0).getLiteral());
}
try {
Object readEntity = jpaProcessor.process(uriParserResultView);
response = responseBuilder.build(uriParserResultView, readEntity, contentType);
} finally {
this.close();
}
return response;
}
@Override
public ODataResponse createEntity(final PostUriInfo uriParserResultView, final InputStream content, final String requestContentType, final String contentType) throws ODataJPAModelException, ODataJPARuntimeException, ODataNotFoundException, EdmException, EntityProviderException {
logger.info("POST: Entity {} called", uriParserResultView.getTargetEntitySet().getName());
ODataResponse response = null;
try {
Object createdEntity = jpaProcessor.process(uriParserResultView, content, requestContentType);
if (createdEntity.getClass().equals(Child.class)) {
response = postProcessCreateChild(createdEntity, uriParserResultView, contentType);
} else {
response = responseBuilder.build(uriParserResultView, createdEntity, contentType);
}
} finally {
this.close();
}
return response;
}
@Override
public ODataResponse updateEntity(final PutMergePatchUriInfo uriParserResultView, final InputStream content,
final String requestContentType, final boolean merge, final String contentType) throws ODataException, ODataJPAModelException, ODataJPARuntimeException, ODataNotFoundException {
logger.info("PUT: Entity {} called with key {}", uriParserResultView.getTargetEntitySet().getName(), uriParserResultView.getKeyPredicates().get(0).getLiteral());
ODataResponse response = null;
try {
Object updatedEntity = jpaProcessor.process(uriParserResultView, content, requestContentType);
response = responseBuilder.build(uriParserResultView, updatedEntity);
} finally {
this.close();
}
return response;
}
@Override
public ODataResponse deleteEntity(DeleteUriInfo uriParserResultView, String contentType) throws ODataException {
logger.info("DELETE: Entity {} called with key {}", uriParserResultView.getTargetEntitySet().getName(), uriParserResultView.getKeyPredicates().get(0).getLiteral());
ODataResponse oDataResponse = null;
try {
this.oDataJPAContext.setODataContext(this.getContext());
Object deletedEntity = this.jpaProcessor.process(uriParserResultView, contentType);
oDataResponse = this.responseBuilder.build(uriParserResultView, deletedEntity);
} finally {
this.close();
}
return oDataResponse;
}
private ODataResponse postProcessCreateChild(Object createdEntity, PostUriInfo uriParserResultView, String contentType) throws ODataJPARuntimeException, ODataNotFoundException {
Child child = (Child) createdEntity;
if (child.getSurname() == null || child.getSurname().equalsIgnoreCase("")) {
if (child.getMother().getSurname() != null && !child.getMother().getSurname().equalsIgnoreCase("")) {
child.setSurname(child.getMother().getSurname());
} else if (child.getMother().getSurname() != null && !child.getFather().getSurname().equalsIgnoreCase("")) {
child.setSurname(child.getFather().getSurname());
} else {
child.setSurname("Gashi");
}
}
return responseBuilder.build(uriParserResultView, createdEntity, contentType);
}
}
package com.et.olingo.service;
import com.et.olingo.config.JerseyConfig;
import org.apache.olingo.odata2.api.ODataService;
import org.apache.olingo.odata2.api.ODataServiceFactory;
import org.apache.olingo.odata2.api.edm.provider.EdmProvider;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.processor.ODataContext;
import org.apache.olingo.odata2.api.processor.ODataSingleProcessor;
import org.apache.olingo.odata2.jpa.processor.api.ODataJPAContext;
import org.apache.olingo.odata2.jpa.processor.api.exception.ODataJPARuntimeException;
import org.apache.olingo.odata2.jpa.processor.api.factory.ODataJPAAccessFactory;
import org.apache.olingo.odata2.jpa.processor.api.factory.ODataJPAFactory;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
public class CustomODataServiceFactory extends ODataServiceFactory {
private ODataJPAContext oDataJPAContext;
private ODataContext oDataContext;
@Override
public final ODataService createService(final ODataContext context) throws ODataException {
oDataContext = context;
oDataJPAContext = initializeODataJPAContext();
validatePreConditions();
ODataJPAFactory factory = ODataJPAFactory.createFactory();
ODataJPAAccessFactory accessFactory = factory.getODataJPAAccessFactory();
if (oDataJPAContext.getODataContext() == null) {
oDataJPAContext.setODataContext(context);
}
ODataSingleProcessor oDataSingleProcessor = new CustomODataJpaProcessor(
oDataJPAContext
);
EdmProvider edmProvider = accessFactory.createJPAEdmProvider(oDataJPAContext);
return createODataSingleProcessorService(edmProvider, oDataSingleProcessor);
}
private void validatePreConditions() throws ODataJPARuntimeException {
if (oDataJPAContext.getEntityManager() == null) {
throw ODataJPARuntimeException.throwException(ODataJPARuntimeException.ENTITY_MANAGER_NOT_INITIALIZED, null);
}
}
public final ODataJPAContext getODataJPAContext()
throws ODataJPARuntimeException {
if (oDataJPAContext == null) {
oDataJPAContext = ODataJPAFactory.createFactory()
.getODataJPAAccessFactory().createODataJPAContext();
}
if (oDataContext != null)
oDataJPAContext.setODataContext(oDataContext);
return oDataJPAContext;
}
protected ODataJPAContext initializeODataJPAContext() throws ODataJPARuntimeException {
ODataJPAContext oDataJPAContext = this.getODataJPAContext();
ODataContext oDataContext = oDataJPAContext.getODataContext();
HttpServletRequest request = (HttpServletRequest) oDataContext.getParameter(
ODataContext.HTTP_SERVLET_REQUEST_OBJECT);
EntityManager entityManager = (EntityManager) request
.getAttribute(JerseyConfig.EntityManagerFilter.EM_REQUEST_ATTRIBUTE);
oDataJPAContext.setEntityManager(entityManager);
oDataJPAContext.setPersistenceUnitName("default");
oDataJPAContext.setContainerManaged(true);
return oDataJPAContext;
}
}
package com.et.olingo.service;
import org.springframework.stereotype.Component;
@Component
public class OdataJpaServiceFactory extends CustomODataServiceFactory {
//need this wrapper class for the spring framework, otherwise we face issues when auto wiring directly the CustomODataServiceFactory
}
application.yaml
spring.h2.console.enabled=true spring.h2.console.path=/console
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- GitHub - Harries/springboot-demo: a simple springboot demo with some components for example: redis,solr,rockmq and so on.
3.测试
启动spring Boot应用
元数据查看
http://localhost:8080/odata/$metadata
插入
查询
4.引用
- https://github.com/ECasio/olingo-spring-example
- Apache Olingo Library
- Spring Boot集成olingo快速入门demo | Harries Blog™