JPA Inheritance :mapping derived entities to different tables - jpa

I use SINGLE_TABLE inheritance startegy to map my usres (see code example bellow).
Is there a way to map UnActiveRegularUser and UnActiveBusinessUser from "ACTIVE_USERS" table to another table, for example "UNACTIVE_USERS" and keep the inheritance startegy?
Note:
-The point here is to avoid code duplication between ex. RegularUser Vs UnActiveRegularUser (since they use the same properties) but still to map them to 2 different tables: "ACTIVE_USERS" and "UNACTIVE_USERS".
-strategy = InheritanceType.SINGLE_TABLE should not be changed.
-May adding another abstraction layer solve this problem?
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "ACTIVE_USERS")
public class User {
#Id #GeneratedValue
protected Long id;
#Column(nullable = false)
protected String name;
}
#Entity
public class RegularUser extends User{
//more getters and settres
}
#Entity
public class UnActiveRegularUser extends User{
//same getters and setters as in RegularUser
}
#Entity
public class BusinessUser extends User {
//more getters and settres
}
#Entity
public class UnActiveBusinessUser extends User {
//same getters and setters as in BusinessUser
}
Thanks,
Nathan

Persisting fields to another table won't prevent code duplication. I think you should just make UnActiveBusinessUser extend BusinessUser, and UnactiveRegularUser extend RegularUser.
Note that if a user can become unactive (i.e. it is a RegularUser and becomes an UnactiveRegularUser), inheritance is not the right solution: an object can't go from one type to another. Since it seems UnactiveRegularUser doesn't have anything more than RegularUser, I'm not sure this subclass is useful.

Related

JPA EntityManager finder method returning other sub class object

