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;
@RestControllerpublic 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;
@Componentpublic 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=truespring.h2.console.path=/console
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- https://github.com/Harries/springboot-demo
3.测试
启动spring Boot应用
元数据查看
http://localhost:8080/odata/$metadata
插入
查询
4.引用
-
https://github.com/ECasio/olingo-spring-example
-
https://olingo.apache.org/
-
http://www.liuhaihua.cn/archives/710825.html