Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best approach for linking diverse entity types in JPA

Short version for the hasty:

There's various tables/entities in my domain model which have the same field (a UUID). There is a table where I need to link rows/instances of such entities to other JPA-managed entities. In other words, the instance of the field in that link table won't be known up-front. The two approaches I can think of are:

  • Use an abstract entity and a TABLE_PER_CLASS strategy, or
  • use an @MappedSuperClass store the class name of the instance in the link table as well, or something similar that lets me define logic for getting the actual instance from the right table.

Both have advantages and disadvantages in terms of complexity and performance. Which do you believe to be best, is there maybe a third option, or have you tried something like this in the past and would advice/strongly warn against?

Long version in case you want more background:

I have a database/object model wherein many types have a common field: a universally unique identifier (UUID). The reason for this is that instances of these types can be subject to changes. The changes follow the command model and their data can be encapsulated and itself persisted. Let's call such a change a "mutation". It must be possible to find out which mutations exist in the database for any given entity, and vice-versa, on which entity a stored mutation operates.

Take the following entities with UUIDs as an (extremely simplified) example: mutable entities

To store the "mutations", we use a table/entity called MutationHolder. To link a mutation to its target entity, there's a MutationEntityLink. The only reason this data isn't directly on the MutationHolder is because there can be direct or indirect links, but that's of little importance here so I left it out: mutation entities

The question comes down to how I can model the entity field in MutationEntityLink. There are two approaches I can think of.

The first is to make an abstract @Entity annotated class with the UUID field. Customer, Contract and Address would extend it. So it is a TABLE_PER_CLASS strategy. I assume that I could use this as a type for the entity field, although I'm not certain. However, I fear this might have a serious performance penalty since JPA would need to query many tables to find the actual instance.

The second is to simply use @MappedSuperClass and just store the UUID for an entity in the entity field of MutationEntityLink. In order to get the actual entity with that UUID, I'd have to solve it programmatically. Adding an additional column with the class name of the entity, or something else that allows me to identify it or paste it in a JPQL query would do. This requires more work but seems more efficient. I'm not averse to coding some utility classes or doing some reflection/custom annotation work if needed.

My question is which of these approaches seems best? Alternatively, you might have a better suggestion, or notice I'm missing something; for example, maybe there's a way to add a type column even with TABLE_PER_CLASS inheritance to point JPA to the right table? Perhaps you've tried something like this and want to warn me about numerous issues that would arise.

Some additional info:

  • We create the database schema, so we can add whatever we want.
  • A single table inheritance strategy isn't an option. The tables must remain distinct. For the same reason, joined inheritance doesn't seem a good fit either.
  • The JPA provider is Hibernate and using things that are not part of the JPA standard isn't an issue.
like image 391
G_H Avatar asked Mar 30 '16 11:03

G_H


People also ask

What is entity relationships in JPA?

JPA - Entity Relationships. This chapter takes you through the relationships between Entities. Generally the relations are more effective between tables in the database. Here the entity classes are treated as relational tables (concept of JPA), therefore the relationships between Entity classes are as follows:

What are the different types of access types in JPA?

JPA provides 2 types of access types, field, and property access types. The placement of @Id determines which strategy to be used. By default only a single strategy is followed. If you put @Id on the field – Entity will be AccessType.FIELD, if you put it on the getter – it will be AccessType.PROPERTY.

Which annotation should I use for the ID field in JPA?

As you learned in the Basic Entity Mapping tutorial, every Entity class in JPA must be annotated with @Entity and its id field should be annotated with @Id. There are several other optional annotations available, they will be discussed as part of Entity Mapping.

Why JPA entity classes should not be final?

Because various JPA implementations will try subclassing our entity in order to provide their functionality, entity classes must not be declared final. 2.2. The Id Annotation Each JPA entity must have a primary key which uniquely identifies it.


2 Answers

If the entities don't have anything in common besides having a uuid I'd use the second approach you describe: use MappedSuperclass. Making the common superclass an entity would prevent you to use a different inheritance strategy if needed, would require a table for that super entity even if no instances exist and from a business point of view it's just wrong.

The link itself could be implemented in multiple ways, e.g. you could subclass MutationEntityLink for each entity to map (e.g. CustomerMutationEntityLink etc.) or do as you described it, i.e. only store the uuid as well as some discriminator/type information and resolve programatically (we're using that approach for something similar btw.).

like image 184
Thomas Avatar answered Oct 18 '22 15:10

Thomas


You need to use @MappedSuperclass while inheriting associations/methods/properties whereas TABLE_PER_CLASS is generally used when you have entity and sub-entities. If there are entities having an association with the base class in the model, then use TABLE_PER_CLASS since the base class behaves like an entity. Otherwise, since the base class would include properties/attributes and methods which are general to such entities not related to each other, using @MappedSuperclass would be a better idea

Example1: You need to set alarms for some different activities like "take medicine", "call mom", "go to doctor" etc. The content of the alarm message does not matter, you will need a reminder. So use TABLE_PER_CLASS since alarm message, which is your base class is like an entity here.

Example2: Assume the base class AbstractDomainObject enables you to create login ID, loginName, creation/modification date for each object where no entity has an association with the base class, you will need to specify the association for the sake of clearing later, like "Company","University" etc. In this situation, using @MappedSuperclass would be better.

like image 24
Eray Balkanli Avatar answered Oct 18 '22 15:10

Eray Balkanli