Generics in POJO - Is this a good practice - java

I have a Base Class.
#Data
class BaseDocument{
String id;
String name;
//Other fields
}
Say I have many classes that extends BaseDocument one below.
class NoteDocument extends BaseDocument{
String description;
Long lastModifiedDate;
//etc
}
It does not make sense to me to send entire document to UI in some cases. Most of the cases I need only id and name.
So for every document I have a VO class.
#Data
class BaseVO {
private String id;
private String name;
}
#Data
class NoteVO extends BaseVO{
//Nothing here now
}
And in NoteDocument I have.
public NoteVO getVo(){
Assert.notNull(getId());
NoteVO noteVo = new NoteVO();
noteVo.setName(getName());
noteVo.setId(getId());
return noteVo;
}
Now I have to copy this method in all the classes that extends BaseDocument.
Instead, I changed my BaseDocument like below.
#Data
class BaseDocument<V extends BaseVO>{
String id;
String name;
public V getVo(Class className) {
Assert.notNull(getId());
V vo = null;
try {
vo = (V) className.newInstance();
vo.setName(getName());
vo.setId(getId());
} catch (IllegalAccessException|InstantiationException e){
e.printStackTrace();
}
Assert.notNull(vo);
return vo;
}
}
I am new to generics. My first question, is this a good practice. Are there any problems in using reflection to create instance, any performance issues? Is there any better way to do achieve (write less code) this.
Edit: Suppose I need to display note in UI, Along with note I need to display name of the User who created note. I am using mongodb, when I save the note I also save UserVO in note, which will have user id and name of the user. If I save only user id while saving the note, I will have to do one more query to get the name of user while displaying. I want to avoid this.

Do not use reflection; use inheritance and maybe covariant return types instead. It will be faster, clearer, more precise, and easier to maintain. You may also find it useful to add methods to populate your VOs incrementally. I didn't come up with a clean way to apply generics to this situation, but I don't think you need them:
class BaseVO {
String id;
String name;
void setId(String id) {
this.id = id;
}
void setName(String name) {
this.name = name;
}
}
class NoteVO extends BaseVO {
// ...
}
#Data
class BaseDocument {
String id;
String name;
//Other fields
protected void populateBaseVO(BaseVO vo) {
vo.setId(id);
vo.setName(name);
}
public BaseVO getVO() {
BaseVO vo = new BaseVO();
populateBaseVO(vo);
return vo;
}
}
#Data
class NoteDocument extends BaseDocument {
String description;
Long lastModifiedDate;
// ....
protected void populateNoteVO(NoteVO vo) {
populateBaseVO(vo);
// ...
}
public NoteVO getVO() {
NoteVO vo = new NoteVO();
populateNoteVO(vo);
return vo;
}
}

Related

What's the best way to add a new Object using the HTTP POST call through Spring?

