Low Code entity persisting in a REST Service

Martien van den Akker
Mar 10 · 11 min read
https://nl.m.wikipedia.org/wiki/Bestand:Weckpotten.jpg

One of the observations I had about Microservices, was that it seemed to me that we got back to coding services in Java. Although there are several frameworks, I got the idea that it is mostly a throwback to the 3GL. You may have learned through my articles that I have a strong background in ESB and SOA. These technologies support a more declarative way of building services. I started my career in the Oracle world as a 4GL and CASE (Oracle Designer) developer, that also put me on the path of declarative development and Low Code. And although I do like coding, I favor a declarative approach where possible. In this article, I want to take a look at how to create a Red Hat Fuse Spring Boot REST Service using the REST DSL. And combine it with database persistence using JPA. I found that this allowed me to create a REST service with very little coding. Basically, the only Java code in my example project is in the beans for the JSON (un)marshalling and persistence, and one convertor. But we’ll get to that.

I’ll describe all the bits and pieces of setting up the project, including spinning of a MySQL Database using Docker Compose.

The example project can be found in my GitHub account: animalorderrest.

So let’s get to it.

The Database

For the database, I choose to use a MySQL database. It is remarkably easy to spin off a database using Docker Compose. So, for the purpose of this article, I use that.

Chris Chuck wrote a great article on how to do this, I used his docker-compose script in my project. By the way, if you might use my Fuse Development Vagrant project, then it already has a provisioner for Docker Compose.
Kudos to my colleague Luberto how showed me how to integrate this with Maven. So you can do mvn docker-compose:up to startup the database. With mvn docker-compose:down you can shut it down again.

The project already has DDL scripts to create:

I also provided two scripts with a few rows of data:

This gives you the first set up to create a database and some base data to test the project.

The Entity Classes

As said I have a few beans to represent the entities of my data model:

These entities are annotated for both JSON as well as for JPA. The annotations in Order.java are of special interest:

At the top, you see the @Entity and @Table annotations. These mark this class as an entity to be stored in the database. The @Table annotation couples this class to the animalorder.Orders table.

With the @NamedQueries and @NamedQuery annotations, we can define multiple queries related to this entity. In this case I have only one, but this syntax allows for a comma-separated list of occurrences of @NamedQuery annotations. I’ll get to this query later.

There is a relationship between Order and Customer. In this case, the Order belongs to a particular Customer. This is annotated using the @ManyToOne relationship, where the @JoinColumn defines how this is implemented at the database side. Of course, there can also be other kinds of relationships, like @OneToMany. This is a simple example that demonstrates a multi-table model.

For the rest, it shows the @JsonProperty and related annotations.

The OpenJPA configuration part

The dependencies

First, let’s take care of the dependencies. I added the following to the pom.xml for JPA:

  • groupId: org.apache.camel, artifactId: camel-jpa

Like JDBC, JPA is an API definition. Therefore, besides camel-jpa we need an implementation. In this case Hibernate. Hibernate is the implementation that translates our changes in the entity to JDBC statements, and therefore we need a JDBC Driver implementation for our database, the MySql Connector for Java.

Regarding the JSON (un)marshaling we need Jackson:

  • groupId: org.apache.camel, artifactId: camel-jackson

And although not JPA-related, I added a dependency for Undertow:

  • groupId: org.apache.camel, camel-undertow

I created the application with the Spring Boot template, as I did with earlier article examples. Using the Camel REST DSL as in this article brings the need for the Spring Boot Starter Web. You could use several component implementations, but Red Hat recommends Undertow as the web container instead of Apache Tomcat. This probably has to do with the support on the HTTP server. So, I excluded Tomcat and added the Undertow camel-spring-boot starter. Also, I apparently needed the camel-undertow library explicitly.

The persistence definition

It took me quite a while to get my persistence definition correctly. It is defined in the META-INF/persistence.xml. It needs to be in the META-INF folder.

It groups all the definitions in a persistence-unit that is named animalorderdb. This is referenced by the beans in the camel-context, as we will see later.

It defines the classes that need to be persisted:

  • nl.vs.fuse.animalorder.entities.Order

