OData Service with Olingo V4 and MySQL database connection

2.6k views Asked by At

I was following some example in which we can able to build OData service with Olingo from Java (maven project). The provided example doesn't have any database interaction. They are using some Storage.class, which contains hard codded data.

You can find sample code on git. please refer example p0_all in provided url.

Does anyone knows how we can connect git example with some database and furthermore perform CRUD operations

Please do help me with some good examples or concept.

Thanking you in advance.

1

There are 1 answers

1
Seymore Throttle On

I recently built an oData producer using Olingo and found myself similarly frustrated. I think that part of the issue is that there really are a lot of different ways to build an oData service with Olingo, and the data access piece is entirely up to the developer to sort out in their own project.

Firstly, you need an application that has a database connection set up. So completely disregarding Olingo, you should have an app that connects to and can query a database. If you are uncertain of how to build a java application that can query a MySQL datasource, then you should Google around for tutorials that are related to that problem and have nothing to do with Olingo.

Next you need to write the methods and queries to perform CRUD operations in your application. Again, these methods have nothing to do with Olingo.

Where Olingo starts to come in to play is in your implementation of the processor classes. EntityCollectionProcessor, EntityProcessor etc. (note that there are other concerns such as setting up your CsdlEntityTypes and Schema/Service Document etc., but those are outside the scope of your question)

Lets start by looking at EntityCollectionProcessor. By implementing the EntityCollectionProcessor class you need to override the readEntityCollection() function. The purpose of this function is to parse the oData URI for the entity name, fetch an EntityCollection for that Entity, and then serialize the EntityCollection into an oData compliant response. Here's the implementation of readEntityCollection() from your example link:

public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat)
throws ODataApplicationException, SerializerException {

    // 1st we have retrieve the requested EntitySet from the uriInfo object
    // (representation of the parsed service URI)
    List<UriResource> resourcePaths = uriInfo.getUriResourceParts();
    UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet)   resourcePaths.get(0); 
    // in our example, the first segment is the EntitySet
    EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();

    // 2nd: fetch the data from backend for this requested EntitySetName
    // it has to be delivered as EntityCollection object
    EntityCollection entitySet = getData(edmEntitySet);

    // 3rd: create a serializer based on the requested format (json)
    ODataSerializer serializer = odata.createSerializer(responseFormat);

    // 4th: Now serialize the content: transform from the EntitySet object to   InputStream
    EdmEntityType edmEntityType = edmEntitySet.getEntityType();
    ContextURL contextUrl =   ContextURL.with().entitySet(edmEntitySet).build();

    final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName();
    EntityCollectionSerializerOptions opts =       EntityCollectionSerializerOptions.with().id(id).contextURL(contextUrl).build();
    SerializerResult serializerResult =  serializer.entityCollection(serviceMetadata, edmEntityType, entitySet, opts);
    InputStream serializedContent = serializerResult.getContent();

    // Finally: configure the response object: set the body, headers and status code
    response.setContent(serializedContent);
    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
    response.setHeader(HttpHeader.CONTENT_TYPE,   responseFormat.toContentTypeString());
}

You can ignore (and reuse) everything in this example except for the "2nd" step:

EntityCollection entitySet = getData(edmEntitySet);

This line of code is where Olingo finally starts to interact with our underlying system, and the pattern that we see here informs how we should set up the rest of our CRUD operations.

The function getData(edmEntitySet) can be anything you want, in any class you want. The only restriction is that it must return an EntityCollection. So what you need to do is call a function that queries your MySQL database and returns all records for the given entity (using the string name of the entity). Then, once you have a List, or Set (or whatever) of your records, you need to convert it to an EntityCollection.

As an aside, I think that this is probably where the disconnect between the Olingo examples and real world application comes from. The code behind that getData(edmEntitySet); call can be architected in infinitely different ways, depending on the design pattern used in the underlying system (MVC etc.), styling choices, scalability requirements etc.

Here's an example of how I created an EntityCollection from a List that returned from my query (keep in mind that I am assuming you know how to query your MySQL datasource and have already coded a function that retrieves all records for a given entity):

private List<Foo> getAllFoos(){
    // ... code that queries dataset and retrieves all Foo records
}

// loop over List<Foo> converting each instance of Foo into and Olingo Entity 
private EntityCollection makeEntityCollection(List<Foo> fooList){
    EntityCollection entitySet = new EntityCollection();

    for (Foo foo: fooList){
        entitySet.getEntities().add(createEntity(foo));
    }

    return entitySet;
} 

 // Convert instance of Foo object into an Olingo Entity
 private Entity createEntity(Foo foo){
    Entity tmpEntity = new Entity()
            .addProperty(createPrimitive(Foo.FIELD_ID, foo.getId()))
            .addProperty(createPrimitive(Foo.FIELD_FOO_NAME, foo.getFooName()));

    return tmpEntity;
}

Just for added clarity, getData(edmEntitySet) might look like this:

public EntityCollection getData(String edmEntitySet){
    // ... code to determine which query to call based on entity name

    List<Foo> foos = getAllFoos();
    EntityCollection entitySet = makeEntityCollection(foos);
    return entitySet;
}

If you can find an Olingo example that uses a DataProvider class, there are some basic examples of how you might set up the // ...code to determine which query to call based on entity name. I ended up modifying that pattern heavily using Java reflection, but that is totally unrelated to your question.

So getData(edmEntitySet) is a function that takes an entity name, queries the datasource for all records of that entity (returning a List<Foo>), and then converts that List<Foo> into an EntityCollection. The EntityCollection is made by calling the createEntity() function which takes the instance of my Foo object and turns it into an Olingo Entity. The EntityCollection is then returned to the readEntityCollection() function and can be properly serialized and returned as an oData response.

This example exposes a bit of the architecture problem that Olingo has with its own examples. In my example Foo is an object that has constants that are used to identify the field names, which are used by Olingo to generate the oData Schema and Service Document. This object has a method to return it's own CsdlEntityType, as well as a constructor, its own properties and getters/setters etc. You don't have to set your system up this way, but for the scalability requirements of my project this is how I chose to do things.

This is the general pattern that Olingo uses. Override methods of an interface, then call functions in a separate part of your system that interact with your data in the desired manner. Then convert the data into Olingo readable objects so they can do whatever "oData stuff" needs to be done in the response. If you want to implement CRUD for a single entity, then you need to implement EntityProcessor and its various CRUD methods, and inside those methods, you need to call the functions in your system (totally separate from any Olingo code) that create(), read() (single entity), update(), or delete().