ORM - ManyToMany relation on MappedSuperclass - java

I have a classes as follows:
AnimalClass [Id, Name, Set<Tag>]
|
+-- FishClass [FishSpecific]
+-- MammalClass [MammalSpecific]
Tag [Name]
So any animal can have any number of associated tags.
For that I use in AnimalClass:
#JoinTable(name="Animal_Tag")
#JoinColumn(name="animal_id", referencedColumnName="id", nullable=false)
#OneToMany(cascade=CascadeType.ALL)
#Getter
protected Set<Tag> tags = new HashSet<Tag>();
My problem is that, Hibernate creates the m-n table as:
Animal_Tag [FishClass_id, MammalClass_id, Tag_id].
I would prefer to have some kind of enumeration as:
Animal_Tag [Animal_id, AnimalTypeEnumeration[ Fish | Mammal ], Tag_id].
Thanks!

The table structure that you want would have a foreign key that can point to different tables, depending on the value of another field. I don't think any DB allows that.
Polymorphism with ORM is always tricky. Best thing to do is just avoid it if you can. Otherwise, maybe you could use #Inheritance(strategy=InheritanceType.JOINED) on Animal? This would result in a table structure like this:
TABLE Animal
- id (primary key)
TABLE Fish
- id (foreign key -> Animal)
- fins
- scales
TABLE Mammal
- id (foreign key -> Animal)
- mammaries

I suspect you can't do that with a mapped superclass.
The thing about a mapped superclass is that it doesn't define a persistent type. It defines a sort of template for a persistent types. Every time you define a subclass of it which is annotated #Entity, you create an instance of the template, but in the data model, there is no relationship between those types. The use of the mapped superclass is almost a shortcut for a copy-and-paste of a given set of fields into the new entity class.
So, as far as the data model is concerned, there is no possible animal_id, because there is no such type as animal. Only fish and mammal exist in the database.
Can you make AnimalClass an entity instead of a mapped superclass? If you use the table-per-class inheritance strategy, you won't need to create a table for it. But it will make animal a type, which means the ORM will be able to use an animal_id.

May be use "table per subclass wih discriminator" inheritance for AnimalClass? In case of hibernate it result fowing hirecaly:
AnimalClass [Id, AnimalTypeEnumeration (discriminator), Name]
|
+-- FishClass [FishSpecific]
+-- MammalClass [MammalSpecific]
Animal_Tag [Animal_id, Tag_id]

Related

Schema design for inheritance strategy Table Per Class

