What is the best practice for modeling Java classes with Hibernate? - java

I have a question about what would be the best practice for modeling my Java classes and how they should be related in the database. I'm using Spring-Boot and Spring-Data in the project
I have the situation containing professional Administrative, Professional Financial and Professional Developer. Each of them have specific characteristics.
So, I created a Professional class with the attibutos that are common to all employees:
Professional Class
#Entity
#Table(name = "Professional")
#Inheritance(strategy = InheritanceType.JOINED)
public class Professional {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String age;
... getters e setters
}
The other classes, extend from the professional class:
Administrative Class
#Entity
public class Administrative extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Financial Class
#Entity
public class Financial extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Developer Class
#Entity
public class Developer extends Professional {
private String attribute1;
private String attribute2;
... getters e setters
}
Is this the most appropriate way to handle this scenario? Should I use Class Composition instead of Inheritance? Could you please share examples?
Thank you

Inheritance makes more sense here. As an example, composition makes sense where an entity has another entity, e.g.
class Adult extends Person {
private List<Animal> pets; // person HAS pets (with are another class)
private ID identityCard; // person HAS an identity card
}
It wouldn't make sense here for a Person to extend either of Animal or ID because they aren't a subset of either. However, they are a child of the Person class so it makes sense to inherit.
In your case inheritance makes sense because your child classes are a subset of the Professional. You wouldn't say that a Developer has a Professional, but they are a Professional.

Related

Spring Boot: Abstracting Repository and Service

I have a similar entities and I use the following approach and use a base class:
#MappedSuperclass
public abstract class Dining {
// constructors
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dining_gen")
private long id;
#Column(nullable = false)
private UUID groupUuid;
#Column(nullable = false)
private String name;
}
#Entity
#SequenceGenerator(...)
#Table(...)
public class Room extends Dining {
// constructors
}
#Entity
#SequenceGenerator(...)
#Table(...)
public class Point extends Dining {
// constructors
}
I also use a base DTO, Request, etc. for these classes. However, I am wondering until which level I should apply abstraction. Should I use a generic base repository (Dining) and extend to Room and Point repositories?
What about services? I use Interface and its implementations in my Spring Bot app. In this scene, should I create a base interface or should I also create base service implementation and extend to Room and Point services?
If there is a good example as a best or proper practice, could you suggest me pls?

JPA Inheritance Two Or More Superclass

I made a research about Inheritance in JPA and resources that I found uses just one superclass for each entity. But there is not an example that uses 2 or more superclass.
What about this:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = “Abstract_One”)
public abstract class AbstractOne {
#Id
protected Long id;
…
}
#Entity(name = “A”)
#DiscriminatorValue(“A”)
public class A extends AbstractOne {
#Column
private int a;
…
}
#Entity(name = “B”)
#DiscriminatorValue(“B”)
public class B extends A {
#Column
private int b;
…
}
Is it possible to do that?
If it is possible, which Inheritance Strategy allows that and gives the best data consistency?
I can imagine only the following example
#MappedSuperclass
public class A
{
...
#Id
#Column(name = "RECID")
public Long getId()
...
}
#MappedSuperclass
public class B extends A
{
...
#Column(name = "COL1")
public String getColumn1()
...
}
#Entity(name="INH_TAB1")
public class C extends B
{
...
#Column(name = "COL2")
public String getColumn2()
...
}
Also at the excellent book "Java Persistence with Hibernate" by Bauer, King, Gregory I found the following plase what can be useful in the context of this question:
6.5 Mixing inheritance strategies
You can map an entire inheritance hierarchy with the TABLE_PER_CLASS,
SINGLE_TABLE, or JOINED strategy. You can’t mix them — for example, to switch from a
table-per-class hierarchy with a discriminator to a normalized table-per-subclass
strategy. Once you’ve made a decision for an inheritance strategy, you have to stick with it. This isn’t completely true, however. By using some tricks, you can switch
the mapping strategy for a particular subclass. For example, you can map a class
hierarchy to a single table, but, for a particular subclass, switch to a separate
table with a foreign key–mapping strategy, just as with table-per-subclass.
However, I can not imagine any real case when such complex inheritance hierarchy will be required/useful and also it can affect performance.

