Get entity subclass without instanceOf - java

I have a scenario where I have a entity hierarchy structure implemented in hibernate.
It is InheritanceType.JOINED
Parent class:
#Entity
#Table(name = "LOY")
#Inheritance(strategy=InheritanceType.JOINED)
public class Loy implements Serializable
Child class boundary
#Entity
#Table(name = "LOY_BOUNDARY")
#PrimaryKeyJoinColumn(name="ID")
public class LoyBoundary implements Serializable
Child class percentage
#Entity
#Table(name = "LOY_PERCENTAGE")
#PrimaryKeyJoinColumn(name="ID")
public class LoyPercentage implements Serializable
I have a Customer entity class which is linked to the Loy entity class in a #ManyToOne.
The Customer can only be linked to one Loy at a time.
What I want to achieve is that I want to query the Customer class with unique id (passport number) and then get The Loy for the specific Customer through the #ManyToOne mapping in the Customer entity.
The problem that I'm sitting with is that I do not know which subclass of Loy is linked to the Customer.
I can go instanceOf to get the specific subclass but I want to try and avoid it.
I can also add a visitor pattern in the subclass entity, but not sure if this is best practice.

I would go with a visitor. Using instanceof won't work if the ManyToOne is lazy-loaded, because the actual type of the Loy won't be any of your subclasses, but a Hibernate proxy extending the Loy class. Indeed, Hibernate has no way to know, from the ID of the loy in the customer, which kind of Loy it refers to.
And a visitor is more OO and cleaner anyway.

Related

Choosing Inheritance Strategy - Hibernate

if this question was aksed here, i surely couldnt find it, or it didnt particulary help me.
i've read some tutorials and some questions for Inheritance Mapping, which couldnt quitely solve my questions.
Say i have an abstract class :
User
And 3 more other subclasses :
UserA, UserB, UserC
those all extend User.
Every Subclass has its own table, Superclass User, meanwhile, doesn't.
In my other class Website i have a ArrayList, or should i say Collections, of Users.
The list should fetch all users of the Website.
Which strategy should i use ? I thought of MappedSuperclass, but since in my Website class the List is of User type, so am not sure what to do here.
Thanks for any help!
With JPA the Java implementation always depends on you own preferences and requirements, sometimes it is the matter of a choice.
Yes, #MappedSuperclass will do.
You can have every child with unidirectional relationship to Website. Then you gonna have Website object inside your User class (with a bunch of annotations), which will map to a database as foreign_key field (presume you are using SQL storage and 'Repositories' DAO abstraction from JPA).
It is not necessary to store a collection of users inside Website class. Just think if you really need it - it can be a mess to support consistency.
But there are cases where you need bidirectional relationship. When you store objects in memory (for caching purposes for example) you'll probably need to have this collection. In this case why not to have 'User' collection? You will fetch data through dedicated repositories(or even if you're not using those, any other way will be using 'User' tables with foreign_key, not the 'Website' table) anyway.
So, for example with the use of Spring Data JPA you can define a unidirectional relationship in a superclass and use 'repositories' next way(and bidirectional example you can find anywhere in the internet, so I am not providing it):
#Entity
public class SuperUser extends User {
...
}
#Entity
public class BasicUser extends User {
...
}
#MappedSuperclass
public abstract class User implements Serializable {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "website_uuid", nullable = false)
protected Website website;
...
}
#Entity
public class Website implements Serializable {
...
}
#Repository
public interface SuperUserRepository extends CrudRepository<SuperUser, Long> {
Iterable<SuperUser> findByWebsite(Website website);
}
#Repository
public interface BasicUserRepository extends CrudRepository<BasicUser, Long> {
Iterable<BasicUser> findByWebsite(Website website);
}
What you are asking for seems a typical "Table-per-concrete-class" inheritance strategy. https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#entity-inheritance-table-per-class
In older version of the user guide, it has mentioned that separate table will be mapped for each non-abstract classes. In the latest document the "non-abstract" part is not mentioned but I believe it still works similarly.
So it looks something like:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class User {...}
#Entity
class UserA extends User {...}
#Entity
class UserB extends User {...}
#Entity
class UserC extends User {...}
But you should be aware of this inheritance strategy usually gives inefficient query as internally it is using union.

How to solve 'no primary key specified' for an Entity that inherits from another Entity (JPA)?

I want to have a superclass that is common to all document types:
#Entity
public abstract class Doc implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected long docId;
public long getDocId()
{
return docId;
}
public void setDocId(long docId)
{
this.docId = docId;
}
}
And I want to have child classes for each doc type:
#Entity
#Table(name = "DocTypeA")
public class DocTypeA extends Doc implements Serializable
{
// other child fields
}
But it gives an error and says that DocTypeA needs a primary key. How can I isolate the primary key and put it in the super class? Because all the subclasses will have that same id field.
I am using EclipseLink.
And my other question is: Why do I need to put #Entity in the abstract class? Being an abstract class it cannot be instantiated, so what is the point of marking it as an Entity? Is it really necessary? I will not persist the superclass. I need it only for isolating the code common in all subclasses.
The stack trace is a long one, relevant part is pasted below:
Exception Description: Entity class [class repository.DocTypeA] has no primary key specified. It should define either an #Id, #EmbeddedId or an #IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.
According to the official JavaDoc the annotation #MappedSuperclass:
Designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.
which is what you are looking for. Thus abstract classes can easily be used for common attributes of entities, which in most cases a primary key or DB-generated object identifier is. Annotated fields of that abstract class will then only be mapped for concrete subclasses:
A class designated with the MappedSuperclass annotation can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself. When applied to the subclasses the inherited mappings will apply in the context of the subclass tables.
Exchange the #Entity annotation in the abstract class Doc like so:
#MappedSuperclass
public abstract class Doc implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected long docId;
//...
}
and you should be good to go.
Hope it helps.

