MySQL tables for abstract/concrete classes using Hibernate for ORM - java

I have the following classes:
public abstract class Generic(){
private int Id;
...
}
public class ExtA extends Generic(){
private Generic fieldA();
private Generic fieldB();
...
}
public class ExtB extends Generic(){
private Generic fieldA;
....
}
public class ExtC extends Generic(){
...
}
Sorry for the vague example.
I am trying to find a way to save those objects to the database, but can't seem to find a good way. I wish to have separate tables for ExtA, ExtB, ExtC, and then use foreign keys to relate the contained fields. I am using MySQL, and working with Java, Spring, Hibernate. Can someone please show me an example of how to do this, or point me to some tutorial?
I should mention I am new at working with databases.

Something like this. i'm relatively sure the Table_Per_Class strategy will let you ask for any "Generic" object and query all it's subclass tables. Keep in mind if you do this, the ID should be unique across all of your subclass types. You'll have to figure out a strategy for this use the right annotations to tell hibernate what to do. But in the mean time, this example assumes you're assigning it manually before saving.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Generic {
#Id #Column(name="ID")
private Long id;
...
}
#Entity
#Table(name="EXTA")
public class ExtA extends Generic {
#Column(name="fieldA")
private Generic fieldA;
}

Related

How to inherit from multiple base abstract classes in JPA

I faced a problem how I can create JPA entity which extends multiple base abstract classes (without creating additional table). I know there is an annotation #MappedSuperclass, but it gives an ability to create only one base class as soon as we use extends and multiple inheritance is not a Java feature.
For example, I have two bases:
#MappedSuperclass
public abstract class Authored {
#ManyToOne
private User user;
}
and
#MappedSuperclass
public abstract class Dated {
private String creationDate;
}
I expect that some of my models will extend only Authored, some -- only Dated, and some -- both.
Though it's only possible to write
#Entity
public class MyEntity extends Authored {
...
}
or
#Entity
public class MyEntity extends Dated {
...
}
Some discussions propose to inherit classes in line (e.g. Authored and AuthoredAndDated) but this seems too dirty, none of this bases logically can't extend another one.
ADDITION
I have to note that this style is supported in other frameworks like Django (due to multiple inheritance in python) so it's strange that JPA doesn't support it.
I am sorry to disappoint you, but there is no other solution than creating AuthoredAndDated as you suggested.
We faced in the same issue for our entities and went with the same procedure.
We have a
#MappedSuperclass
public class TimestampedObject {
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "created_at")
private Date createdAt;
#UpdateTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "updated_at")
private Date updatedAt;
}
and a
#MappedSuperclass
public class IdObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id", updatable = false, columnDefinition = "serial")
private Long id;
}
Thus we created a TimestampedIdObject for this purpose.
Edit:
If you find another suitable solution, it would be great if you could post it here, as we have the same issue...
You should use an #Embeddable / #Embedded for goal by replacing inheritance with composition.
First, do not use #MappedSuperClass, but use #Embeddable instead for your classes you want to share the attributes with:
#Embeddable
public class Authored {...}
#Embeddable
public class Dated {...}
In the next step your Entity should not inherit from Authored or Dated but instead get an attribute referencing them:
#Entity
public class MyEntity {
#Embedded
private Authored authored;
#Embedded
private Dated dated;
}
If you want to get behaviour out of this, where you can generically access without those new attributes, you would need to introduce an interface exposing the necessary methods.
For expample if MyEntity should be able to provide details on last updates and creation, you would introduce an interface Authorable which defines to methods to access the relevant data.
public interface Authorable { /* necessary methods */ }
MyEntity will implement this interface then:
#Entity
public class MyEntity implements Authorable {
/* previous content plus implemented mehtods from interface */
}

Querying different EntitySubclasses in one query

I have 3 entities that have something in common.
#Entity
public class BaseEntity {
Date updatedAt;
}
#EntitySubclass
public class A extends BaseEntity {
String someData;
}
#EntitySubclass
public class B extends BaseEntity {
int someData;
}
#EntitySubclass
public class C extends BaseEntity {
boolean someData;
}
Can I make one query to create List<BaseEntity> or Query<BaseEntity>?
Supposedly something like this?
ofy().load().type(User.class).filter("updatedAt > ", someDate)
Actually, with Objectify I think you can. Here they give an example:
https://code.google.com/p/objectify-appengine/wiki/Entities#Polymorphism
I think the datastore can't do it natively, but Objectify will setup a structure for you that allows polymorphism anyway.
The other answer is not strictly correct. With Objectify, you can query by kind on a superclass and it will seamlessly query all applicable datastore entities which match the inheriting #Entity annotated classes.

Create index on field from parent class

I have problem with adding index. I use hibernate with annotation driven configuration.
I have something like this:
#MappedSuperclass
public abstract class BaseEntity {
#Id
private String id;
private String profileId;
...
//getters and setters
}
and several child classes
#Table(name="note")
public abstract class Note extends BaseEntity{
//different fields
}
#Table(name="message")
public abstract class Message extends BaseEntity{
//different fields
}
I want to add index to field "profileId" in class BaseEntity. But if I do so, with annotation #Index(name="profileid_index"), it creates only for table "note", and fails on "message", because index "profileid_index" already exist.
I did not find way, how to make hibernate generate unique index names. Or may be someone knows another solution how to index field in parent class.
Did you have a look on #Tables annotation: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/ ?
You can do stuff like:
#Tables(value={#Table(appliesTo="table1", indexes={#Index(name="index1", columnNames={"column1", "column2"})}),
#Table(appliesTo="table2", indexes={#Index(name="index1", columnNames={"column1", "column2"})})})
Should help in your case if you put this annotation to your #MappedSuperclass, although I don't know if there is a more cleaner solution
Being more precise, you could try for your case:
#Tables(value={#Table(appliesTo="note", indexes={#Index(name="index_profile_id1", columnNames={"profileId"})}),
#Table(appliesTo="message", indexes={#Index(name="index_profile_id2", columnNames={"profileId"})})})

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