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:

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

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.

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

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.