Hibernate Mapped Superclass relationships and overriding - java

I have an abstract MappedSuperClass, Participant, which is extended by three kinds of 'Participant'. Each one then uses its own kind of 'Project', also an abstract MappedSuperClass. However, I want the base class to know about Projects so I can write generic code to interact with Participants. How do I specify this using Hibernate annotations? and how will I override it in the ExtendedParticipant and ExtendedProject classes?
Each Participant type, and each Project type, have their own database tables with existing data and ids (not unique across tables) that I cannot change.
The following code gives me the IDE error "Many to one attribute should not be 'Mapped Superclass'".
#MappedSuperclass
public abstract class Participant implements Persistable {
...
#ManyToOne
#JoinColumn(name = "project_id")
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
...
}
and the Project class is much the same with the same problem:
#MappedSuperclass
public abstract class Project implements Persistable {
...
#OneToMany
public List<Participant> getParticipants() {
return participants;
}
public void setProject(List<Participant> participants) {
this.participants = participants;
}
...
}

A mapped superclass is not an Entity, it can't be part of an association. So map your classes as entities and either introduce a mapped superclass "above" them or use a TABLE_PER_CLASS strategy.
See also
EclipseLink: Query to MappedSuperclass fails
Hibernate - Persisting polymorphic joins

Seems possible to have relations defined through the mappedsuperclass
according to the following
jboss_docs
another_stackoverflow_thread
object_db_doc

Docs said it's possible, but superclass isn't abstract in example
https://docs.oracle.com/javaee/5/api/javax/persistence/MappedSuperclass.html

Related

JPA OneToMany Association from superClass

