hibernate inheritance in generic classes - java

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;
[...]
}

Related

Spring-Data Jpa Inheritance: Keeping Entity Id's in Children Entity

I'm dealing with a couple of Entities with Tree like structures that were getting more complicated so I decided to create an abstract class for it so code was a bit more mainainable:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeStructure<T extends TreeStructure>
{
#ManyToOne
protected T parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
protected Set<T> children = new HashSet<>();
//...
Then I have two Entities which extend it:
#Entity(name = "TreeStructureOne")
public class TreeStructureOne extends TreeStructure<TreeStructureOne>
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonProperty("TreeStructureOne_id")
private long id;
And I basically want the database to be completely unaware of this TreeStructure abstraction and save all of the fields in each Entities tableand expected InheritanceType.TABLE_PER_CLASS to deal with that. But it seems I need to define the Id in the TreeStructure Entity at least or I get:
Invocation of init method failed; nested exception is org.hibernate.AnnotationException: No identifier specified for entity: TreeStructure
And I don't want to add an ID into the abstract class since this makes three tables in the database called: HT_TREE_STRUCTURE, HT_TREE_STRUCTURE_ONE and HT_TREE_STRUCTURE_TWO with one field ID each one.
Is there any solution to that?
Since TreeStructure is not an #Entity use only #MappedSuperclass
#MappedSuperclass
public abstract class TreeStructure<T extends TreeStructure> {
instead of #Entity and #Inheritance for the parent class.
You can find #MappedSuperclass in the Oracle JEE API documentation.

Hibernate + JPA can't map different id for sub-class

For reasons that were around before I got to this project, there are tables that are similar types but have different ID columns.
So, when I try this
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Element implements Serializable {
public String title;
}
#Entity
public class PrimaryElement extends Element {
#Id
long pid;
}
#Entity
public class OtherElement extends Element {
#Id
long oid;
}
But then I get an obvious error
No identifier specified for entity: Element
Now, I can't very well put the ID in the Element class because they are obviously mapped to different columns.
I have tried various flavors of #Id and 'abstract' and #MappedSuperClass and so on..
I am at a complete loss. Is there a way around this?
Any insights would be appreciated.
Thanks!
You can either replace the #Entity and #Inheritance annotations of your Element class by #MappedSuperclass (this annotation is responsible for technical mappings and only PrimaryElement and OtherElement will be fully featured entities) or move the oid field with it's #Id annotation to the class Element and use the #AttributeOverride annotation in it's subclasses to modify the column names (in this case also the abstract class Element will be a fully featured entity).
#AttributeOverride(name="oid", column=#Column(name="primary_element_id"))
UPDATE:
#MappedSuperclass
public abstract class Element implements Serializable {
#Id
private long id;
public String title;
}
#Entity
#AttributeOverride(name="id", column=#Column(name="pid"))
public class PrimaryElement extends Element {
}
#Entity
#AttributeOverride(name="id", column=#Column(name="oid"))
public class OtherElement extends Element {
}

org.hibernate.WrongClassException on saving an entity via Hibernate

In this question I am working with Hibernate 4.3.4.Final and Spring ORM 4.1.2.RELEASE.
I have an User class, that holds a Set of CardInstances like this:
#Entity
#Table
public class User implements UserDetails {
protected List<CardInstance> cards;
#ManyToMany
public List<CardInstance> getCards() {
return cards;
}
// setter and other members/methods omitted
}
#Table
#Entity
#Inheritance
#DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public abstract class CardInstance<T extends Card> {
private T card;
#ManyToOne
public T getCard() {
return card;
}
}
#Table
#Entity
#Inheritance
#DiscriminatorOptions(force = true)
#DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Card {
// nothing interesting here
}
I have several types of cards, each extending the Card base class and the CardInstance base class respectivly like this:
#Entity
#DiscriminatorValue("unit")
public class UnitCardInstance extends CardInstance<UnitCard> {
// all types of CardInstances extend only the CardInstance<T> class
}
#Entity
#DiscriminatorValue("leader")
public class LeaderCardInstance extends CardInstance<LeaderCard> {
}
#Entity
#DiscriminatorValue("unit")
public class UnitCard extends Card {
}
#Entity
#DiscriminatorValue("leader")
public class LeaderCard extends AbilityCard {
}
#Entity
#DiscriminatorValue("hero")
public class HeroCard extends UnitCard {
// card classes (you could call them the definitions of cards) can
// extend other types of cards, not only the base class
}
#Entity
#DiscriminatorValue("ability")
public class AbilityCard extends Card {
}
If I add a UnitCardInstance or a HeroCardInstance to the cards collection and save the entity everything works fine.
But if I add a AbilityCardInstance to the collection and save the entity it fails with a org.hibernate.WrongClassException. I added the exact exception + message at the bottom of the post.
I read through some questions, and lazy loading seems to be a problem while working with collections of a base class, so here is how I load the User entity before adding the card and saving it:
User user = this.entityManager.createQuery("FROM User u " +
"WHERE u.id = ?1", User.class)
.setParameter(1, id)
.getSingleResult();
Hibernate.initialize(user.getCards());
return user;
The database entries for "cards"
The database entries for "cardinstances"
org.hibernate.WrongClassException: Object [id=1] was not of the specified subclass [org.gwentonline.model.cards.UnitCard] : Discriminator: leader
Thanks in advance for any clues how to fix this problem. If you need additional information I will gladly update my question!
According to the first paragraph of the JavaDocs for #ManyToOne:
It is not normally necessary to specify the target entity explicitly since it can usually be inferred from the type of the object being referenced.
However, in this case, #ManyToOne is on a field whose type is generic and generic type information gets erased at the type of compilation. Therefore, when deserializing, Hibernate does not know the exact type of the field.
The fix is to add targetEntity=Card.class to #ManyToOne. Since Card is abstract and has #Inheritance and #DiscriminatorColumn annotations, this forces Hibernate to resolve the actual field type by all possible means. It uses the discriminator value of the Card table to do this and generates the correct class instance. Plus, type safety is retained in the Java code.
So, in general, whenever there is the chance of a field's type not being known fully at runtime, use targetEntity with #ManyToOne and #OneToMany.
I solved the problem.
The root cause lies in this design:
#Table
#Entity
#Inheritance
#DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public class CardInstance<T extends Card> {
protected T card;
}
#Entity
#DiscriminatorValue("leader")
public class LeaderCardInstance extends CardInstance<LeaderCard> {
}
At runtime information about generic types of an class are not present in java. Refer to this question for further information: Java generics - type erasure - when and what happens
This means hibernate has no way of determining the actual type of the CardInstance class.
The solution to this is simply getting rid of the generic type and all extending (implementing) classes and just use one class like this:
#Table
#Entity
#Inheritance
#DiscriminatorColumn(name = "card_type", discriminatorType = DiscriminatorType.STRING)
public class CardInstance {
Card card;
}
This is possible (and by the way the better design) because the member card carries all the information about the card type.
I hope this helps folk if they run into the same problem.

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.

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