I am facing an issue with JPA EntityManager finder method. JPA entities are using inheritance structure as follows:
//Need to persist this table to database
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="Table1")
public class BaseEntity implements Serializable {
#Id
#Column(name="PRIMARY_ID")
private long id;
private String field1;
.......
}
//This table will NOT persist and has parameters only for Sub classs
#MappedSuperclass
public abstract class MappedSuperClassEntity extends BaseEntity {
private String field2;
private String field3;
........
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable1")
public class Sub1 extends MappedSuperClassEntity {
private String field4;
private String field5;
...............
}
//This sub class is persisted and inherits fields form above class including Primary Key using TABLE_PER_CLASS strategy defined in BaseEntity
#Entity
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
#Table(name="SubTable2")
public class Sub2 extends MappedSuperClassEntity {
private String field4;
private String field5;
..............
}
As you can see, Sub1 and Sub2 are persistable entites and both of which extends MappedSuperClassEntity which is annotated with `#MappedSuperClass'. This class further inherits BaseEntity which has decribed TABLE_PER_CLASS inheritance strategy.
I enabled the hibernate stat collector and found that hibernate is storing sub class objects using key of Parent class. So in the above case it stores the data as follows for Sub1 finder in cache:
14:17:03,941 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:17:03,943 DEBUG [org.hibernate.cache.TransactionalCache] cache miss
14:17:03,948 DEBUG [org.hibernate.cache.TransactionalCache] caching: com.abc.BaseEntity#10
Next time if i do a find for Sub2 for same id (10), hibernate thinks it is in Cache as it uses Parent Class as key and returns the Sub1 object as follows:
14:27:54,574 DEBUG [org.hibernate.cache.TransactionalCache] cache lookup: com.abc.BaseEntity#10
14:27:54,575 DEBUG [org.hibernate.cache.TransactionalCache] cache hit
So this is happening when you run the finders for Sub1 and Sub2:
entityManager.find(Sub1.class, id); //returns Sub1 object
entityManager.find(Sub2.class, id); //returns Sub1 object (PROBLEM HERE).
Please help me to fix the issue (I do not want to clear cache in between these calls)
The problem is that you're using a base entity when that doesn't make sense. When you inherit from a base entity, and not just from a mapped superclass, you're not just inheriting fields and methods. You're establishing an is a relationship.
An example where that would make sense is the following: Car and Bike both inherit a base entity Vehicle. In that case, a Car entity is a Vehicle entity. And a Bike entity is a Vehicle entity.
If a car has an ID 42, then a Bike may not also have the ID 42, because you would have two vehicles with the same ID. Imagine a Driver entity with a ManyToOne association with a vehicle (i.e. a driver drives a vehicle). If I store the ID 42 in the vehicle_id column of the driver table, this ID 42 must uniquely identify a vehicle. It could be a car, it could be a bike, and hibernate will look in both tables, but it can't be both at the same time.
You're violating this inheritance concept. BaseEntity should not be annotated with Entity. It should just be a MappedSuperclass, which just allows inheriting fields and methods, but doesn't establish this semantic is a association.

How to map OneToMany relationship from Superclass

Hello! Recently I was stuck with such problem, and I hope the solution I provide below will help some other JPA newbies like me. If there is better solution please post it here!
The problem is as follows:
I want to create OneToMany relationship from classes Book and CD to class Tag.
In order to unify all the logic regarding class Tag from Book and CD I create #MappedSuperclass class Item, and make Book and CD descendants of class Item. But when I try to map
List <Tag>
tags with #OneToMany” in that superclass I get nothing good..
My solution:
In order to do an ORM mapping one should understand first what he actually wants to see in the database. So when I realized that reasonable solution is to create several transition tables between descendants of Item and Tag, I understood, that this may be accomplished using #ManyToMany. And it works fine!
Listing below.
#MappedSuperclass
public class Item extends Model {
#Id
public Long id;
#ManyToMany(cascade = CascadeType.ALL)
public List<Tag> tags;
public String name;
<…> }
#Entity
public class Book extends Item {
public int pageNum;
<…> }
#Entity
public class CD extends Item{
public int size;
<…> }
#Entity
public class Tag extends Model{
#Id
public Long id;
public String text;
<…> }
PS I'd post also class and er diagrams, but currently I got no r8n to post images.

intermediate `#MappedSuperclass` failed to persist

I have three classes look like this.
#Entity
class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#MappedSuperclass
abstract class B extends A {
}
#Entity
class C extends B {
}
Should above even work? And it seems not work, at least, with EclipseLink.
I got Column 'ID' cannot be null when I tried to persist an instance of C.
I found a bug at Inheritance with abstract intermediate class using #MappedSuperclass fails to populate subclasses but I'm not sure it is exactly the same situation or not.
UPDATE per #James's answer
I'm sorry, I should written more verbosely. Yes I'm intending SINGLE_TABLE inheritance. I don't have any extended property with B nor C. It's just a hierarchical class design.
#DiscriminatorColumn
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Entity
abstract class A {
#Id #GeneratedValue #TableGenerator
private long id;
}
#DiscriminatorValue("B") #Entity // I currently should do like this.
//#MappedSuperclass // error
abstract class B<T extends SomeoneElse> extends A {
}
#DiscriminatorValue("C")
#Entity
class C extends B<James> {
}
I believe that in JPA a #MappedSuperclass must be a superclass not a subclass (as its name infers).
I'm not sure what having an #Entiy subclass as a #MappedSuperclass would mean?
What are you trying to do?
For #Entity inheritance JPA only provides three options, SINGLE_TABLE, JOINED, and TABLE_PER_CLASS. All persistence subclasses must be entities.
I assume you are using JOINED inheritance and trying to avoid a table for B. JPA does not specify a standard way of doing this. In EclipseLink you can avoid the table by making its table match the parent (#Table(name="A")).

Mapping abstract classes with includePaths

I have a question regarding mapping of Hibernate Search and using an abstract base class.
I'm getting the following error
Caused by: org.hibernate.search.SearchException: Found invalid #IndexedEmbedded->paths configured on class nl.project.model.social.AbstractGroup, member language: language.id
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.validateAllPathsEncountered(AbstractDocumentBuilder.java:901)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.checkForIndexedEmbedded(AbstractDocumentBuilder.java:880)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.initializeMemberLevelAnnotations(AbstractDocumentBuilder.java:489)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.initializeClass(AbstractDocumentBuilder.java:391)
at org.hibernate.search.engine.spi.AbstractDocumentBuilder.<init>(AbstractDocumentBuilder.java:174)
at org.hibernate.search.engine.spi.DocumentBuilderContainedEntity.<init>(DocumentBuilderContainedEntity.java:60)
at org.hibernate.search.spi.SearchFactoryBuilder.initDocumentBuilders(SearchFactoryBuilder.java:396)
at org.hibernate.search.spi.SearchFactoryBuilder.buildNewSearchFactory(SearchFactoryBuilder.java:222)
at org.hibernate.search.spi.SearchFactoryBuilder.buildSearchFactory(SearchFactoryBuilder.java:146)
at org.hibernate.search.event.impl.FullTextIndexEventListener.initialize(FullTextIndexEventListener.java:130)
at org.hibernate.search.hcore.impl.HibernateSearchIntegrator.integrate(HibernateSearchIntegrator.java:83)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:301)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1750)
Based on the following mapping configuration
#Entity
#Table
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "DTYPE", discriminatorType= DiscriminatorType.STRING, length = 3)
#Indexed
public abstract class AbstractGroup implements Serializable, IEntity, IPhoto{
protected Language language;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
#JoinColumn(name="FK_LanguageId")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.NEVERCHANGE)
#NotNull
#IndexedEmbedded(includePaths={"id"})
public Language getLanguage() {
return language;
}
}
#Entity
#DiscriminatorValue(value = "GRP")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.GROUP)
public class Group extends AbstractGroup{
#Entity
#DiscriminatorValue(value = "PGE")
#Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.GROUP)
public class Page extends AbstractGroup{
I've tried putting #Indexed on the subclass but this gives the same error.
The #Indexed annotation should indeed be on the sub classes. However, I am not sure what this should have to to with te discriminator column. JPA and Search annotations should be orthogonal. Two different things really. Btw, how does your Language entity look like? See also https://forum.hibernate.org/viewtopic.php?f=9&t=993097&hilit=abstract+base+class

How do I represent this using JPA?

I would like a 'RolesPlayed' entity with the following columns
user
role
department/project/group
All the three columns above constitute a composite primary key. I would like to know if defining a column to be one of department/project/group possible ? If yes, how ? Or do I need to break the entity into DepartmentRoles, GroupRoles and ProjectRoles.
Thanks.
You could use polymorphism with an abstract base class to do that.
#Entity
public class RolePlayed {
#ManyToOne
private User user;
#ManyToOne
private Role role;
#ManyToOne
private Body body;
...
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Body {
...
}
#Entity
public class Department extends Body {
...
}
#Entity
public class Project extends Body {
...
}
#Entity
public class Group extends Body {
...
}
Check out the Polymorphism section in the Java Enterprise tutorial for a good overview.
Alternatively, you could also make the RolePlayed entity abstract, with DepartmentRole, GroupRole and ProjectRole implementations.