I am trying to design a database schema so that it is applicable for the Hibernate's Table Per Class inheritance strategy. So, I will not let Hibernate to generate the tables, instead I need to design them myself in Liqibase but in such way that Hibernate can use them with that strategy.
My entity classes should look like this.
Vehicle.java
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle {
#Id #GeneratedValue
private int id;
private String name;
private String brand;
}
Car.java
#Entity
public class Car extends Vehicle {
private String oil;
}
Bike.java
#Entity
public class Bike extends Vehicle {
private String frame;
}
Now, I know that that this strategy means that all the fields are in all tables, but I am not sure about two things.
1) Do I need to include the ID in the derived tables?
2) Do I even need a Vehicle table in the DB, since it will actually never be used as an entity (rather just an abstract class)?
I'd appreciate if someone could clear that for me.
Now, I know that [the table-per-concrete-class] strategy means that all the fields are in
all tables,
Well no, you seem to be confused. With InheritanceType.TABLE_PER_CLASS, each concrete entity class E maps to a table that contains columns corresponding to all the fields of E, including inherited ones, but not including fields of entities that are not superclasses of E.
This is to be contrasted with InheritanceType.SINGLE_TABLE, in which all entity types in a whole inheritance hierarchy map to the same table, which then necessarily contains a column for each property of each entity in the hierarchy (without duplicating inherited fields).
Note also that it is odd that your Vehicle entity contains fields with the same names as fields of its subclasses. Java fields are not polymorphic, so this is unlikely to be what you want. It's certainly unnecessary.
but I am not sure about two things.
1) Do I need to include the ID in the derived tables?
Supposing that you stick with TABLE_PER_CLASS, yes, each entity table needs to provide a column for each persistent property of the corresponding entity class, including those inherited from superclasses. That includes id.
2) Do I even need a Vehicle table in the DB, since it will actually
never be used as an entity (rather just an abstract class)?
Not if you actually declare it abstract, but if you leave it concrete, then yes, you need a table for it. And that makes sense, because in that case you could have Vehicle entities that are neither Cars nor Bikes.
Hibernate schema generation tool should be able to generate your schema if you annotate your entities correctly.
In this case Vehicle table which be generated since your entity class is concrete. You need to define the class as abstract.
All the fields defined in the superclass (including the #Id) will be copied in the corresponding child class tables. There is a limitation though regarding id auto generation. You cannot use the table per class inheritance strategy with the GenerationType.IDENTITY strategy since the generated keys should be unique across all tables. Identity creates unique ids per table
Make sure the Generation.AUTO strategy does not map to the IDENTITY strategy for your RDBMS (You can specify a strategy explicitly eg GenerationType.TABLE to override the default)

JPA Table per Class Inheritance with different Id names

I have the following mapping:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
#Id
#GeneratedValue
Long id;
}
#Entity
#Table(name = "car")
#AttributeOverride(name = "id", column = #Column(name = "car_id"))
public class Car extends Vehicle {
}
#Entity
#Table(name = "bus")
#AttributeOverride(name = "id", column = #Column(name = "bus_id"))
public class Bus extends Vehicle {
}
And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities. For this sake I've created the following Spring Data repository
public interface VehicleRepository extends CrudRepository<Vehicle, Long> {
}
and try to use it like this: vehicleRepository.findAll();
However, in this case I get java.sql.SQLSyntaxErrorException: ORA-00904: "KEY": invalid identifier. Seems like using #Inheritance together with #AttributeOverride for #Id field doesn't work.
What I'd like to point out is that if Car and Bus entities had the same mapping for #Id it would work perfectly (but it's not the case: "car_id" and "bus_id")
Also, I've tried to move #Id field from Vehicle class to subclasses, however it turned out that every #Entity should contain an #Id.
One more thing I'd like to mention is that I've tried using #MappedSuperclass instead of #Inheritance but in this case I'm not able to query with abstact Vehicle type.
Could anyone help me with that?
Thanks
You say,
And what I'm trying to achieve is to query different tables for retrieving both Car and Bus entities.
, but as a first consideration, you should evaluate whether you really want to do that. Think about this:
The single-table inheritance strategy is generally the fastest for whole-hierarchy queries such as you imagine performing. It can perform both whole-hierarchy and concrete-entity operations with single queries, without joins or unions.
The single-table and joined inheritance strategies ensure that all entities in the hierarchy have distinct keys, which is not necessarily the case for the table-per-class strategy.
The single-table and joined inheritance strategies facilitate relationships involving the abstract superclass; these are not well supported by the table-per-class strategy.
Support for the table-per-class strategy is optional. JPA providers are not required to support it, and the default provider in the GlassFish reference implementation in fact does not support it. Applications that rely on table-per-class therefore are not guaranteed to be portable. (Your provider, Hibernate, does support it.)
You go on to say,
However, in this case I get java.sql.SQLSyntaxErrorException:
ORA-00904: "KEY": invalid identifier. Seems like using #Inheritance
together with #AttributeOverride for #Id field doesn't work.
#AttributeOverride is only specified to work for overriding the attributes of mapped superclasses and fields and properties of embedded classes. It does work for #Id properties if they appear in those contexts. It is not specified to work (though neither is it specified to not work) for persistent fields and properties inherited from an entity superclass, but do observe that it cannot work for such properties with either the single-table or the joined inheritance strategy.
If #AttributeOverride did happen to work for you, that use would be non-portable. On the other hand, JPA has nothing else to accomplish what you want. A particular persistence provider could have an extension that supports it, but Hibernate has not historically done so -- all properties inherited from an entity superclass are mapped with the same names.
You also say,
One more thing I'd like to mention is that I've tried using
#MappedSuperclass instead of #Inheritance but in this case I'm not
able to query with abstact Vehicle type.
JPA does not provide a solution for your particular combination of requirements:
Mapping each concrete entity class to a separate table,
Naming the ID to a different column name in each entity table, and
Supporting polymorphic queries on the abstract supertype.
If you are unwilling to change any of those then you'll have to rely on an extension. And in that case you're in luck: Hibernate supports polymorphic queries where the polymorphic type is not mapped as an entity. Thus, if you're willing to make your application explicitly dependent on Hibernate, you can probably get where you want to be.
Specifically, to do this in Hibernate you would rely on "implicit polymorphism". To do this, you would avoid mapping the superclass as an entity, and from your experience, I guess it should not be a mapped superclass, either. It can be an ordinary class, though its properties would not be persistent, or you could use an interface instead. If your Vehicle class has properties that you want to make persistent, then you could change it to an embeddable class. You would furthermore annotate each of the vehicle entities to specify implicit polymorphism, for example:
#Entity
#Polymorphism(type = PolymorphismType.IMPLICIT)
// ...
public class Car implements Vehicle {
// ...
}
The Hibernate docs claim that implicit polymorphism is the default, but I recommend applying the #Polymorphism annotation anyway, for clarity.

