JPA deep inheritence annotation attributes - java

I have a hierarchical JPA mapping that is several Classes deep. Something like this
#Entity
#Inheritance(strategy= InheritanceType.SINGLE_TABLE)
public abstract class BaseEntityClass implements Serializable {
// lots of stuff common to all my entities.
}
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class EntityTypeOne extends BaseEntityClass {
// stuff common to EntityTypeOne
}
#Entity
#Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public abstract class EntityTypeTwo extends BaseEntityClass {
// stuff common to EntityTypeTwo
}
I know you use the #Inheritence method with the superclass to define the mapping strategy. But how does this work with deep hierarchies? I want all the leaf classes to be mapped to their own tables. Should I do SINGLE_TABLE with BaseEntityClass?

Your base class should be marked TABLE_PER_CLASS. Here's an example from OpenJPA:
#Entity
#Table(name="MAG")
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Magazine {
...
}
#Entity
#Table(name="TABLOID")
public class Tabloid
extends Magazine {
...
}
According to the JPA JavaDocs, the #Inheritance annotation should be specified on the root of the class hierarchy:
Defines the inheritance strategy to be used for an entity class
hierarchy. It is specified on the entity class that is the root of the
entity class hierarchy.
It is not specified by the specification if this annotation can be placed on a subclass and as such its behavior is undefined.
So to answer the question you have asked in the comments, if you have this hierarchy:
SubSub -> Sub -> Base
You only need to annotate Base with #Inheritance(strategy = TABLE_PER_CLASS).

Related

How to inherit and replace a #Entity from commons package?

I have an #Entity in a commons package:
#Entity("person")
public class Person {
//fields...
}
In an implementing application, I want to extend this entity with some custom fields.
But I still want to map to table person:
#Entity("person")
public class Person extends org.commons.Person {
//additional fields
}
Result:
Caused by: org.hibernate.DuplicateMappingException: The [Person] and [CustomPerson] entities
share the same JPA entity name: [person] which is not allowed
So how can I extend that entity and tell spring to somehow "forget" about the parent Person entity, so that only my CustomPerson is loaded?
The problem is that you are assigning the same name to both entities. You don't need to do that. You can annotate your parent entity with #Entity, #Table and #Inheritance (which by default employs single table inheritance) and your subclass entities with just #Entity:
#Entity
#Table("person")
#Inheritance
public class Person {
//fields...
}
#Entity
public class CustomPerson extends Person {
//additional fields
}

Map Collection of Interface using annotation in Hibernate