I'm learning the Spring Framework and I'm struggling with the Rest services with spring, in particular for the POST call that it's supposed to add a new object to the database.
I've seen a lot of different implementations through the web, but I don't know how to pick the best.
Let's take for example a film class:
#Entity
public class Film {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String title;
private String description;
//Constructor, Getter and Setter Omitted.
}
Assuming the repository extends the JpaRepository<Film,Long>, this would be the Controller class:
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
//Implementation #1
#PostMapping("/film")
public Film addNew(#RequestBody Map<String,String> body){
String title = body.get("title");
String description = body.get("description");
return filmRepository.save(new Film(title,description));
}
//Implementation #2
#PostMapping("/film")
public Film addNew(String title, String description){
Film film = new Film(title,description);
System.out.println(film.getTitle() + " " + film.getDescription());
return filmRepository.save(film);
}
//Implementation #3
#PostMapping("/film")
public Film addNew(#RequestBody Film newFilm){
return filmRepository.save(newFilm);
}
}
Why some implementations have as parameter a Map<String, String> ? Is that a body mapped to a key/value pair ?
Also bear in mind that I managed to implement correctly just the implementation #2, the first and the third gave me a
415 error:"Unsupported Media Type" org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'multipart/form-data;boundary=--------------------------901298977805450214809889;charset=UTF-8' not supported]
(Despite I followed the official Spring tutorial) on REST services.
I also read something about the creation of DTO classes where I can define attributes without exposing the object to the controller, how can be implemented such solution?
Implementation 3 is the best practice, but you should create a lightweight DTO class (maybe FilmDto) to avoid exposing the internal structure of your entity, please see LocalDTO, Martin Fowler.
You may use ModelMapper to map FilmDto to Film, and make sure there are proper getters and setters in both classes, if the getters and setters have the same names in both classes, then ModelMapper will do the conversion smoothly:
public class FilmDto {
private long id;
private String title;
private String description;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
and you controller:
#RestController
#RequestMapping("/api")
public class FilmController {
private final FilmRepository filmRepository;
private ModelMapper modelMapper = new ModelMapper();
#Autowired
public FilmController(FilmRepository filmRepository) {
this.filmRepository = filmRepository;
}
//Implementation #3
#PostMapping("/film")
public ResponseEntity<FilmDto> addNew(#RequestBody FilmDto filmDto){
Film newFilm = modelMapper.map(filmDto, Film.class);
newFilm = filmRepository.save(newFilm);
filmDto.setId(newFilm.getId());//you may use modelMapper here
return ResponseEntity.ok(filmDto);
}
}
you can test using postman by passing the film as below:
{
"title": "some title",
"description": "some description"
}
and the body should be of type "raw", "JSON".
Why some implementations have as parameter a Map<String, String> ?
some implementations use map<key,value> because they need the properties that map interface provide such as non-duplicate key value or the classes that implement map interface such as TreeMap and LinkedHashMap.
about your implementation of the class FilmController i think its not necessary to use map<String,String> for posting your domain in the data base simply you can have this implementation
#RestController
public class FilmController {
#Autowired
FilmRepository filmRepository;
#PostMapping("/film")
public ResponseEntity addNew(#RequestBody Film film){
return ResponseEntity.ok(filmRepository.save(film));

Mapstruct: Clear Collection on update when using Adders

I try to map my DTO objects to my JPA entities. I have a Collection of children in my ParentEntity. They can be added addChild(). Using the Adder is supported by Mapstruct via the CollectionMappingStrategy (http://mapstruct.org/documentation/dev/reference/html/#collection-mapping-strategies).
This works fine if I create new entities, but fails to clear the children on updating before adding the new children.
The Mapstruct manual says (http://mapstruct.org/documentation/dev/reference/html/#updating-bean-instances):
Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map.
What am I missing? Is there an additional option I have to set? There is a full example with test case to reproduce the problem at https://github.com/davidfuhr/mapstruct-jpa-child-parent
Here are the classes:
public class ParentEntity {
private String name;
private List<ChildEntity> children = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildEntity> getChildren() {
return children;
}
public void addChild(ChildEntity child) {
children.add(child);
child.setMyParent(this);
}
public void removeChild(ChildEntity child) {
children.remove(child);
child.setMyParent(null);
}
}
public class ChildEntity {
private String name;
private ParentEntity myParent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentEntity getMyParent() {
return myParent;
}
public void setMyParent(ParentEntity myParent) {
this.myParent = myParent;
}
}
public class ParentDto {
private String name;
private List<ChildDto> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildDto> getChildren() {
return children;
}
public void setChildren(List<ChildDto> children) {
this.children = children;
}
}
public class ChildDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ParentEntity toEntity(ParentDto s);
ParentEntity updateEntity(ParentDto s, #MappingTarget ParentEntity e);
#Mapping(target = "myParent", ignore = true)
ChildEntity toEntity(ChildDto s);
}
The text in the documentation need to be rephrased. The problem is that especially for collections, there's no good way to handle this out of the box in MapStruct. I'm currently writing some new text for the documentation.
Consider this (when thinking what MapStruct should do for updating collections in general):
What if there's no match: should the non-matching elements be removed?
Should the non matching source elements be added?
What exactly constitutes to a match: equals? hashcode? comparator==0?
Can there be more than one match (Lists, but also depending on what is considered a match.)
How should the resulting collection be sorted?
Should a newly created object be added to a persistence context?
What about JPA child-parent relations?
About the latter one, Dali (Eclipse) also generates remove methods. So should MapStruct call these in the light of the above?
At this moment it works like this: whenever the user wants a collection update method, MapStruct generates a regular call to element mappings (in stead of an update call), because it is the only sensible thing to do. All the remainder is highly dependent on the use-case. If you need to clear the collection at before hand, use the #BeforeMapping to clear it.
Note: I just fixed an issue that handles also adders in this fashion in stead of the vague error message you get now.
If you want a nice way to handle child/parent relations and integrate them with JPA.. have a look at the examples.

Java JPA Preventing Proxies from calling db

I have a spring boot (1.5.4.RELEASE) project using Java 8. I have an entity and it's related domain class like this:
#Entity
#Table(name = "Foo", schema = "dbo")
public class FooEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id")
private int id;
#Column(name="Name")
private String name;
#Column(name="Type")
private String type;
#Column(name="Color")
private String color;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "Car")
private Car car;
//getter and setter
}
public class Foo {
private int id;
private String name;
private String type;
private String color;
private Car car;
//Constructors and getters
}
I want to create a repository that fetches this Foo object from the DB but only fetching the complex fields if the user asks for them to prevent unnecessary join statements. The repo looks like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain().apply(entity);
}
}
So a barebones call for a Foo object will return the Entity with values for all the fields except for the car field. If the user wants car information then they have to explicitly call withCar.
Here is the mapper:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return entity -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), e.getCar());
};
}
}
The problem is when you do e.getCar() if the value is not there (i.e. there's a proxy present) JPA will go out and fetch it for you. I don't want this to be the case. It will just grab the values and map them to the domain equivalent if it's not there then null.
One solution that I've heard (and tried) is calling em.detach(entity); however, this doesn't work as I intended because it throws an exception when you try to access getCar and I've also heard this is not best practice.
So my question is what is the best way to create a repo using a builder pattern on a JPA entity and not have it call the DB when trying to map.
You could create a utility method that will return null if the given object is a proxy and is not initialized:
public static <T> T nullIfNotInitialized(T entity) {
return Hibernate.isInitialized(entity) ? entity : null;
}
Then you can call the method wherever you need it:
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), nullIfNotInitialized(e.getCar()));
Just map it to a new object and leave out the Car relation, this is the standard approach. You can use MapStruct and just ignore the car field during mapping: http://mapstruct.org/documentation/stable/reference/html/#inverse-mappings
Just don't map the car... Map a field holding the ID and use another method to get the actual Car. I would use a distinctive method name, to differentiate it from the other getters.
class FooEntity {
#Column
private int carId;
public int getCarId() {
return carId;
}
public void setCarId(int id) {
this.carId = id;
}
public Car fetchCar(CarRepository repo) {
return repo.findById(carId);
}
}
You can write query on top of JPA
#Query("select u from Car c")
import org.springframework.data.repository.CrudRepository;
import com.example.model.FluentEntity;
public interface DatabaseEntityRepository extends CrudRepository<FooEntity , int > {
}
As you said
I don't want this to be the case. It will just grab the values and map them to the domain equivalent, if it's not there then null.
Then you just set it to null, because the field car will always not be there.
Otherwise, if you mean not there is that the car not exists in db, for sure a subquery(call the proxy) should be made.
If you want to grab the car when call Foo.getCar().
class Car {
}
class FooEntity {
private Car car;//when call getCar() it will call the proxy.
public Car getCar() {
return car;
}
}
class Foo {
private java.util.function.Supplier<Car> carSupplier;
public void setCar(java.util.function.Supplier<Car> carSupplier) {
this.carSupplier = carSupplier;
}
public Car getCar() {
return carSupplier.get();
}
}
class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain() {
return (FooEntity e) -> {
Foo foo = new Foo();
foo.setCar(e::getCar);
return foo;
};
}
}
Make sure you have the db session ,when you call Foo.getCar()
You could try adding state to your repository and influence the mapper. Something like this:
import static com.test.entities.QFooEntity.fooEntity;
import static com.test.entities.QCarEntity.carEntity;
#Repository
public class FooRepository {
private final JPAQuery<FooEntity> query = createQuery().from(fooEntity);
private boolean withCar = false;
public FooRepository getFooByName(String name) {
query.where(fooEntity.name.eq(name));
return this;
}
public FooRepository withCar() {
query.leftJoin(fooEntity.car, carEntity).fetchJoin();
withCar = true;
return this;
}
public Foo fetch() {
FooEntity entity = query.fetchOne();
return FooMapper.mapEntityToDomain(withCar).apply(entity);
}
}
In your mapper, you then include a switch to enable or disable car lookups:
public class FooMapper {
public static Function<FooEntity, Foo> mapEntityToDomain(boolean withCar) {
return e -> {
return new Foo(e.getId(), e.getName(), e.getType(), e.getColor(), withCar ? e.getCar() : null);
};
}
}
If you then use new FooRepository().getFooByName("example").fetch() without the withCar() call, e.getCar() should not be evaluated inside FooMapper
You may want to use the PersistentUnitUtil class to query if an attribute of entity object is already loaded or not. Based on that you may skip the call to corresponding getter as shown below. JpaContext you need to supply to user entity bean mapper.
public class FooMapper {
public Function<FooEntity, Foo> mapEntityToDomain(JpaContext context) {
PersistenceUnitUtil putil = obtainPersistentUtilFor(context, FooEntity.class);
return e -> {
return new Foo(
e.getId(),
e.getName(),
e.getType(),
e.getColor(),
putil.isLoaded(e, "car") ? e.getCar() : null);
};
}
private PersistenceUnitUtil obtainPersistentUtilFor(JpaContext context, Class<?> entity) {
return context.getEntityManagerByManagedType(entity)
.getEntityManagerFactory()
.getPersistenceUnitUtil();
}
}

