Simplified explanation of JPA relations
Tags: jpa, onetoone, onetomany, manytomany
Published: 2023-08-16 06:27:06

It was hard for me to grasp the concept of JPA relations until I started to invest more time in it. 

I will start with OneToOne, the easiest, what I find important in this relation is that we can insert ids for each participant making it bidirectional at no cost(except allocated space for parent id in child table).

![OneToOne](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkloo7aqbawh91g0qwxy.png)

It is not necessarily to map OneToOne bidirectionally and it does make sense to map it _LAZY_ otherwise N+1 problem is your friend if using JPA QueryBuilder. This is pretty strange because _LAZY_ is the source for N+1. True, however you can choose if it is a hidden N+1 or a managed one. When query a Parent through QueryBuilder, if OneToOne child is eagerly connected, JPA will run a query to find all parents, will create a list of child ids and for each id will run a query to get each child details, so N+1 through _EAGER_. This is hidden, you can spot it just by verifying generated queries. Only by using _LAZY_, you will be able to fetch Parents only. And yes you will have to fetch explicitly each child, still within N+1, the only difference is that first example is unmanaged, done by JPA behind the counter, second one is managed, you are fully aware of what happens.    

OneToMany is a bit complicated, and I wish I knew from the beginning that in order to not duplicate the rows in parent table, parent table id should be placed into the child not vice-versa. The reason is simple, if child id is placed in parent, parent has to replicate its rows for each child relation just because child id is present in parent table.

![OneToManyBidirectionalRelation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1n3jg3qmflu492fz0s77.png)

Here is how data in parent duplicates:

![OneToManyBidirectionalData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oqe4fka9j3goh23wfzpu.png)

A better approach would be to map child to parent by parent id and live it as is. It becomes ManyToOne now.

![OneToManyUnidirectionalRelation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/riqu3pp7toms6tzgshcs.png)

And data is aligned as expected:

![OneToManyUnidirectionalData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pcu0vkn972k80slig8eb.png)

The join table for OneToMany is an overhead. Not only it becomes redundant, requires additional space, and adds up to the complexity, it is also a source to mistakenly transform it into a ManyToMany relation.

![OneToManyJoinTableRelation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2erd86segbtiuv76bl3n.png)

For OneToMany relation join table does not save allocated space:

![OneToManyJoinTableData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y7oh1wiu9djxicv57af3.png)

ManyToMany is where a join table helps. Its apparent complexity saves database space, without a join table database size will increase unjustifiably by replicating data. 

Without join table:

![ManyToManyBidirectionalRelation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/und0iow0i4xtdc5xck1w.png)

Data is duplicated:

![ManyToManyBidirectionalData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xw8oycd5qn0p7ey0gkx.png)

Moving to join table and schema becomes comprehensible:

![ManyToManyJoinTableRelation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kymcp1hhcxh9dmhr2lu7.png)

Data also avoids redundancy except for ids:

![ManyToManyJoinTableData](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1801h9mxlkk5zo1j9iy1.png) 

**Offtopic**
Non relational databases are prone data replication issues. Mainly because it is hard to imagine a relational diagram. Though, not only non relational DBs are prone to this issue. Relational are too. For example if the architecture is based on Spark framework. The gain in performance by running tasks in parallel comes at the cost of removing foreign key relations from database. And when multiple DDL changes are made uncontrollably it is hard to imagine how data are linked. This is why Spark based architects run away from Hibernate/JPA, they will move to MyBatis or similar frameworks, even JDBC in this case would become a good choice. Most interesting is that after a while, Spark applications become Spark addictive. Due to data duplication it will be hard to optimize such databases. And the solution would be to increase the number of Spark workers. Unfortunately, even horizontal scaling is not indefinitely inflatable. It is easy to predict the refactoring to a new data storage solution, non relational inclusively, in this kind of projects.