by seadan83 | 2025-04-10 02:42:59+00:00
First, for definitions, I'd suggest we use wikipedia for ORM [1] and also Active Record Pattern [2].

I believe Active Record is a more specific implementation of something that is ORM-like. We can stop speaking of Active Record since my point holds for the more generic ORM, and therefore holds for Active Record as well.

To clarify my point, there is a fundamental impedance mismatch between object mapping of data vs relational database mapping of data. One implication of this is you cannot use database as a service. Interactions with database must instead be gated behind the ORM and the ORM controls the database interaction.

I'll note that database as a service is very powerful. For example, when there is an API contract exposing a value that is powered by some raw-dog SQL, when the database changes, anything using the API does not need to change. Only the SQL changes. In contrast, when an ORM exposes an object, an attribute might sometimes be loaded, sometimes not. A change to load or not load that attribute ripples through everything that uses that object. That type of change in ORM-land is the stuff of either N+1 problems, or Null-Pointers.

To back up a bit, let me re-iterate a bit about the impedance mismatch. Wikipedia speaks of this [1]: "By contrast, relational databases, such as SQL, group scalars into tuples, which are then enumerated in tables. Tuples and objects have some general similarity... They have many differences, though"

To drive the point home - in other words, you can't do everything in object world that you can do in a database 1:1. A consequence of this is that the ORM requires the application to view the database as a persistence store (AKA: data-store, AKA: object store, AKA: persistence layer). The ORM controls the interaction with database, you can't just use database as a data service.

I believe this point is illustrated most easily from queries.

To illustrate, let's pull some query code [3] from Java's Hibernate, a prototypical ORM.

```

public Movie getMovie(Long movieId) {

EntityManager em = getEntityManager(); Movie movie = em.find(Movie.class, new Long(movieId)); em.detach(movie); return movie;

}

```

So, getting a release year might look like this:

```

int movieId = 123;

Movie m = orm.getMovie(movieId);

return m.getReleaseYear();

```

In contrast, if we put some raw-dogged SQL behind a method, we get this code:

```

int movieId = 123;

return movieDao.getMovieReleaseYearByMovieId(movieId);

```

Now, let's illustrate. To do this, let us look at the example of finding the release year of the highest grossing movie. As a service, that looks like this:

```

return dao.findReleaseYearOfHighestGrossingMovie();

```

In contrast, as an ORM, you might have to load all Movies and then iterate. Maybe the ORM might have some magic sugar to get a 'min/max' value though. We can go on though, let's say we want to get the directors of the top 10 grossing movies. An ORM will almost certainly require you to load all movies and then iterate, or start creating some objects specifically to represent that data. In all cases, an ORM presents the contract is an an object rather than as an API call (AKA, a service).

For the update case, ORMs often do pretty well. ORMs can get into trouble with the impedance mismatch when doing things like trying to update joined entities. For example, "update all actors in movie X". Further, ORM (and objects) creates issues of stale/warm caches, nullity, mutability, performance, and more... What is worse, all of this is intrinsic, relational data and objects are intrinsically different.

[1] https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapp...

[2] https://en.wikipedia.org/wiki/Active_record_pattern

[3] https://www.baeldung.com/hibernate-entitymanager