How to inherit from multiple base abstract classes in JPA

I faced a problem how I can create JPA entity which extends multiple base abstract classes (without creating additional table). I know there is an annotation #MappedSuperclass, but it gives an ability to create only one base class as soon as we use extends and multiple inheritance is not a Java feature.
For example, I have two bases:
#MappedSuperclass
public abstract class Authored {
#ManyToOne
private User user;
}
and
#MappedSuperclass
public abstract class Dated {
private String creationDate;
}
I expect that some of my models will extend only Authored, some -- only Dated, and some -- both.
Though it's only possible to write
#Entity
public class MyEntity extends Authored {
...
}
or
#Entity
public class MyEntity extends Dated {
...
}
Some discussions propose to inherit classes in line (e.g. Authored and AuthoredAndDated) but this seems too dirty, none of this bases logically can't extend another one.
ADDITION
I have to note that this style is supported in other frameworks like Django (due to multiple inheritance in python) so it's strange that JPA doesn't support it.
I am sorry to disappoint you, but there is no other solution than creating AuthoredAndDated as you suggested.
We faced in the same issue for our entities and went with the same procedure.
We have a
#MappedSuperclass
public class TimestampedObject {
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
}
and a
#MappedSuperclass
public class IdObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
}
Thus we created a TimestampedIdObject for this purpose.
Edit:
If you find another suitable solution, it would be great if you could post it here, as we have the same issue...
You should use an #Embeddable / #Embedded for goal by replacing inheritance with composition.
First, do not use #MappedSuperClass, but use #Embeddable instead for your classes you want to share the attributes with:
#Embeddable
public class Authored {...}
#Embeddable
public class Dated {...}
In the next step your Entity should not inherit from Authored or Dated but instead get an attribute referencing them:
#Entity
public class MyEntity {
#Embedded
private Authored authored;
#Embedded
private Dated dated;
}
If you want to get behaviour out of this, where you can generically access without those new attributes, you would need to introduce an interface exposing the necessary methods.
For expample if MyEntity should be able to provide details on last updates and creation, you would introduce an interface Authorable which defines to methods to access the relevant data.
public interface Authorable { /* necessary methods */ }
MyEntity will implement this interface then:
#Entity
public class MyEntity implements Authorable {
/* previous content plus implemented mehtods from interface */
}

Modeling Classes: hierarchy or attribute?

