search though all tables extended from #MappedSuperclass - java

I have and issue with searching in entities that are extended from #MappedSuperclass. I created a class PhoneBook and extended 2 entities from it: FirstPhoneBook and SecondPhoneBook. The structure looks the following:
#MappedSuperclass
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
These tables are absolutely similar. I discribe all fields in PhoneBook class, childs have only default constructor in it. External system sends a phone number as a parameter. Depending on whether tables contain such number or not my system responds with a word.
The question is: how can I search separately in each table that is extended from #MappedSuperclass without hardcoding each child class name?
I could only find variants of search by value like that:
currentSession.get(Employee.class, theId);
but there is explicit call to entity class. I want this to be extendable without need to write new DAO for each new entity added. Current method signature looks the following:
public <T extends PhoneBook> T findByNumber(String number);

What you describe is polymorphic queries, i.e. queries that reference the parent class. The Hibernate documentation says this is not well supported when using #MappedSuperclass inheritance:
Because the #MappedSuperclass inheritance model is not mirrored at the database level, it’s not possible to use polymorphic queries referencing the #MappedSuperclass when fetching persistent objects by their base class.
If polymorphic queries are frequently used, it's better to use the table per class inheritance strategy:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class PhoneBook {
...
#Entity
#Table(name = "first_phone_book")
public class FirstPhoneBook extends PhoneBook {
...
#Entity
#Table(name = "second_phone_book")
public class SecondPhoneBook extends PhoneBook {
...
You can then fetch an entity using the superclass:
PhoneBook phoneBook = currentSession.get(PhoneBook.class, theId);
and Hibernate would typically use a UNION to do the query with both tables.
This being said, even with #MapperSuperclass, Hibernate can still query all tables for classes that extend the parent class. You can use the following JPA query (note that it uses the fully qualified class name of the parent class):
Query<PhoneBook> query = currentSession.createQuery("from " + PhoneBook.class.getName() +
" where id = :id", PhoneBook.class);
query.setParameter("id", theId);
The difference is that here it's not querying an entity, but just all classes that extend a parent class. Also in this case, unlike with the table-per-class strategy, Hibernate will not use a UNION, but send a query to each table, in this case two separate SQL queries instead of one.

Related

How to handle Runtime Polymorphism in Spring Boot?

I have abstract class Employee
public abstract class Employee {
private int employeeId;
private String name;
}
I also have two concrete classes that extends Employee and that is OfficeEmployee and HomeEmployee which are currently empty.
This my controller:
#RestController
#RequestMapping("/api/employee")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#PostMapping("/office")
public EmployeeResponse saveOfficeEmployee(#RequestBody OfficeEmployee request) {
return employeeService.save(request);
}
#PostMapping("/home")
public EmployeeResponse saveHomeEmployee(#RequestBody HomeEmployee request) {
return employeeService.save(request);
}
}
And finally EmployeeService class:
#Service
public class EmployeeService {
#Autowired
private EmployeeRepository employeeRepository;
public Employee save(Employee request) {
// here i think i should do something like this: Employee employee = new OfficeEmployee or
// Employee employee = new HomeEmployee();
Employee employee = employeeRepository.save(employee);
return employee;
}
}
How to determine what employee did i get from POST request? Am i approaching this problem wrong?
Why would you think you need to determine anything? EmployeeRepository is already well-equipped to handle all types of Employees.
For this to work, Employee must be an #Entity. It can still be abstract, though.
As a side note, an alternative to having separate endpoints (/home, /office) is to use #JsonTypeInfo with one of the available strategies to determine Employee subtype from the input data.
Yeah abstract class entity is something I use for adding a common column in multiple tables.
For example if I want to add createdDate and updatedDate on many tables, I would defined those 2 columns in an abstract entity class (call it BaseDateEntity for example), and inherit it in all the entity classes I want to use it. Also annotate the base entity class with #MappedSuperclass. But repositories should be per specific entity classes. You can't use 1 repository for all entities that inherit your abstract Employee entity, otherwise the query will be executed on all subclass entities(OfficeEmployee, HomeEmployee, XyzEmployee, ..) of your superclass baseentity(=Employee), which could be sufficient from time to time.
Rough example of your entity codes.
import java.persistence.MappedSuperclass;
#MappedSuperclass
public abstract class Employee { //body skipped for brevity}
There's an alternative. Use #Entity and #DiscriminatorColumn on your base entity.
And use #Entity & #DiscriminatorValue on your child entity.
#Entity
#DiscriminatorColumn
#Inheritance(strategy=InheritanceType.JOINED) //more explanation on this below
public abstract class Employee {//body skipped for brevity}
#Entity
#DiscriminatorValue("Officeemployee")
public class OfficeEmplyee extends Employee {}
You cannnot use both MappedSuperclass and Entity. So choose one.
Rough example of your repositories
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long>{}
public interface OfficeEmployeeRepository extends JpaRepository<OfficeEmployee,Long>{}
Obviously I skipped code examples for HomeEmployee because that will be same as OfficeEmployee.
Also you don't need OfficeEmployeeRepository if you never want query specifically on OfficeEmployees only. If you always query on all subclasses of Employees, then you only need EmployeeRepository. However in this case I think you need both EmployeeRepository for general Employee query and also OfficeRepository & HomeRepository for query on specific type of employees
To explain a bit further about the difference between MappedSuperclass methodology and DiscriminatorValue methodology, you have to think about tables in DB.
In simple cases where you don't want to create another table for parent (abstract) entity object, it's much simpler to use MappedSuperclass. It simply maps(adds) the additional columns that are described in abstract parent entity, onto child entities. In my usual usecase(createdDate, updadedDate column), this is the better approach since there's no reason to build a table for all datasets that have createdDate&updatedDate column. (A table of all the posts, announcements, comments, threads, re-replies, A2A, ...etc? Makes no sense)
However in your case you might want to keep a table of all kinds of employees. In that case use discriminatorcolumn & discriminatorvalue approach. Here's where #Inheritance(strategy=) annotation comes into play.
If #Inheritance doesn't exist, default inheritance strategy is SINGLE_TABLE. Which is self explanatory imo. All subclass entity columns are also added on this superclass (abstract entity) table. It will create a giant Employee table. Since it doesn't need join query it's faster and simpler in querying. But the down side is that table is giant and also there will be a lot of null values. (If OfficeWorker has column called 'OfficeLocation' and HomeWorker doesn't, then every HomeWorker rows will have OfficeLocation=null in the giant Employee table.)
What I used above is JOINED strategy. Also self explanatory. Makes a table of all Employee, a table of all OfficeWorker, a table of all HomeWorker. But in this case, Employee table only has common column values (id, name, .. what not) and type (OfficeWorker vs HomeWorker), and a foreignkey that is used for join query onto OfficeWorker table and HomeWorker table.
Last option is TABLE_PER_CLASS. It doesn't generate a table of all Employee. So it is the same as MappedSuperclass annotation but only more verbose. Never recommended.

How to use #MappedSuperClass as #OneToMany mapping collection in JPA Entity

I am using Entity Inheritance for the first time and stuck with using a MappedSuperClass as collection inside another MappedSuperClass.
I have two similar set of Entities which must save data to different tables in DB. But since the business logic is perfectly same, so I want to use base classes for these entities, so these base classes can be used in common JPA repository (#NoRepositoryBean).
#MappedSuperClass
class Classroom {
String std;
}
#MappedSuperClass
class School {
String name;
#OneToMany //ERROR
Set<Classroom> rooms;
}
#entity
#table("A_SCHOOL")
class ASchool extends School {
Set<AClassroom> rooms;
}
#entity
#table("A_CLASSROOM")
class AClassroom extends Classroom {
}
#entity
#table("B_SCHOOL")
class BSchool extends School {
Set<BClassroom> rooms;
}
#entity
#table("B_CLASSROOM")
class BClassroom extends Classroom {
}
The issue is the #OneToMany mapping in School is not allowed since Classroom is not an entity.
Considering the common JPA repository (and all other business logic) is designed to work with only School/Classroom, how should I update the association of Classrooms in School?
the idea of using #MappedSuperClass is to make common attributes between different classes in one place .
in your case each School has dependency on certain ClassRoom so you need to declare that relationship in each subclass of school,
so you need to remove the relationship from the parent class and declare it in all School subclasses .
have a look at this tutorial :How to inherit properties from a base class entity using #MappedSuperclass with JPA and Hibernate

Spring repository for super entity class not fetching all subclass entity types when changed from #MappedSuperClass to #Inheritance

We have an abstract #MappedSuperClass and bunch of entities extending it, like:
#MappedSuperclass
public abstract class SuperEntity implements Serializable {
#Id
private Long id;
private String name;
}
and lots of entities like:
#Entity
public class Sub[1..20]Entity extends SuperEntity {
...
}
Because of this there were created - as well - a bunch of repositories for each entity. All well this far.
Now there is a need to fetch all the entities that extend super. Therefore SuperEntity was changed as below:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class SuperEntity implements Serializable {
This should be functionally almost the same (is it?).
Then a new repository for this was created, like:
public interface SuperEntityRepository extends JpaRepository<SuperEntity, Long> { };
Now the problem is, when calling:
superEntityRepository.findAll();
it returns only about 5 of sub entities not all the 20. What could be wrong?
Upon writing the question I realized what was the problem. Values - including IDs - were inserted straight to the database and IDs were not unique in the scope of SuperEntity. That is why there is no #GeneratedValue, BTW. IDs were only unique in the scope of each extending sub class entity.
There were no error messages. Spring repository just picked up the first found id and all the other entities with same ID were ignored.
So the answer to have this working is to to update all the extending entities to have unique ID in the scope of the SuperEntity.
However, updating references cascading is quite a job so if there is a lighter way to get this working, share it.
Yes, I could have deleted the question but maybe someone finds it and this answer useful

dao, tx, service structure: where to place a method querying an abstract entity?

I have an abstract entity that 4 other entities inherit from. This relationship works well, however I want to query the abstract entity so that I get all entities regardless of their types. I have no idea where to place such a method since the parent entity dao is also abstract.
EntityParent (abstract) -> EntityType1, EntityType2, EntityType3, EntityType4
DAOs look like this:
EntityParentDAO (abstract) -> EntityType1DAO, EntityType2DAO, EntityType3DAO, EntityType4DAO
TX also look like this:
EntityParentTx (abstract) -> EntityType1Tx, EntityType2Tx, EntityType3Tx, EntityType4Tx
My project structure goes as follows:
Entities -> DAO for each entity -> TX for each DAO -> Service combining several TXs
There is Service which uses all of the *TX*s that's within the scope of my project. Is this where a criteria/HQL query should be placed? That doesn't sound quite right.
For example let's say I have a Car parent entity and that I have children entities Coupe, Sedan, Minivan and so on and I want a list of cars given a property that is common to all and therefore in the entity (and it its table) Car. Where would I place this query/method given the structure I'm following?
I'm not sure I follow the transaction inheritance, but why not make the parent dao concrete and add it there? As long as the parent is an Entity, and it has the field, you can query on it. The return type will be a list of the base type, but it will be instances of the actual type.
Ex:
#Entity
#Table(name = "table")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.INTEGER)
public abstract class ParentImpl implements Parent{}
#Entity
#DiscriminatorValue("1")
public class Entity1Impl extends ParentImpl {}
public interface AbstractDao<T extends Parent> {}
public interface ConcreteParentDao<Parent> {}

jpa2 hibernate use existing entity to load data from view

In my JPA2/Hibernate application I have table USERS and matching entity User + DAO.
Now, I created view V_USERS, which contains filtered data from table USERS.
Can I reuse entity User to query data from this view ?
No you can't. You can map two different classes into the same table but not the other way around.
The reason for this limitation is simple, how would JPA provider knew which table do you have in mind in the following query:
em.createQuery("SELECT u FROM User u");
However you might want to create a base class AbstractUser with all properties and have to empty subclasses: User and for instance VUser. The latter two classes will have a mapping to different tables/views. See MappedSuperclass and an example there.
#MappedSuperclass
public abstract class AbstractUser {
//all your columns/JPA mapping go here
}
#Entity
#Table(name="USERS")
public class User extends AbstractUser {}
#Entity
#Table(name="V_USERS")
public class VUser extends AbstractUser {}

Categories