6. Tuning ORM down

Full-blown object-relational mapping maps database foreign keys as relations between objects. Instead of Integer breedId you have Breed breed where mapping information provides all the necessary low-level information about the foreign key. This mapping information can be stored elsewhere (typically XML mapping file) or as annotations directly on the entity (probably more popular nowadays). Relations can be mapped in any direction, even in reverse, for one or both directions and in any cardinality with annotations @OneOnOne, @OneToMany, @ManyToOne and @ManyToMany. If you map both directions you choose the owner’s side, which is kinda more “in control” of the relationship. Mapping foreign keys as relations is probably the most pronounced feature of ORM – and originally I wanted to make a case against it in this very book.

In an ironic twist I discovered that the way we bent JPA 2.1 works only on its reference implementation, that is EclipseLink1, this all more than a year too late. I was considering to stop any further work on the book after loosing this most important showcase point. After all I decided to go on with a weaker version after all. We will describe the troubles with to-one relations, their possible solutions – including the one that is not JPA compliant – and there are also other helpful tips, many Querydsl examples throughout and two Querydsl dedicated chapters.

Most of the points I’m going to explain somehow revolve around two rules:

  • Be explicit. For instance don’t leave persistence context (session) open after you leave the service layer and don’t wait for the presentation layer to load something lazily. Make explicit contract and fetch eagerly what you need for presentation, just like you would with plain SQL (but still avoiding ORM syndrome “load the whole DB”). Also, don’t rely on caches blindly.
  • Don’t go lazy. Or at least not easily. This obviously stems from the previous point, but goes further. Lazy may save some queries here and there, but in practice we rely on it too much. In most cases we can be explicit, but we’re lazy to be so (so let’s not go lazy ourselves either). There are places where FetchType.LAZY means nothing to JPA. Oh, they will say it’s a hint for a provider, but it’s not guaranteed. That’s nothing for me. Let’s face it – any to-one mapping is eager, unless you add some complexity to your system to make it lazy. It’s not lazy because you annotate it so, deal with it.

As a kind of shortcut I have to mention that Hibernate 5.x supports LAZY to-one relationships as you’d expect, more about it later in part on how lazy is (not) guaranteed. When talking about often vain LAZY on to-one, let me be clear that it is still preferred fetching for collections – and quite reliable for them as well. This does not mean you should rely on these lazy to-many relations when you want to fetch them for multiple entities at once. For instance if you want to display a page with the list of messages with attachments, possibly multiple of them for each message, you should not wait for lazy fetch. More about this in the chapter Avoid N+1 select.

Price for relations

For me the biggest problem is that JPA does not provide any convenient way how to stop cascading loads for @ManyToOne and @OneToOne (commonly named to-one in this book as you probably noticed) relationships. I don’t mind to-many relations, they have their twists, but at least their lazy works. But to-one typically triggers find by id. If you have a Dog that has an owner (type Person) and is of specific Breed you must load these two things along with a dog. Maybe they will be joined by the JPA provider, maybe not2, maybe they are already in second-level cache and will be “loaded” nearly “for free”. All these options should be seriously considered, analyzed and proved before you can say that you know what is going on in your application.

And it just starts there, because Person has an Address which – in case of a rich system – may further point to District, County, State and so on. Once I wanted to change something like Currency in a treasury system. It loaded around 50 objects – all of them across these to-one relations. This may happen when you naively want to change a single attribute in that currency. In SQL terms, that’s an update of a single column for a single row.

When you insert or update a Dog you can use em.getReference(clazz, id) to get a Breed object containing only id of the breed. This effectively works as a wrapped foreign key (FK). Heck, you can just new an empty Breed and set its id, you don’t even have to ask em for the reference. In case of update you will not save much as that Dog is probably managed already anyway. You can either update managed entity – which means it loaded previous values for all to-one FKs cascading as far as it needed – or you can try to merge the entity – but this works only if you overwrite it completely, it’s not usable for update of a single column. Or should you just use JPQL update and possibly remove all dogs from the second-level cache? How ORM is that?

Why JPA doesn’t provide better fetch control for finds? I want to work with this Dog object now, I’m not interested in its relations, just wrap those FK values into otherwise empty entity objects (like references) and let me do my stuff! How I wished I could just map raw FK value instead of related object… actually, you can, but while you are able to load such related object explicitly (find by id), you can’t join on a relationship defined so.

If you use EclipseLink you can do it, but that means you can’t switch to other JPA 2.1 providers reliably, not to mention that dropping the mapping is by no means a minor detour from standard path. However the problems with to-one ultimately led me to these extreme measures and I got rid of to-one mappings and never looked back. Because I based my work on reference implementation of JPA 2.1 (EclipseLink) it took me more than a year to discover I diverged from the standard – and even then only because I started to write tests for this book with both major JPA providers.

More about this in the chapter Troubles with to-one relationships. Other points are much less radical compared to this one. Long before I learnt that being explicit and less lazy is definitely better and we will talk about it in other sections.

How does this affect my domain model?

I stated that this book will not be about architecture, but this is the part where we have to tackle it a bit. If we talk about domain model, we probably also talk about domain driven design (DDD) best described in [DDD]. I can’t claim experience with DDD because I never saw it in practice, but it must work for some, reportedly, especially for complex business domains with lots of rules, etc. Reading [PoEAA] it is obvious that there’s a lot of synergy between DDD and ORM. One may even ask whether to use ORM without DDD at all. And I can’t answer that, sorry.

