Showing posts with label JPA. Show all posts
Showing posts with label JPA. Show all posts

Wednesday, 23 December 2015

JPA result lists and Java 8 streams

We recently changed a project to build using Java 8.
This decision was largely driven by the need to use the Date Time API available in Java 8 but several developers, including me, were itching to start using Functional Interfaces/programming and Lambdas.

Fortunately we have not gone down the road of "refactor" everything into Java 8 but new code is now being written using things like Streams and Lambdas. When the opportunity presents itself existing code is also being re-factored.

All was well until I started using Streams on result lists from JPA Entities (the same is valid for JPA query result lists). After changing code like this

into this
no results were being returned.

The result list is the returned by the zone.getPlaces() method whose type is List<Place>. No surprises there and in fact mocking this for a test would mean that the re-factored code would pass tests.
So what is causing the production code to behave differently?

Well the answer lies in the actual JPA implementation, in this case EclipseLink 2.5.2, and the IndirectList classes which are used to facilitate all the clever things which EclipseLink does under the hood. The actual explanation is too detailed to go into it here but there is a bug which does show what was wrong and, more importantly, that this has been fixed in EclipseLink versions 2.6 and beyond.

https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075

Worryingly tests which use mocks cannot not pick this problem up, tests which use the underlying JPA implementation would but do you want to write JUnit tests using the database?

Why is this important?

  • Because JUnit tests which mock database operations will provide false test results.
  • Because the JPA implementation we use for this project is provided by the application server we run the software on.
Newer versions of that application server which provide a later version of EclipseLink are available but that migration is a different story.

Tuesday, 23 April 2013

A tale of detached Entities, identity and equals with JPA

I was asked to do update an old project of mine in order to accommodate some new requirements for a web application which manages manufacturing cycle times. Data about the resulting cycles is stored in a Database for analysis through another system.

The existing project was specifically designed with Cathode Dip Painting plants in mind and wasn't suitable for generic assembly line monitoring. In particular a requirement that the current cycle and production lot state be persisted through Web Application downtimes/restarts was new.

A majority of the new requirements were cosmetic and easily added without having to reach into the plumbing of the asynchronous progress state monitoring.

Yesterday however I added the cycle start persistence and suddenly everything broke. Granted I had things like telephone calls breaking my concentration but still I was flummoxed about why the Production Lot data was not being processed as expected.

Basically a Production Lot has a maxCycles and a currentCycle property.
When the currrent cycle completes, the currentCycle property is decremented. If this value is < 1, the current production lot is deleted and the next one is retrieved via JPA. The next cycle is then started.

So, being the optimist that I was and between phone calls I simply added a merge(ProductionLot entity).
This one line of code broke the app such that Production Lots were not being processed correctly.

After 2 hours bashing my head against the telephone I went home hoping that today would bring a fresh head and ideas to the problem.

This morning all sorts of light bulbs were lit up. In what way can an entity merge cause a problem like this... Consult the literature... lo and behold

The merge method of an EntityManager is used when you want to either update an Entity persistently OR when you want to attach the Entity to the current Persistent Context. It will also create a new Entity for the current Persistent Context if one does not exist (essentially equivalent to a persist method call). It also returns the Entity you merged. But, and this is what was causing me the problems, it returns the attached version of the entity and not the original.

Somewhere in my code I was checking for equality between the current Production Lot and one returned by the ProductionLot Stateless Session EJB. This worked before because the Production Lot only changed when the current one was exhausted and new one returned. In merging the current Production Lot I broke the semantics because the equals semantic was no longer strong enough.

So currentProductionLot != nextProductionLot && currentProductionLot.equals(nextProductionLot) need to be added.

This is something that is normally bread and butter to me but I still made the elemental mistake. Why?

Was it the telephone calls or the desire to leave before 19:00? No, actually it was a lack of preparation made when changing the actual semantics of the system.

On the face of things: never take for granted that a one line change will not profoundly change things. They will and the possibility should always be taken into consideration when making them.