My Java project uses JPA as a persistence solution. As I read more and more JPA tutorials and guides I find (for the sake of simplicity, maybe) that every writer uses the entity classes as their model classes. That's obviously a poor design choice with regards to code extensibility and maintainability: a decision to change the persistence solution would imply a complete refactoring (or rewriting) of the whole model.
Thing is, rewriting model classes corresponding to each entity class feels out of place and inelegant, since the would all just ditto the corresponding entity class (in my personal case), were it not for the JPA annotations. Writing them off as interfaces implemented by the entity classes feels wrong as well, be it for the potential inconsistency arising when I might need to expand the model with a class that does not correspond to an entity, or for the fact that in Java the generics used to represent @OneToMany
and @ManyToMany
relationships are not (and should not be) covariant with the abstracting interfaces.
I shall now post some code to exemplify my concern:
package com.depvin.pps.orm;
import javax.persistence.*;
@Entity
@DiscriminatorValue("E")
public class EmployeeEntity extends UserEntity {
@ManyToMany
@JoinTable(name = "EmployeeProject",
joinColumns = @JoinColumn(name = "employee"),
inverseJoinColumns = @JoinColumn(name = "project"))
List<ProjectEntity> projects;
public EmployeeEntity(String username) {
super(username);
}
public List<ProjectEntity> getProjects() {
return projects;
}
}
How should the corresponding model class/interface be realized, taking into account the matters I brought up above?
From my point of view it is obvious that it is a good design. An entity is a model class. It is (or should be) the manifestation of the business object which is part of the domain model.
You are right, you should not build your model for the persistence API. But I don't see in the example above where you do that. Except for the annotations, but those don't make up your model, its just metadata. You could even put that into an extra mapping file and would have straight model classes.
If you mean that you create your model classes from an existing database and the resulting classes do not express the business model, then you should think about your database model.
And you can at anytime change the persistence solution, I can't see why you need to stick to JPA here. It would be very easy, for example, to create a JSON from any entity class with just some annotations.
Last thing: JPA is only using the generic type of a collection, if you don't define the targetEntity
attribute in the @OneToMany
.
The annotations of JPA-Objects are object-relational mapping, so JPA-Objects are part of the object-relational mapping.
You are right, that you often see JPA objects as model objects in project architecture. I don't say that this solution is sufficient bad in every situation. But if you want to be clear you should separate the two aspects of object relational mapping layer and business layer. If you have done this, you maybe have technical redundancy but no SEMANTICAL redundancy according to SRP. SRP says that one code fragment should only be change for one purpose. If the mapping changes, then the ORM layer is the right place. If the business rules change go to the model layer.
Of course you may have code to transfer data between the layers. A "String name" may appear in the JPA-Object and in the Model-Object. But its not a mapping at all, its caching. Caching makes the redundancy that makes you uncomfortable. If you don't want to cache, delegate from the model layer to the JPA-Layer. But keep Business logic separated from the JPA-Layer.
After all: if you have a small project you won't face any real problems with a JPAObject-only solution. It all comes with the size. And then I recommend the BusinessObject -> DAO -> JPAObject structure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With