Hibernate: Inheritance strategy, and annotations when persist abstract collections bidirectional

I am struggling with the right combination of inheritance strategy, and associations annotations when trying to persist abstract collections bidirectional with Hibernate.
All attempts I made so far fail, most with the complaint that #ManyToOne references an unknown identity.
Given the below example, the questions I have are
what is the best to maintain hibernate inheritence strategy for the
below structure?
which row id generation fits the inheritence strategy?
how to annotate each of the classes (e.g. #Entity, #MappedSuperclass)?
how to annotate the methods or field definitions (e.g. #ManyToOne, #OneToMany?
Since my code is embedded in a bigger structure and each class is rather big, here is a mock about cars - which is still big. Some relations I would model differently if I would really model cars, but this mock hopefully describes the basic problem in a more digestable form, without all the ballast from my real code.
The relations look like this:
the related java code looks like this:
package persistence;
public abstract class MechanicalObject {
private int weight;
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public abstract void someMethod();
}
package persistence;
import java.util.List;
public abstract class Car extends MechanicalObject {
protected List<Engine> potentialEngines; // references to something that also extends MechanicalObject
protected List<Driver> potentialDrivers; // references to something that does not extend MechanicalObject
public List<Engine> getPotentialEngines() {
return potentialEngines;
}
public void setPotentialEngines(List<Engine> potentialEngines) {
this.potentialEngines = potentialEngines;
}
public List<Driver> getPotentialDrivers() {
return potentialDrivers;
}
public void setPotentialDrivers(List<Driver> potentialDrivers) {
this.potentialDrivers = potentialDrivers;
}
}
package persistence;
public abstract class Engine extends MechanicalObject {
private int driveRPM;
private Car parent; // bidirectional reference
public int getDriveRPM() {
return driveRPM;
}
public void setDriveRPM(int driveRPM) {
this.driveRPM = driveRPM;
}
public void someMethod() {
// do something
}
public abstract void anotherMethod();
}
package persistence;
public class Driver {
// db row id
private long id;
private String name;
private String licenceType;
private Car parent; // bidirectional reference
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLicenceType() {
return licenceType;
}
public void setLicenceType(String licenceType) {
this.licenceType = licenceType;
}
public Car getParent() {
return parent;
}
public void setParent(Car parent) {
this.parent = parent;
}
}
package persistence;
public class CombustionEngine extends Engine {
// db row id
private long id;
private int cylinderCapacity;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getCylinderCapacity() {
return cylinderCapacity;
}
public void setCylinderCapacity(int cylinderCapacity) {
this.cylinderCapacity = cylinderCapacity;
}
public void anotherMethod() {
// do something differently
}
}
package persistence;
public class ElectroEngine extends Engine {
// db row id
private long id;
private int nominalVoltage;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getNominalVoltage() {
return nominalVoltage;
}
public void setNominalVoltage(int nominalVoltage) {
this.nominalVoltage = nominalVoltage;
}
public void anotherMethod() {
// do something
}
}
package persistence;
public class TeslaModelX extends Car {
private long id;
private String licenceNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getLicenceNumber() {
return licenceNumber;
}
public void setLicenceNumber(String licenceNumber) {
this.licenceNumber = licenceNumber;
}
public void someMethod() {
// do something differently
}
}
package persistence;
public class VWBeetle extends Car {
private long id;
private String serialNumber;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getSerialNumber() {
return serialNumber;
}
public void setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
}
public void someMethod() {
// do something differently
}
}
I would be greatful for any help. Links to web pages that explain the solution are also welcome, as I couldn't find any that handeled this subject completely.
what is the best to maintain hibernate inheritence strategy for the below structure?
That depends on what the rest of your model looks like. The easiest to use strategy would be single-table but if the subclasses differ too much a joined-table strategy or table-per-class strategy might be better. Just read up on those and pick one you're comfortable with. Note, however, that it's hard to change inheritance strategy further down the hierarchy - if it's possible at all (probably depends on the JPA provider).
which row id generation fits the inheritence strategy?
I'd say those are independent as Hibernate should handle that for you. Just pick an id generation strategy that fits your needs. What you'd probably need though is a discriminator column to let Hibernate identify the entity class each row represents.
how to annotate each of the classes (e.g. #Entity, #MappedSuperclass)?
That depends on your needs but I'd assume that Car and Engine aren't that related so they'd be entities while MechanicalObject could be a mapped superclass. Of course, if you want some other entity to reference any MechanicalObject you might have to make it an entity as well, use a different mapping approach or rethink your requirements/model.
how to annotate the methods or field definitions (e.g. #ManyToOne, #OneToMany?
Do as needed and read up on the annotations. Just keep in mind that one side needs to be the owner of the relation, i.e. only changes to that side are persisted on the database. If you don't do that you risk corrupting your model and confusing the JPA provider. The easiest way to do that on bidirectional 1-n relations would be to add mappedBy="..." to the 1-side, i.e. to #OneToMany.
As an example, your Car-Driver relation could look like this:
class Car extends MechanicalObject {
#OneToMany( mappedBy="parent" )
protected List<Driver> potentialDrivers;
}
class Driver {
#ManyToOne
protected Car parent; //I'd use a better name here
}

What is the appropriate way persist extended classes?

Consider the diagram:
I've been working with JPA for a short time and, so far, I have never had the need to persist extended classes... As you can see by the example, SNMPNode, IPNode, etc are all extended classes from Node that is also extended from GeoLocation.
I understand that I can annotate the master classes with #MappedSuperclass and IPNode and SNMPNode will inherit their properties for persisting... But in this scenario I will end up with nearly identical tables and, to my understanding, instead of doing that I could just group all information in Node and work with a single table.
Is this the way persistence of extended classes on JPA work or my concepts are wrong?
Same thing as a resumed piece of code:
public class Node extends GeoLocation {
private String name;
private Group group;
private Location location;
private Type type;
private Company company;
}
public class IPNode extends Node {
private Long id;
private String ipAddress;
}
public class SNMPNode extends Node {
private Long id;
private SNMPServer server;
}
[[ EDITED AFTER ANSWER FROM THIS POINT ]]
For the sake of contributing, here's a sample of what I end up doing:
INode:
public interface INode {
public Long getId();
public void setId(Long id);
public String getName();
public void setName(String name);
public String getIpAddress();
public void setIpAddress(String ipAddress);
public String getCommunity();
public void setCommunity(String community);
}
Node:
#Entity
#DiscriminatorValue("N")
#DiscriminatorColumn(name="NODE_TYPE",discriminatorType=DiscriminatorType.STRING, length=20)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Node extends GeoLocation implements INode {
#Id
#GeneratedValue
private Long id;
private String name;
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
(... Overrides from INode ...)
}
IPNode:
#Entity
#DiscriminatorValue("I")
public class IPNode extends Node {
private String ipAddress;
public String getIpAddress() { return this.ipAddress;}
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
(... Overrides from INode ...)
}
SNMPNode:
#Entity
#DiscriminatorValue("S")
public class SNMPNode extends Node {
private String community;
public String getCommunity() { return community;}
public void setCommunity(String community) { this.community = community; }
(... Overrides from INode ...)
}
NodeRepository:
#Repository
public interface NodeRepository extends JpaRepository<Node, Long> { }
So now I can do stuff like this:
#ContextConfiguration("classpath:/spring/application-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class NodeRepositoryTest {
#Autowired
NodeRepository repo;
private INode node;
#Before
#Transactional
#Rollback(false)
public void setup() {
node = new IPNode();
node.setName("ipNode");
node.setIpAddress("1.1.1.1");
repo.save((IPNode)node);
node = new SNMPNode();
node.setName("snmpNode");
node.setIpAddress("2.2.2.2");
node.setCommunity("some text");
repo.save((SNMPNode)node);
}
#Test
#Transactional
public void Test() throws Exception {
INode testNode = repo.findOne(1L);
assertNotNull(testNode);
}
}
Both Node types are saved on the same table and so their keys can't repeat... My REST URL can grab them by /nodes/1 or /nodes/2, which was my main goal after all...
Thanks :)
If your base class is annotated with #MappedSuperclass then inheritance is only relevant in the OOP context. The #MappedSuperclass properties are simply copied to each sub-class associated database table and you can only query for sub-class entities.
Single table inheritance yields the best performance (no join or union is involved) at the price of not being able to declare not-nullable all specific sub-class properties (since all base and all sub-class specific properties go to a single database table).
With joined inheritance tables you can have the base class properties in a base database table, and each specific sub-class has it's own associated table. The sub-class table is linked with the base database table through a FK, so you need to join these tables to fetch a sub-class entity. As opossed to #MappedSuperclass, the base class is queryable since both the OOP context and the database reflect the inheritance model.

Categories

Resources