JPA / HIBERNATE Entity from two inherited classes

Can some one help me with example / idea how to deal with this scenario.
I have class Animal and class Cat , that extend Animal.
In Animal I have some field, also some new field in my Cat class.
So I have to create hibernate entity, that store my Cat in db and somehow to have info about parents field.
use annotation #Inherance
with
strategy=InheritanceType.ONLY_ONE_TABLE (or SINGLE_TABLE)
strategy=InheritanceType.TABLE_PER_CLASS
strategy=InheritanceType.JOINED

bidirectional one-to-one relationship (mappedBy)

You create a bidirectional one-to-one relationship using fields on
both classes, with an annotation on the child class's field to declare
that the fields represent a bidirectional relationship. The field of
the child class must have a #Persistent annotation with the argument
mappedBy = "...", where the value is the name of the field on the
parent class. If the field on one object is populated, then the
corresponding reference field on the other object is populated
automatically.
What can a bidirectional one-to-one relationship do?
Can someone give me a example?
And why I always got this error.
Class "com.example.datastore.Employee" has field "contactInfo" with
"mapped-by" specified as "contactInfo". This field doesnt exist in the
target of the relation ("com.example.datastore.ContactInfo")!!
Thanks in advance!
I try to answer from what I learnt from Hibernate/JPA (which I think is similar)
Seems that your ContactInfo do not have relationship to Employee. To use what you described as an example for bidirectional one-to-one relationship, you will see something like (it is probably not syntactically correct, just to give u idea):
public class Employee {
//... other relationship or fields
#OneToOne(mappedBy="employee") // the field in ContactInfo
private ContactInfo contactInfo;
}
public class ContactInfo {
#OneToOne
#JoinColumn("EMP")
private Employee employee;
}
The 'real' relationship in persistence layer is in fact dominated by ContactInfo.employee. Setting Employee.contactInfo will not cause persistence layer to contain correct data.
I wish this help and applies to JDO too. :P
It sounds like you have a one-to-one of Employee to ContactInfo. An employee has exactly one contact info, and a contact info belongs to exactly one employee. That's a bidirectional one-to-one. Your error is occurring because "mapped-by" needs to specify the name of the property of the other object that refers back to this one. For example, if you have
class Employee {
private ContactInfo contactInfo;
}
class ContactInfo {
private Employee employee;
}
then when you map the Employee.contactInfo property, its "mapped-by" would be "employee" because that's the property that it's "mapped by" in the ContactInfo.
First the Error What the error is saying is that contactInfo is not a field of class com.example.datastore.ContactInfo . They field mapped by must be a field\property of the class you are mapping to .
Second The concept of Bi-Direction Mapping
It's just that two tables in one-to-one or one-to-many relationship where both entities will have knowledge of the other s. You do not always need it and that depends on situation and context but generally is more common to have bi-directional one to many than one to one.
You question is about one-to-one so to give you an example of when both entities need to know about each other from Hibernate docs A bidirectional one-to-one association on a join table is possible, but extremely unusual. But if you really wanted to be able to get either entiy from which ever table (which is very rare) then you create bi-directional one to one
Person and Address just to quote an example

Can I use CompositeId on subclasses of Discriminated Entity Hierarchy?

I have a entity hierarchy has single table for all subclasses strategy. And I want to use each properties - mapped #ManyToOne - as primarykey. When I put #Id annotation, I'm getting this error:
java.lang.ClassCastException: org.hibernate.mapping.SingleTableSubclass cannot be cast to org.hibernate.mapping.RootClass
So how can I provide to define all properties as identifier on discriminated hierarchy?
Your ID needs to be unique for the highest object in the hierarchy that is an entity. You can't define ID on subclasses and have an superclass, that is an entity, that has no ID.
Think of it this way, entityManager.find(SuperClass.class, 2); is a perfectly legal call. If ID were defined on subclasses as an FK column, more than one of them could have ID 2! What would be returned?
Using the FK side of a ManyToOne relationship as a primary key is nonsensical in the first place. It would by definition be a OneToOne then.

Categories