The MySql JDBC driver properties:

  • property javax.persistence.jdbc.driver: com.mysql.cj.jdbc.Driver

Take special care of the following property:

  • property hibernate.hbm2ddl.auto: none

Possible other values are: validate, update, create, and create-drop. Probably only in a development scenario, you may want to generate the tables from the entity classes. However, being “raised” by a large database company, I wouldn’t recommend that. I prefer to explicitly design the data model. I’d generate the classes from the tables. Like I did earlier with my Oracle-type generator. It would be fun to try to create a tool that can generate the java classes from the same table definitions. So, to prevent Hibernate from creating your tables and mess things up set this property to none.

JPA Bean definitions

Now we have the persistence definition in place, let’s prepare the camel-context.xml. I added the following beans:

The first is the Camel JPA Component. This one refers to both the entityManagerFactory as well as the transactionManager. Then the JpaTransactionManager is defined, which also refers to the entityManagerFactory. The entityManagerFactory refers to the persistenceUnit that is defined in the META-INF/persistence.xml.

I did not explicitly configure for global transactions. I’ll leave that out of scope for this article.

REST DSL Configuration

As you may have guessed, I love the declarative style of the REST DSL. You can define it completely in the camel-context, and the Fuse tooling in Code Ready Studio even has a design tab for it:

REST Pane to declaratively configure the REST DSL

In the top part, you can see that I choose the undertow component. The component is defined with JSON as a Binding Mode. Notice the pull-down list where you can choose the values.

You can add or delete REST Elements, for which you can specify a path. Like /shop” in this example.

In the properties panel you can see other properties to set:

The Enable CORS checkbox may be applicable for JavaScript apps. For the id, a GUID is generated, but I’d like to change it to a more meaningful name.

In the source, this translates to:

REST DSL definition in the Camel Context

You can add dataFormatProperties like prettyPrint to have the JSON output formatted.

Within that rest element you can add REST Operations:

Add new Operation

You can select an HTTP verb and provide an ID. You must provide a URI and choose a route to direct to. So, it is handy to have a few skeleton-routes defined upfront.

Implement the Get Orders operation

Let’s take a look at the Get operation:

Define the properties of the get-orderid operation

Here the URI is orders/{orderId}. This results in a URL as http://localhost:8080/shop/orders/1. Where the 1 at the end denotes the id template parameter. The value of this id parameter is placed in the message-header variable with the same name.

It directs the flow to the direct:getOrders route:

Define the JPA Endpoint

The step of interest here is the to queryOrder as Of interest are the following two components:

  • entityType: jpa:nl.vs.fuse.animalorder.entities.Order

The entityType is mandatory. Since the get operation does not have a payload to send, it can just “entityType”. It’s going to query an Order, so it is better to refer to the Order entity class.

The namedQuery refers to the NamedQuery getOrder, defined in the Order class as described at the top of this article. This selects an Order based on an id with the “:id binding parameter.

This brings us to the question: how to set the binding parameter? You could choose to generate a query with a where clause that refers to a fixed value. However, that is very bad practice. It makes your solution prone to SQL Injection. And with an Oracle database, it is also a performance downgrade.

Camel supports this through the Message Header variable CamelJpaParameters. The value is expected to be a java.util.Map<String, Object>. The REST DSL as defined above provides the order id as the orderId header parameter. You can read more about it in the documentation of the JPA Component.

We need a bean with a setOrderId method that adds this header to the Map in the CamelJpaParameters Header variable. This is done in the JpaParameters class. If you’re not so familiar with beans yet, the autograph of the setOrderId method is of interest:

The setOrderId method has the Exchange exchange as a first parameter. Camel knows by the Exchange type to provide the Exchange object to it. Then using the @Header(“orderId”) annotation (where I moved the parameter name to a constant), Camel knows that the value of the orderId header variable is expected here. Remember that the REST DSL definition puts the value in the URI of the request in this header. Also, notice that in the Order class the id is defined as an int. However, the Map expects the value as an Object. Therefore, the parameter has to be defined as an Integer. We trust the JVM to implicitly cast the int to Integer.