JPA inheritance of subclass

Imagine that we have :
public class Person {...}
public class Employee extends Person {....}
We make of Employee an entity that have an other table as Person.
With JPA we make our interface extends JpaRepository.
I ask the personRepo.findAll()
Is it possible to get only the List of Person without the Employees?
(I'm analysing this atm so I don't have concreet example, just need to know if this is possible)
Thx in advance
I did the same thing.
This is my code:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Audited
public abstract class AbstractUser{..}
#Entity
#Audited
public class Admin extends AbstractUser{ .. }
#Entity
#Audited
public class OtherUser extends AbstractUser{ .. }
And then, I use Repository too.
So if I use yoursRepository.findAll(); I get all Users..
In your case, with personRepo.findAll(), you get all person.. You get all those that extend person and those that do not extend it.
EDIT: after question edit
If you want get only employee you can use two different repository, one for Employee and one for Person. So when you use employeeRepository, you get only employee..

Relating an entity to multiple subclass entities as a single relationship to the superclass in JPA?

I'm building an application in Java with the Play 2 framework and ebean ORM.
I have an entity class Person that is related to several entity classes with the supertype Certificate, which is an abstract #MappedSuperclass. Currently, I'm creating a relationship for each subclass, like so:
public class `Person` extends Model {
// elided...
#OneToMany
public List<SubType1> subType1s;
#OneToMany
List<SubType2> subType2s;
// elided...
}
and the subclasses look like this:
public class SubTypeGeneric {
// elided...
#ManyToOne
public Person person;
// elided...
}
What I want to know is this: is it possible to group the subclass entities lists in Person together into a single superclass list, like so:
public class `Person` extends Model {
// elided...
#OneToMany
public List<SuperType> superTypes;
// elided...
}
No, it's not possible. An association is between entities, not MappedSuperclasses. And if you have a OneToMany can be the inverse association of only one ManyToOne.
If you want that, then Certificate should be annotated with #Entity, and should contain the ManyToOne association with Person instead of all the subclasses.

Any way to get fields from entity without actually joining into that table?

I'm not sure this is possible, but knowing just the very basics of JPA, I want to ask if it is possible. Basically I have an entity (We'll call it MyEntity) with a bunch of fields on it. We now want a 2nd entity that has all the same fields as MyEntity plus some of it's own. The use case for this is archiving these entities. We want to store all the archived entities in a separate table than MyEntity so that we don't have to qualify all the queries with archived=false. The JPA annotations for MyEntity look something like this:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity
{
....
There are multiple classes that extend this abstact class, each with #DiscriminatorValue annotations
For my archived entity (MyArchivedEntity) I want something along the lines of this:
#Entity
public class MyArchivedEntity
{
private MyEntity entity;
private String archiveSpecificField;
....
The problem with this of course is that it will want to join into the MyEntity table and get a specifc MyEntity record for populate the entity field. Is there some kind of annotation or something I can do to just get the same fields/columns from that entity (MyEntity) into this entity (MyArchivedEntity)?
Like I said in the beginning, I'm not sure if this is possible, but I hope I've explained well enough the end goal of what I'm trying to achieve, so that there could be some way to achieve it. If it makes any difference, I'm using PostgreSQL with EclipseLink.
What you can do is using #MappedSuperclass on a AbstractParentEntity becoming the super class of both MyEntity and MyArchiveEntity. So you will have something like the following:
#MappedSuperclass
public abstract class AbstractParentEntity {
public String someField;
...
}
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
public abstract class MyEntity extends AbstractParentEntity
{
//here you don't have any field (they all move to AbstractParentEntity
// (or, at least all the fields that need to be archivied are now declared in parent class)
....
}
#Entity
public class MyArchivedEntity extends AbstractParentEntity
{
private String archiveSpecificField;
....
More about MappedSuperclass here:
Mapped superclass inheritance allows inheritance to be used in the object model, when it does not exist in the data model. It is similar to table per class inheritance, but does not allow querying, persisting, or relationships to the superclass. Its' main purpose is to allow mappings information to be inherited by its' subclasses. The subclasses are responsible for defining the table, id and other information, and can modify any of the inherited mappings. A common usage of a mapped superclass is to define a common PersistentObject for your application to define common behavoir and mappings such as the id and version. A mapped superclass normally should be an abstract class. A mapped superclass is not an Entity but is instead defined though the #MappedSuperclass annotation or the <mapped-superclass> element.
You may wish to look into EclipseLink's history support. It can automatically maintain a historical archive table.
See,
http://wiki.eclipse.org/EclipseLink/Examples/JPA/History
Another option would be to map the same classes in another persistence unit using an orm.xml to the archive tables.

Categories

Resources