[PoEAA] recommends other patterns for simpler domain problems (Transaction Script, Table Module) with related data source patterns (Table Data Gateway, Row Data Gateway or Active Mapper). There are some newer architectural solutions as well, like Command Query Responsibility Segregation (CQRS) or Data, context and interaction (DCI), both of them usable with object-oriented languages, but not necessarily for every problem.

Back to domain, though. One antipattern often mentioned in relation to ORM/JPA is called Anemic domain model. Let’s quote master Martin Fowler a bit again:

I’m not able to slip out of this topic and make object purists happy, but there are many other styles of programming and the question (far beyond the scope of this book) is whether being purely OO is the most important thing after all. In most projects I treated my entities like java beans with just a little low-level logic to it – so it was mostly this “anemic domain model” song. But entity objects can be perfect data transfer objects (DTOs) in many situations. It is still far more advanced compared to amorphous rows of a ResultSet. We have dumb model, but we can utilize it in a domain if we need to.

I even contemplated something like “domain over entity model” solution where entity is as dumb as possible (it still has responsibility for me, it maps to a database table) and I can wrap it with whatever logic I want in a domain object. This even allows me to reverse some dependencies if I wish so – for instance, in a database my order items point to the order using a foreign key, but in my higher model it can be the other way around, invoice can aggregate items that may not even know about an order.

In any case, whatever we do, we can hardly get rid of all the notions of a particular data store we use, because any such “total abstraction” must lead to some mismatch – and that typically leads to complexity and often significant performance penalty as well. The way I go with this book is trying to get as much benefit from using the JPA without incurring too much cost. Maybe I’m not going for the best approach, but I’m trying to avoid the biggest pain points while accepting that I have SQL somewhere down there.

One thing I respect on Fowler et al. is that they try to balance costs/benefits and they neither push ORM always forward nor do they criticize it all the time without offering real alternatives for the complex cases. However, many other smart and experienced people dislike ORM. Listening to “pure haters” does not bring much to a discussion, but some cases are well-argued, offering options and you can sense the deep understanding of the topic.

When to tune down and when not?

I’d say: “Just use it out of the box until it works for you.” There are good reasons not to rely on something we don’t understand but in custom enterprise software there often is no other way. For many systems everything will be OK, but for slightly complicated database/domain models a lot of things can go wrong.

Performance problems

Most typically the performance degrades first. You definitely should start looking at the generated queries. Something can be fixed in the database (typically with indexes), some queries can be rewritten in the application but sometimes you have to adjust the mapping.

With to-one relations you either have ORM with reliable lazy fetch or you have to do tricks described in Troubles with to-one relationships or even change the mapping in a way that is not JPA compliant, as in Removing to-one altogether. There are also still JPA native queries and JDBC underneath – needless to say that all of this introduces new concept into the code and if we mix it too much the complexity is on the rise.

To-many has less problems but some mappers generate more SQL joins than necessary. This can be often mended by introducing explicit entity mapping for the associative table. This does not raise conceptual complexity but some query code may look awkward and more SQL like instead of JPA like. I argue that explicit joins are not that bad though.

Other typical problems involve N+1 select problem, but this type of problems is easy to avoid if we are explicit with our service contracts and don’t let presentation layer to initiate lazy load.

Strange persistence bugs

Most typical enterprise applications can keep persistence context short – and we should. When ORM got mainstream a lot of people complained about lazy load exceptions (like the Hibernate famous LazyInitializationException) but this results from misusing ORM. Programmers solved it with making the session (persistence context) longer and introduced Open Session in View (OSIV) pattern. This is now widely considered antipattern and there are many good reasons for that.

Using OSIV allowed to keep back-end and front-end programmers separated (with all the disadvantages when two minds do what one mind can do as well), dealing only with some common service API, but this API was leaky when it didn’t load all the data and let presentation fetch the rest on demand. Instead of making it explicit what data the service call provides for the view we let the view3 to perform virtually anything on the managed (“live”) entities. This means that the presentation programmers must be very careful what they do.

There is a serious consequence when the service layer loses control over the persistence context. Transaction boundaries will stop working as one would expect. When the presentation layer combines multiple calls to the service layer you may get unexpected results – results no one have ever consciously implemented.

  1. Presentation layer first fetches some entity A. Programmer knows it is read-only call – which is not reliable across the board, but let’s say they know it works for their environment – and because used entity objects as DTO.
  2. Based on the request we are servicing it changes the entity – knowing it will not get persisted in the database.
  3. There is some transactional read-write service call working with completely different type of entities B.

Result without OSIV is quite deterministic and if we hit some lazy-load exception than we should fetch all the necessary data in the first service call. With OSIV, however, modified A gets persisted together with changes to B – because they are both part of the same persistence context which got flushed and committed.

There is a couple of wrong decisions made here. We may argue that even when the presentation layer is not remote we should still service it with one coarse-grained call. Some will scowl at entities used as DTOs and will propose separate DTO classes (others will scowl at that in turn). But OSIV is the real culprit, the one that gives you unexpected behaviour spanning across multiple service calls. Even if the example (real-life one, mind you) seems contrived when we dropped OSIV from our project everything around transactions got much cleaner.