The method checks if the Map in the header variable already exists. If not, it will initialize a HashMap and adds it to the CamelJpaParameters header. Then it will put the orderId header value on the Map.

The JpaParameters class needs to be defined in the camel-context as a bean. Then, right before invoking the JPA component, the method needs to be called:

setOrderId method call

Based on the autograph of the method, Camel knows what to provide.

The Delete operation

The delete operation is quite similar to the get. The REST DSL definition is exactly the same, apart from the HTTP-Verb, which is “delete”:

The delete operation in camel-context.xml

The route can also be pretty much the same. There is only one difference: the @NamedQuery annotation apparently does not support DML operations, only SQL queries. So, the URI is:

Delete Order URI

As you can see, instead of a namedQuery reference, we provide the query directly in the URI.

In this case, however, I’d like to show another way to provide the parameters. The URI supports a separate property called parameters. It is intended to refer to a bean, using a #. But, we can also provide a JSON snippet referencing the id parameter with the simple expression ${headers.id}. Because of this reference, the to element needs to be replaced with a toD. In the toD element, the expression is executed on runtime.

The value of the parameters property needs to be prefixed with a #. The JPA component needs the values provided as a java.util.Map, so we need to transform the JSON snippet to the Map. I found a neat solution on StackOverFlow that I translated into my solution. The StringToMapTypeConverter class converses the JSON string to a Map. It is defined in the camel-context.xml as a bean definition. You don’t need to do anything else than define it in a bean so that it is added to the registry. Camel will find it and uses it to transform the String into a Map. It will remove the # before the actual transformation.

The advantage of this method is the plumbing of the translation of the header variable to the Map is hidden. You don’t need to explicitly invoke a bean to set the Header variable. The side effect though, is that the URI is more complex. And in the case of multiple bind variables, this JSON snippet can be quite large, which will make the URI quite unreadable. The encoding of the quotes and ampersands in the XML Code makes this worse. And to be honest, the StringToMapTypeConverter is a bit less obvious than the JpaParameters class. I’m not that into Jackson that I could come up with this implementation.
But I liked this method too much to not show it as an option.

Implementing the Post and Put operation

The HTTP-verbs post and put are straightforward. In examples like in the Camel-in-Action book, you typically use the post-verb for create operations and put for updates. In this example, the working is the same. I implemented both verbs, but you may find that the post-operation for an existing order will update it.

The REST DSL configuration is slightly different:

REST DSL for the post-operation

The URI does not contain a parameter, only the primary resource reference. But in this example, you also see the Order bean as a type. The referenced route is also simpler. We only have to call the JPA component providing the entity to be saved:

Call JPA component with Order entity

The HTTP-put operation is actually the same. So, even though I implemented it to support the Update feature, the post/create operation also functions as an update.

Test the routes

I created a simple SoapUI project, with no TestSuite/TestCase, but with a few simple ad-hoc requests.

Order REST Service Test requests

To create an order, edit the payload as shown in this example:

Create an Order with the post of a JSON message

Make sure you provide a customer with an existing id. The fun thing in this example is that as a response you’ll get a complete customer object. Where did that come from? Remember that the Order entity has an annotation on the customer attribute linking to the Customer entity using the foreign key attributes. After the insert or update, Hibernate will do a query to reinitialize the entity objects to have a correct representation in memory. With an update, Hibernate will select the entities upfront to find out what to update.

Be aware that using a JPA implementation as Hibernate will cause many additional queries to the database. When doing performance tuning, your DBA may question you about those “ghost-queries”.

Updating using the PUT operation will work similarly.

To Get or Delete an order, choose the request for the particular method:

Get an Order

Notice that the parameter is of Style TEMPLATE. It is a resource, not a URI Query parameter.

Conclusion

I had fun creating this example, with so little code. Camel, REST DSL, and JPA do take a lot of plumbing work out of your hands. I think that creating a functional REST Service with multiple entities couldn’t be much easier.

Feel free to clone and/or fork my animalorderrest project and play around with it.

virtualsciences

Het blog van Virtual Sciences

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store