This is one of the most unconventional (controversial) articles on this blog. I will try to describe my position in detail. Naturally, if the project already exists, then usually no one bothers to change the storage layer - it takes a long time, threatens with errors and so on. This means that I continue to work with JPA in many projects, but this does not mean that I need to make mistakes in new projects as well.

Usually people don’t really think about it – well, there is JPA and there is, everyone uses it. Well, there are problems, so everyone has them. And if you take a few steps back and look at the big picture?


JPA essentially standardizing [Hibernate]( /). It is clear that alternative implementations have appeared over time ([EclipseLink]( /) first of all, in the hope that the Hibernate developers simply did not really master the features, but still they can be done qualitatively), but this does not change the concept itself.

The idea of JPA is that we can describe classes in such a way that we can get a tree of objects from a JPA request at once, and then be able to upload data, change and save data. To do this, simply creating instances of classes is not enough, you need to do magic with proxy classes and object managers that catch changes and upload/save. And then various “interesting” side effects begin.

This way we hide the relational nature of the database and SQL. It is clear that with this approach, it is simply inefficient to write a query, because it is far from obvious how it will be executed. It is more difficult to work, because we have 4 APIs: the basic programming API, Criteria API, HQL (Hibernate Query Language)/JPQL(Java Persistence Query Language ), SQL. In complex cases, you need to turn to SQL, but since 99% of the time there is no work with it, it is difficult for programmers. Just because of switching to another language with a bunch of its own features.

JPA Wrappers

Later, wrappers appeared over JPA – Spring Data and Panache. Their idea is simple – let’s not use all those complicated stuff from JPA. Let’s focus on working with one class - usually there are no special problems here.

Interestingly, some developers, having suffered with JPA, go to NoSQL – there are basically no such problems there due to the lack of JPA. At the same time, they find others, but this is a separate story.


Among the alternatives, [MyBatis] stand out ( /) and JetBrains Exposed.

MyBatis looks like an intermediate step from JPA to SQL. It doesn’t seem to be the first, but there is no SQL typing.

Exposed, of course, is focused on the Kotlin language. There is a DSL for working in SQL. 2 problems: there is no understanding that this is exactly Postgres (respectively, not full typing) and there is no code generation according to the scheme in the database. Since the project has been in the first year, it is most likely not expected. Just as I understood, there is no support for reactivity.


There was also a reverse movement - let’s make the most of SQL. But not in strings, but typed – for a specific version of the database and even a specific schema (ideally, the code is generated according to the schema in the database). Migrations will also be written in SQL. Thus, we get full access to a specific version of the database (it’s not for nothing that the Postgres version is already 15.2 and development continues).

This is represented by 2 libraries: [Querydsl]( /) and [jOOQ]( /). Querydsl has not been updated for several years, and jOOQ is a semi-open library.

The result

There is no ideal, so far I am using jOOQ and I am quite satisfied. I also think to use Spring Data repositories for simple cases.