I’m trying to map the inheritance from the superclass LendingLine and the subclasses Line and BlockLine. LendingLine has an ManyToOne association with Lending.
When I try to get the LendingLines from the database without the inheritance it works fine. The association works also. But when i add the inheritance, lendingLines in Lending is empty. I also can't get any LendingLines from the DB with the inheritance.
Can anybody help me?
(Sorry for the bad explanation)
Thanks in advance!
LendingLine:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
#DiscriminatorValue(value="Line")
#Table(name = "LendingLine")
public class LendingLine {
...
public LendingLine(){}
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER, targetEntity=Lending.class)
#JoinColumn(name = "LendingId")
private Lending lending;
...
Lending:
#Entity
#Table(name = "Lending")
public class Lending {
...
public Lending(){}
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER, mappedBy = "lending")
private List<LendingLine> lendingLines;
...
BlockDate:
#Entity
#DiscriminatorValue(value = "BlockLine")
public class BlockLine extends LendingLine {
public BlockLine(){
}
}
LendingLineRepository:
This class only reads from the db because the db was created by another application ( C#) where the objects are added to the db.
public class LendingLineRepository extends JpaUtil implement LendingLineRepositoryInterface {
#Override
protected Class getEntity() {
return LendingLine.class;
}
#Override
public Collection<LendingLine> findAll() {
Query query = getEm().createQuery("SELECT l FROM LendingLine l");
System.out.println(query.getResultList().size());
return (Collection<LendingLine>) query.getResultList();
}
Table LendingLine:
Choose your type of superclass according to your needs:
Concrete Class
public class SomeClass {}
Define your superclass as a concrete class, when you want to query it and when you use a new operator for further logic. You will always be able to persist it directly. In the discriminator column this entity has it's own name. When querying it, it returns just instances of itself and no subclasses.
Abstract Class
public abstract class SomeClass {}
Define your superclass as an abstract class when you want to query it, but don't actually use a new operator, because all logic handled is done by it's subclasses. Those classes are usually persisted by its subclasses but can still be persisted directly. U can predefine abstract methods which any subclass will have to implement (almost like an interface). In the discriminator column this entity won't have a name. When querying it, it returns itself with all subclasses, but without the additional defined information of those.
MappedSuperclass
#MappedSuperclass
public abstract class SomeClass {}
A superclass with the interface #MappedSuperclass cannot be queried. It provides predefined logic to all it's subclasses. This acts just like an interface. You won't be able to persist a mapped superclass.
For further information: JavaEE 7 - Entity Inheritance Tutorial
Original message
Your SuperClass LendingLine needs to define a #DiscriminatorValue as well, since it can be instantiated and u use an existing db-sheme, where this should be defined.

What is the best way to persist a hierarchy with an abstract class w/o a table

I have a simple inheritance model:
public abstract class Base {
int id;
string name;
}
public class Derived1 extends Base {
int valueD1;
}
public class Derived2 extends Base {
int valueD2;
}
How should I map the classes (with JPA annotations) so that I have separate tables for Derived1 and Derived2 (Table per concrete class), and no table for Base.
Should I use #MappedSuperclass, or #Embeddable (and skip inheritance), or #Inheritance?
Use #MappedSuperclass, and define distinct tables for each entity, IMHO,
But it depends on if you will most likely query for the parent class or if you use both derived entities for themselves, without having the need to query two tables.

Embedded single table mapping

I'm facing an issue with modelling hibernate mapping. Here is what i have:
#Entity
#Table
public class Entry {
#Id private long id;
#Embedded private Content content;
...
}
#MappedSuperclass
#DiscriminatorColumn(name="content_type")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public abstract class Content {
#Column(name="content_type") private String type;
...
}
#Embeddable
#DiscriminatorValue("A")
public class AContent extends Content {
...
}
#Embeddable
#DiscriminatorValue("B")
public class BContent extends Content {
...
}
I'd like to have that all subclasses of Content to be mapped as embedded onto the Entry class.
In other words, in result i'd like to have one Entry table with columns from all subclasses of Content.
Currently the persisting Entry test says that:
javax.persistence.PersistenceException: org.hibernate.InstantiationException:
Cannot instantiate abstract class or interface: : foo.bar.Content
So it seems that loading fails because instead of getting AContent it tries to instantiate abstract Content.
Any ideas?
Spec says...
An entity may inherit from another entity class. Entities support inheritance, polymorphic associations and polymorphic queries.
It says nothing about embeddables being inheritable and thus has no support for inheritance for them.

JPA Inheritance mapping multiple implementations

I have a problem with JPA inheritance. See my entities below. I have a Person that can be in either a House or a Car, never at the same time of course. Both Car and House implement the PersonHoldable interface. I know I cannot map an Entity directly to an interface.
This is my model:
#Entity
public class Person{
private PersonHoldable personHoldable; // either a Car or a House
// This does not work of course because it's an interface
// This would be the way to link objects without taking JPA into consideration.
#OneToOne
public PersonHoldable getPersonHoldable() {
return this.personHoldable;
}
public void setPersonHoldable(PersonHoldable personHoldable) {
this.personHoldable = personHoldable;
}
}
#Entity
public class Car implements PersonHoldable{}
#Entity
public class House implements PersonHoldable{}
public interface PersonHoldable{}
How can I map this correctly in JPA taking the following into consideration?
I tried #MappedSuperclass on an abstract implementation of PersonHoldable. Although it will work for this particular setup, the problem with this is that Car and House in reality implement more interfaces. And they are mapped to other entities as well.
The Person could have a property for every possible PersonHoldable, so in this case it could have a getCar() and getHouse() property. That does not seem very flexible to me. If I would add a Bike implementation of the PersonHoldable I would have to change my Person class.
I can map the other way around, so having a OneToOne relation only on the PersonHoldable implementation side. This would mean adding a getPerson() property to the PersonHoldable. But then it's not very easy from a Person perspective to see what PersonHoldable it is linked to.
I'm using default JPA, so no Hibernate specific tags if possible.
If this is not possible with default JPA, what would be best practice in this case?
A slight variation on your second point would be to make Person have an inheritance type and implement a CarPerson and HousePerson (and later a BikePerson) whose whole purpose is to define the specific join relationship to a specific PersonHolder implementation. That keeps the relationship intact and more easily queryable from the Person side.
#Inheritance(strategy = JOINED)
#DiscriminatorColumn(name="holdableType", discriminatorType=CHAR, length=1)
#Entity
public class Person {
// common fields
}
#Entity
#DiscriminatorValue("C")
public class CarPerson extends Person {
#OneToOne
private Car car;
}
#Entity
#DiscriminatorValue("H")
public class HousePerson extends Person {
#OneToOne
private House house;
}

Why #OneToMany does not work with inheritance in Hibernate

#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Problem {
#ManyToOne
private Person person;
}
#Entity
#DiscriminatorValue("UP")
public class UglyProblem extends Problem {}
#Entity
public class Person {
#OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
I think it is pretty clear what I am trying to do. I expect #ManyToOne person to be inherited by UglyProblem class. But there will be an exception saying something like: "There is no such property found in UglyProblem class (mappedBy="person")".
All I found is this. I was not able to find the post by Emmanuel Bernard explaining reasons behind this.
Unfortunately, according to the Hibernate documentation "Properties from superclasses not mapped as #MappedSuperclass are ignored."
Well I think this means that if I have these two classes:
public class A {
private int foo;
}
#Entity
public class B extens A {
}
then field foo will not be mapped for class B. Which makes sense. But if I have something like this:
#Entity
public class Problem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
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;
}
}
#Entity
public class UglyProblem extends Problem {
private int levelOfUgliness;
public int getLevelOfUgliness() {
return levelOfUgliness;
}
public void setLevelOfUgliness(int levelOfUgliness) {
this.levelOfUgliness = levelOfUgliness;
}
}
I expect the class UglyProblem to have fileds id and name and both classes to be mapped using same table. (In fact, this is exactly what happens, I have just checked again). I have got this table:
CREATE TABLE "problem" (
"DTYPE" varchar(31) NOT NULL,
"id" bigint(20) NOT NULL auto_increment,
"name" varchar(255) default NULL,
"levelOfUgliness" int(11) default NULL,
PRIMARY KEY ("id")
) AUTO_INCREMENT=2;
Going back to my question:
I expect #ManyToOne person to be inherited by UglyProblem class.
I expect that because all other mapped fields are inherited and I do not see any reason to make this exception for ManyToOne relationships.
Yeah, I saw that. In fact, I used Read-Only solution for my case. But my question was "Why..." :). I know that there is an explanation given by a member of hibernate team. I was not able to find it and that is why I asked.
I want to find out the motivation of this design decision.
(if you interested how I have faced this problem: I inherited a project built using hibernate 3. It was Jboss 4.0.something + hibernate was already there (you'd download it all together). I was moving this project to Jboss 4.2.2 and I found out that there are inherited mappings of "#OneToMany mappedBy" and it worked fine on old setup...)
In my case I wanted to use the SINGLE_TABLE inheritance type, so using #MappedSuperclass wasn't an option.
What works, although not very clean, is to add the Hibernate proprietary #Where clause to the #OneToMany association to force the type in queries:
#OneToMany(mappedBy="person")
#Where(clause="DTYPE='UP'")
private List< UglyProblem > problems;
I think it's a wise decision made by the Hibernate team. They could be less arrogante and make it clear why it was implemented this way, but that's just how Emmanuel, Chris and Gavin works. :)
Let's try to understand the problem. I think your concepts are "lying". First you say that many Problems are associated to People. But, then you say that one Person have many UglyProblems (and does not relate to other Problems). Something is wrong with that design.
Imagine how it's going to be mapped to the database. You have a single table inheritance, so:
_____________
|__PROBLEMS__| |__PEOPLE__|
|id <PK> | | |
|person <FK> | -------->| |
|problemType | |_________ |
--------------
How is hibernate going to enforce the database to make Problem only relate to People if its problemType is equal UP? That's a very difficult problem to solve. So, if you want this kind of relation, every subclass must be in it's own table. That's what #MappedSuperclass does.
PS.: Sorry for the ugly drawing :D
Unfortunately, according to the Hibernate documentation "Properties from superclasses not mapped as #MappedSuperclass are ignored." I ran up against this too. My solution was to represent the desired inheritance through interfaces rather than the entity beans themselves.
In your case, you could define the following:
public interface Problem {
public Person getPerson();
}
public interface UglyProblem extends Problem {
}
Then implement these interfaces using an abstract superclass and two entity subclasses:
#MappedSuperclass
public abstract class AbstractProblemImpl implements Problem {
#ManyToOne
private Person person;
public Person getPerson() {
return person;
}
}
#Entity
public class ProblemImpl extends AbstractProblemImpl implements Problem {
}
#Entity
public class UglyProblemImpl extends AbstractProblemImpl implements UglyProblem {
}
As an added benefit, if you code using the interfaces rather than the actual entity beans that implement those interfaces, it makes it easier to change the underlying mappings later on (less risk of breaking compatibility).
I think you need to annotate your Problem super-class with #MappedSuperclass instead of #Entity.
I figured out how to do the OneToMany mappedBy problem.
In the derived class UglyProblem from the original post. The callback method needs to be in the derived class not the parent class.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#ForceDiscriminator
public class Problem {
}
#Entity
#DiscriminatorValue("UP")
public class UglyProblem extends Problem {
#ManyToOne
private Person person;
}
#Entity
public class Person {
#OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
Found the secret sauce for using Hibernate at least. http://docs.jboss.org/hibernate/stable/annotations/api/org/hibernate/annotations/ForceDiscriminator.html The #ForceDiscriminator makes the #OneToMany honor the discriminator
Requires Hibernate Annotations.
In my opinion #JoinColumn should at least provide an option to apply the #DiscriminatorColumn = #DiscriminatorValue to the SQL "where" clause, although I would prefer this behaviour to be a default one.
I am very surprised that in the year 2020 this is still an issue.
Since this object design pattern is not so rare, I think it is a disgrace for JPA not yet covering this simple feature in the specs, thus still forcing us to search for ugly workarounds.
Why must this be so difficult? It is just an additional where clause and yes, I do have a db index prepared for #JoinColumn, #DiscriminatorColumn combo.
.i.. JPA
Introduce your own custom annotations and write code that generates native queries. It will be a good exercise.

Categories