Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Second Iteration of Our Shape Calculator: The Service

DZone's Guide to

Second Iteration of Our Shape Calculator: The Service

The second iteration of the shape calculator series, continuing to build Java Web Services.

· Java Zone
Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Introduction

If you have been with us since the beginning, you know we are mimicking the journey of some "useful" (let's pretend, shall we) piece of software that began life as just some code.

It is turning into something more serious, and we now have somewhat of a resuable component.

In our previous article, we fleshed out a first iteration of a simple command-line application.

Keep in mind that we are on our way to developing with and exploring various technologies of web applications and web services.

We are going to go through our second iteration in two main parts:

  1.  A new project for a persistence-enabled version of our component (the Shape Calculator Service)

  2. A new project for another command-line app that will utilize the above component.

New Component (Shape Calculator) Project

I chose to begin with a clean slate and created another Java project, setting it up just like in previous articles.  I named the project "shape-calc-jpa-hibernate", made sure to have the standard source folders (src/main/java, src/main/resources, etc), and to check all the same Eclipse settings, such as Build Path; made sure to convert the project into a Maven project, yadda-yadda.

If you did create a new project, then of course you will want to copy all of the packages and classes over from the first shape-calc-p5 project.

Entities and Keys

The Shape Calculator has at least 4 responsibilities.

  • Create a Calculation Request

  • Execute pending Calculation Requests and obtain Calculation Result(s)

  • Maintain list of Pending Requests

  • Maintain list of Calculated Results

Our calculator, given a Calculation Request, will yield a Calculation Result.

Image title

Expressed more mathematically, Calculation Result is a function of Calculation Request.

Image title

For every unique Calculation Request (CR), there exists at most only 1 Calculation Result (CRS). There is a one-to-one correspondence from CR to CRS. But NOT the other way. A CRS can be traced back to many CR(s).

But what is a CR, anyway? In our service, a CalculationRequest is comprised of three things:

  • Shape (or Shape Name, really). Examples: Circle, Square, Tetrahedron

  • Calculation Type. Examples: Area(Surface Area), Volume

  • Dimension (of an edge, or radius)

So, any unique combination of those three items make a CR, and when run through the Calculator, yield a unique CRS.

A Point to Keep In Mind — Sometimes a Simple @Id is Not Enough

Thus, one thing I think we can say, is that while a CalculationRequest can exist alone as an entity, we can't say that about a CalculationResult.

As a real example, any 2-dimensional shape of any size will yield a value of zero for a volume calculation. Thus, a CalculationResult can not just have in it a "result" member.  We need to know how the CalculationResult obtained its value.  Or, from which CalculationRequest was it produced.

That then means that a CalculationResult has a reference, or contains the originating CalculationRequest.

This is more important as we move into persisting our pending requests and calculated results, because we will need to have keys. We will need to insure that every CalculationRequest is unique in the underlying database.  The same applies to every CalculationResult.

Let's expand on the above, because we are ready to try to persist the data that our Shape Calculator Serivce is producing.

I have come across many articles about entities, and complex keys, etc...  but I had to end up going in a somewhat different direction for this component.

(This is not meant to be a tutorial on JPA / Hibernate, but I will touch on various points here and there.)

The CalculationRequest Members Together Compose the Key

I found that I was unable to (does someone have a better idea?) use a simple numeric @Id for either the CalculationRequest or the CalculationResult objects.  It seems to me that the CalculationRequest entity results in something that is in third-normal form (3NF).

That is, the ShapeName, the CalcType, and the Dimension, are all required (as a set) to determine the uniqueness of any given CalculationRequest.

And so, I had to create a complex key for the CalculationRequest entity (composed of ALL the members), and that then meant I did not have a simple @Id with which to join with the CalculationResult.

In any case, there wasn't much of a point to joining the two underlying data tables, because CalculationRequests are somewhat temporary, but not so CalculationResults.

Meaning, we only wish to persist the Requests until the Calculator runs the batch... and then we don't need them anymore, whereas we would want to keep the Results.

Our Entities for the Calculator

Thus, here is what we have for both of the entities:

CalculationRequest

@Entity
@Table( name="PENDING_REQUESTS")
@IdClass(value=RequestPk.class)
public final class CalculationRequest {

    @Id
    @Enumerated(EnumType.STRING)
    @Column(name="shapename",nullable=false)
    private ShapeName shapeName;

    @Id
    @Enumerated(EnumType.STRING)
    @Column(name="calctype",nullable=false)
    private CalcType calcType;

    @Id
    @Column(name="dimension",nullable=false)
    private Double dimension = new Double(0.0);

CalculationResult

@Entity
@Table(name="CALCULATION_RESULTS")
@IdClass(value=RequestPk.class)
public final class CalculationResult {

    @Id
    @Enumerated(EnumType.STRING)
    @Column(name="shapename",nullable=false)
    private ShapeName shapeName;

    @Id
    @Enumerated(EnumType.STRING)
    @Column(name="calctype",nullable=false)
    private CalcType calcType;

    @Id
    @Column(name="dimension",nullable=false)
    private Double dimension;

    @Column(name="result",nullable=false)
    private Double result;

    private boolean error = false;

And so, here is our key class, RequestPk:

public class RequestPk implements Serializable {

private ShapeName shapeName;

private CalcType calcType;

private Double dimension;

public RequestPk() {}

    // ... and so on ...


Spring Data JPA

For our new component project, it has all of the previous packages and classes as we had before, but there are also new ones, related to persisting the data.

Image title

Those new classes follow all of the pretty standard Spring Data JPA, and you can find many good articles on all that.

Of note are the two persistence interfaces:

CalculationRequestPersistService

public interface CalculationRequestPersistService {

    public void deleteAllRequests();

    public void saveRequest(CalculationRequest request);

    public List<CalculationRequest> getAllRequests();

    public void deleteRequest(CalculationRequest request);

    public long getNumRequests();
}

CalculationResultPersistService

public interface CalculationResultPersistService {

    public void saveResult(CalculationResult result);

    public void deleteResult(CalculationResult result);

    public void deleteAllResults();

    public List<CalculationResult> getAllResults();

    public CalculationResult findResultByRequest(CalculationRequest request);
}

You can go to https://github.com/elicorrales/shape-calc-jpa-hibernate/tree/master/shape-calc-jpa-hibernate/src/main/java/com/eli/calc/shape/persistence to look at the implementation of these, as well as the Repository classes (there's not much to them).

The Glue Between Pre-existing and New

One more interesting note is that the key changes, as far as our pre-existing component is concerned, were made inside the PendingRequests and CalculatedResults.

Image title

These are the objects that had been managing the data in our first iteration.

In our previous incarnation of the calculator, those objects just manipulated an internal Set:

private final Set<CalculationRequest> requests = new HashSet<CalculationRequest>();

For this persistence-enabled iteration, we replaced the above line of code with this:

@Autowired
private CalculationRequestPersistService requestsPersistService;

(Of course the CalculatedResults will have its own, similar change)

And then, of course, each method of the implementations are modified to use that new internal member, rather than the previous Set<>.

And that's it. A few changes in two classes, and we went from non-persistent to persistent data.

The upper layers, such as the calculator itself, or any clients or wrappers, have no knowledge of this change.

We are again, almost, ready to try out our second component, and it should function about the same as before. We need one more thing.

Persistence Java Config

In our first iteration of the shape-calc component, we already had an AppConfig java class. It doesn't have much in it other than to manage our ExecutorService thread pool.

And it still has not changed. I only changed the name to AppContext.

However, we should add a new config class. This class is what makes all the above changes work.

Image title

PersistenceContext has all of the standard stuff in it. You'll want to set up:

  • A reference to the Environment (to get properties)
  • DataSource
  • JpaVendorAdapter
  • EntityManagerFactory (LocalContainerEntityManagerFactoryBean)
  • JpaTransactionManager

At the class level, you will want to:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages={"com.eli.calc.shape.persistence.repository"})
public class PersistContext {


POM Dependencies

I believe our last step is to add these to our previous version of the POM:

<!-- =========== persistence dependencies ============= -->



<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-jpa</artifactId>

<version>1.10.2.RELEASE</version>

</dependency>



<dependency>

<groupId>com.zaxxer</groupId>

<artifactId>HikariCP</artifactId>

<version>2.4.7</version>

</dependency>



<dependency>

<groupId>org.hibernate</groupId>

<artifactId>hibernate-entitymanager</artifactId>

<version>5.1.0.Final</version>

</dependency>



<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.39</version>

</dependency>

Ok, we are ready to build the project.  We will want to run our same JUnit tests from the previous iteration of the calculator component.  And they should pass.

MySQL

However, we need an actual database.  (I suppose we could have gone with an in-memory one, but I chose MySQL).

I downloaded and installed mysql-5.7.12-winx64, right at the root c:\ level.

There is an initialization step or steps that you will need to do one time, and I am not going to cover those here.

You need the MySQL server and the client.

I chose to create a symbolic link from my local Cygwin bin directory, that points to the mysqld executable.   The article that comes before this one, discusses symbolic links.

Here is the startup of the MySQL server:

Image title

If you see the last line show up, you should be good to go.

Note: for Cygwin, you have to add the "--console" option when running the server. Otherwise you see no output at all.  I believe this is NOT the case with Window command.

The remaining steps (perhaps) are to setup a database user (other than root) and to create a database for these projects.

Take a look at the "application.properties" file. We had begun one from before, when we wanted to have our thread pool size to be a property. We can use this same file for our persistence values.

Go to: https://github.com/elicorrales/shape-calc-jpa-hibernate/blob/master/shape-calc-jpa-hibernate/src/main/resources/application.properties

Ok, I think we are ready to run our MySQL client.  I chose NOT to use the GUI-based workbench, but went with the command-line.

For this, I fired up the Cygwin setup utility, and selected to install the client.

(By the way, you always need to close any open Cygwin terminals when installing something from cygwin)

Here is the client running:

Image title

Notice I had previously created a "shapecalc" database. It currently has no tables.

Build and Test

Let's go ahead and maven clean and install.  Since we have JUnit tests, during the install, they will probably also build and run.  We will monitor them.

You can also monitor the database with the command-line client by repeated running this at the mysql> prompt:

use shapecalc;select count(*) as pending from pending_requests; select count(*) as results from calc_results;

Just use the up-arrow to recall the line.

Image title

Image title

Everything went very well:

Image title

Next Step: Second-Iteration App

You can review all of the latest changes by going to https://github.com/elicorrales/shape-calc-jpa-hibernate/tree/master/shape-calc-jpa-hibernate.

In the next article, we will finalize our second-iteration by constructing another comand-line app.  It is essentially the same app that we have already used, just with some added functionality.

To be continued in the next article!

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
java 8 ,spring data jpa ,hibernate ,mysql ,cygwin ,entities ,annotations ,primary key

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}