I have an interface called Rule with 2 implementing classes who all share one Abstract base class.
#MappedSuperclass
public interface Rule { .. }
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BaseRule implements Rule {
#Entity
public class ImlementingRule1 extends BaseRule {
#Entity
public class ImlementingRule1 extends BaseRule {
I'm using this Rule interface in a containgRules class as such:
#OneToMany
#JoinColumn(name = "RULES_ID")
private List<Rule> rules;
Whatever setup I try I always end up with:
Caused by: org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: mynamespace.BaseRule
I personally have found no other solution than to use the abstract base class, instead of interface.
#OneToMany
#JoinColumn(name = "RULES_ID")
private List<BaseRule> rules;
It states right here:
Annotating interfaces is currently not supported.

ORM Mapping inheritance with two strategies at once in JPA?

Now, as far as I know, there are two most used inheritance mapping strategies in JPA:
Single table - where all the classes (both subclasses and superclasses) are mapped in a single table that has as columns all the fields from all the classes; and the fields specific only to some subclasses are NULL in other corresponding entries.
.
#Entity
#Table(name="types")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="types", discriminatorType=DiscriminatorType.STRING, length=1)
public abstract class Base { //... }
#Entity
#DiscriminatorValue(value="A")
public class TypeA extends Base { //... }
#Entity
#DiscriminatorValue(value="B")
public class TypeB extends Base { //... }
Joined table strategy - where there is one table for the base class having as columns all the base class' fields and one other column for type; also there are specific tables for each subclass each having only the corresponding subclass' fields which are mapped one-to-one (by PK) with the base table.
.
#Entity
#Table(name="USERS")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="types",discriminatorType=STRING, length=1)
public abstract class Base { //... }
#Entity
#Table(name="SELLERS")
#DiscriminatorValue(value="A")
#PrimaryKeyJoinColumn(name="base_id")
public class TypeA extends User { //... }
#Entity
#Table(name="BIDDERS")
#DiscriminatorValue(value="B")
#PrimaryKeyJoinColumn(name="base_id")
public class TypeB extends User { //... }
In my domain I have these classes:
public abstract class Base {
//data
}
public class TypeA extends Base {
//only one field
}
public class TypeB extends Base {
// many more fields
}
and I would want a 1. inheritance mapping strategy between TypeA and Base and 2. inheritance mapping strategy between TypeB and Base (this is an oversimplification; there are actually many entities) because I wouldn't want another table for one more field. Is this possible in JPA and does it make any sense?
Edit: I do not know what is wrong with the code formatting...
No, this isn't possible in JPA. You need to configure #Inheritance at the base class level, and it can't be overridden at the subclass level.
Your options do however include one more possibility which you didn't mention, which is InheritanceType.TABLE_PER_CLASS. If you use this, you'd have N tables (for N subclasses) rather than one or N+1. And it works well for some cases, see the advantages/disadvantages in the link.

hibernate inheritance in generic classes

i have a generic class which is supper class of some non-generic class and those are just setting its generic parameter like this:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
class A<T>{
#Id
getId(){..}
setID(int id){..}
int id
T t;
T getT(){...}
setT(T t){...}
}
and
#Entity
class B extends A<Integer>{}
but hibernate says that B does not have an identifier what should I do?
If A won't be directly persisted, but you do want it's subclasses to pick up some (or all) of its Hibernate annotations, you should use #MappedSuperclass:
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
class A<T>{
#Id
getId(){..}
setID(int id){..}
int id
T t;
T getT(){...}
setT(T t){...}
}
You need to add the #Entity annotation to class A as well.
The #Transient annotation on attribute t should help with your second exception
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
class A<T> {
#Id
getId(){..}
setID(int id){..}
int id
#Transient
T t;
T getT(){...}
setT(T t){...}
}
I agree with reply No. 1, use #MappedSuperclass for A - don't make something abstract an Entity.
You should probably make this class specifically abstract too.
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class A<T>{
#Id
getId(){..}
setID(int id){..}
int id
T t;
T getT(){...}
setT(T t){...}
}
A table-per-class strategy often requires this kind of abstract base.
Then the subclass specifies the table name, and additional fields.
#Entity
#Table(name="MY_INTEGERS")
class B extends A<Integer>{}
(Personally I would move this variable type into the subclass, but I don't know what you're trying to achieve).
After lots of testing, trying to get Java parameterisation working with an abstract parent (Single-table inheritance), and an abstract child table (one-table-per-class inheritance), I've given up.
It may be possible, but often you get problems where Hibernate tries to instantiate an abstract (parameterised) class as an entity. this is when you get the error "A has an unbound type and no explicit target entity."
It means Hibernate doesn't have a parameter value for a parameterised type.
I found that tests for the extending classes were fine, but tests around parent entities would break.
I would suggest rewriting it using the JPA inheritance, moving the parameterised stuff down into extending classes. That way you get the same polymorphism back from the database.
#MappedSuperclass
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "CLASS_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class ClassA {
[...]
}
extension B:
#Entity
#DiscriminatorValue=("B")
public class ClassB extends ClassA {
#OneToOne
#JoinColumn(name = "mycolumn_id")
private Integer instance;
[...]
}
extension C:
#Entity
#DiscriminatorValue=("C")
public class ClassC extends ClassA {
#OneToOne
#JoinColumn(name = "mycolumn_id")
private String instance;
[...]
}

Persist collection of interface using Hibernate

I want to persist my litte zoo with Hibernate:
#Entity
#Table(name = "zoo")
public class Zoo {
#OneToMany
private Set<Animal> animals = new HashSet<Animal>();
}
// Just a marker interface
public interface Animal {
}
#Entity
#Table(name = "dog")
public class Dog implements Animal {
// ID and other properties
}
#Entity
#Table(name = "cat")
public class Cat implements Animal {
// ID and other properties
}
When I try to persist the zoo, Hibernate complains:
Use of #OneToMany or #ManyToMany targeting an unmapped class: blubb.Zoo.animals[blubb.Animal]
I know about the targetEntity-property of #OneToMany but that would mean, only Dogs OR Cats can live in my zoo.
Is there any way to persist a collection of an interface, which has several implementations, with Hibernate?
JPA annotations are not supported on interfaces. From Java Persistence with Hibernate (p.210):
Note that the JPA specification
doesn’t support any mapping annotation
on an interface! This will be resolved
in a future version of the
specification; when you read this
book, it will probably be possible
with Hibernate Annotations.
A possible solution would be to use an abstract Entity with a TABLE_PER_CLASS inheritance strategy (because you can't use a mapped superclass - which is not an entity - in associations). Something like this:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractAnimal {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
...
}
#Entity
public class Lion extends AbstractAnimal implements Animal {
...
}
#Entity
public class Tiger extends AbstractAnimal implements Animal {
...
}
#Entity
public class Zoo {
#Id #GeneratedValue
private Long id;
#OneToMany(targetEntity = AbstractAnimal.class)
private Set<Animal> animals = new HashSet<Animal>();
...
}
But there is not much advantages in keeping the interface IMO (and actually, I think persistent classes should be concrete).
References
Annotations, inheritance and interfaces
using MappedSuperclass in relation one to many
Polymorphic association to a MappedSuperclass throws exception
I can guess that what you want is mapping of inheritance tree.
#Inheritance annotation is the way to go.
I don't know if it will work with interfaces, but it will definitely work with abstract classes.
I think you have to annotate the interface too with #Entity and we have to annotate #Transient on all getters and setters of interface.

Categories