I have currently these 2 entities:
Titular and Familiar (Family/kin/relative).
Familiar [0..*]<------>[1] Titular
These 2 classes has commons attributes of a person (firstName, lastName, birthDate....) and they are 2 kind of Affiliate
I want to unify those in one super class (generalization) Person but what I can't figure out is I should make Titular and Familiar extend Person or add Person as attribute of them.
Person also has to exists by itself (cannot be abstract) and not all Persons are Affiliates
BUT! I also need a way to establish/handle commons behaviors for Titular and Familiar.
Person [1]<------>[0..*] Titular
Person [1]<------>[0..*] Familiar
Titular [1]<------>[0..*] Familiar
So the doubt is:
public class Titular extend Person
public class Familiar extend Person {
or
public class Titular implement Affiliate {
private Person person;
public class Familiar implement Affiliate {
private Titular t;
private Person person;
or (3rd thought)
public class Person {
public abstract class Affiliate {
protected Person person;
public class Titular extends Affiliate {
The way I see it, all Affiliates are persons too (or you have exceptions to this?)! So the right hierarchy is:
Person --- Affiliate --- Titular
\- Familiar
Now, as to inheriting or having a pointer... that is called composition v/s inheritance and there are good arguments for both. The main reasons for chosing composition are
Mutable or optional relationships: Say, the owner of a car can change or a car can have no owner. A cat can not stop to be an animal and be something else.
Different Public API: While more work, composition allows you to manually forward whatever API you want to expose from your internal pointer, hiding or changing things from the "parent" as you will.
In general, you'll find inheritance makes more sense when ClassA IS-A ClassB, so you would not expect that to change nor would you want both classes to present a different API. You'll see this recommendation everywhere and it seems to fit your example like a glove.
After a few test, I had decided for Composition over inheritance.
As one of tag is "JPA", this solution has to be mapped/annotated
..and none of the possible annotations suits
#Inheritance(strategy = InheritanceType.JOINED/SINGLE_TABLE/TABLE_PER_CLASS)
If Titular and Familiar extends from Person, the ORM required a "DTYPE" column in Person, which is useless for me because no matters in how many Titular or Familiar a Person can/will become in its life, it just must be ONE register of Person.
And being an Affiliate is a "concept" or behavior, which I need it to do some polymorphic tasks, it doesn't has any persistable attribute (FOR NOW!), I will make an Interface of it.
#Entity
public class Persona {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Entity
public class Titular implements Afiliado {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(nullable = false)
#ManyToOne(optional = false)
private Persona persona;
#Entity
public class Familiar implements Afiliado {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(nullable = false)
#ManyToOne(optional = false)
private Persona persona;
#ManyToOne
private Titular titular;
public interface Afiliado {
public String getNumero();
//trick but necessary
public Persona getPersona();
//another default Java 8 implementations..
}
Stated in verbose, we want the following behavior (If I understand you correctly):
Titular is a Person who supports the functionality of an Affiliate.
Familiar is a Person who supports the functionality of an Affiliate and is associated with a Titular.
After reading these two lines enough number of times, one possible solution that seems reasonable is:
public class Titular extends Person implements Affiliate {
public class Familiar extends Person implements Affiliate {
private Titular t;

JPA Mixed Inheritance Strategy

I have 3 entities:
#Entity
public abstract class A {
#Id
public Long id;
public String a1;
public String a2;
public String a3;
//much more fields
//getters and setters
}
#Entity
public class B extends A {
public String b1;
public String b2;
public String b3;
//much more fields
//getters and setters
}
#Entity
public class C extends A {
public String c;
//that's it. no more data
//getters and setters
}
I want to map these classes to 2 tables. First one will contain all of A and C data (i.e. SINGLE_TABLE inheritance). And the second one will contain B's data and a foreign key to A (i.e. JOINED inheritance).
I tried the solution proposed here, but it doesn't work for me. The attributes of BB1 and BB2 are also included into A.
How to implement such a strategy? Classes A and C are as different as Dog and Cat are, so I can't merge them into one class.
Also I don't want to use table-per-hierarchy which would result in duplicating lots of A's data.
JPA spec (paragraph 2.12) is saying that Support for the combination of inheritance strategies within a single entity inheritance hierarchy is not required by this specification.
Keeping that in mind, in similar cases I usually use JOINED strategy for all of my entities.
After spending so much time on this, and not getting any answer, I've come to this solution (which could be not the best):
#Entity
public abstract class A implements Serializable {
#Id
public Long id;
public String a1;
public String a2;
public String a3;
//much more fields
//getters and setters
}
#Entity
public class B implements Serializable {
#Id
#Column(name="id", nullable=false)
public Long id;
#MapsId
#OneToOne(optional=false)
#JoinColumn(name="id")
public A a;
public String b1;
public String b2;
public String b3;
//much more fields
//getters and setters
}
#Entity
public class C extends A {
public String c;
//that's it. no more data
//getters and setters
}
Conclusion
I was amazed at how such well-supported and popular technology as JPA does not offer a solution for such a trivial case. As pointed out by Pascal Thivent in his answer to this question, Hibernate is cheating us, using a secondary table, which is a very tedious and error-prone approach (you should manually specify for each field, to which table it belongs). So it looks like, there is still a room for improvement in JPA spec.

Categories