Hibernate mapping generic class with #MappedSuperClass ancestor - java

I've run into a problem and I don't know, if is possible to solve it. Let me show you my code and explain the situation. I have an abstract class User mapped as superclass.
#MappedSuperclass
public abstract class User extends AbstractEntity {
}
Then I have two classes, Person and Company, extending the superclass.
#Entity
public class Person extends User {
}
#Entity
public class Company extends User {
}
Since this, everything is ok. I have two tables called by the entity names and it works june fine. But, I have some entity called Invoice, where I have to map Person or Company into.
#Entity
public class Invoice extends AbstractEntity {
#ManyToOne()
#JoinColumn(name="user_id", updatable=false)
private Class<? extends User> user;
}
The problem is that I don't know, which implementation of User will be mapped to this Invoice entity. With this solution, Hibernate gives me an error org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.xxx.user references an unknown entity: java.lang.Class
I understand it, but please, is there any way to implement this behaviour without an exception ? Or am I completely wrong and nothing similar can be done in ORM ?
I cannot use private User user because User is a #MappedSuperclass, not an #Entity.
Thanks !

Well instead of using lower bound, why not just use the type User:
#Entity
public class Invoice extends AbstractEntity {
#ManyToOne()
#JoinColumn(name="user_id", updatable=false)
private User user;
}
Like this, you should still be able to put any class that extends from the User class as the previous declaration. However this will not work as User is not an Entity.
It should however work, if you declare User as entity but set inheritance strategy to TABLE_PER_CLASS.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User extends AbstractEntity {
}

Hibernate won't see the generic "User" at runtime (type erasure).
Use User instead of Class<? extends User>
Note that even if Hibernate could see the generic type, you'd still have a user Class not a User...

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.

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.

How to annotate composition of abstract class instance?

I have a design of Person as shown in the following picture. The design is to allow a person to change his role from Staff to Student, Student to Faculty and so on.
I want to persist this system to DB using hibernate annotation. Does anyone know how to do that?
Thanks a lot!
So you have 1:N relation between Entity Persona and abstract entity Person Role. Think this may work for you.
#Entity
public class Person {
// Here you have all roles in some collection.
#OneToMany(mappedBy="person", fetch=FetchType.LAZY)
private List<PersonRole> roles;
...
}
#Entity
public abstract class PersonRole {
#ManyToOne
#JoinColumn(name="PERSON_ID")
private Person person;
...
}
#Entity
public class Staff extends PersonRole {
...
}
Also don't forget to set proper
#Inheritance(strategy=InheritanceType.<strategy>)
to define how class model is mapped to relational model.
Edit: Unfortunately #MappedSuperclass can't be used in relations mapping so this is not an option here as long as you would like to have PersonRole collection in Person entity.

Is there a way of inheriting Hibernate's #Where annotation?

We have a DB table user that evolved quite a bit and we don't want to load legacy users into the app. Legacy user is identified by user_type column.
If I use following mapping then everything works as expected:
#Entity
#Table(name="user")
#Where("user_type = 2") // 1 is legacy
class User {
#Column(name="user_type")
int type;
}
I need to map user table multiple times and I want to stay DRY. So I thought I can extract #Where bit to a super class and inherit it like so:
#Where("type = 2") // 1 is legacy
abstract class BaseUser {
}
#Entity
#Table(name="user")
class User extends BaseUser {
}
I have a following test (I hope it's self-explanatory enough) that fails though:
#Test
#DbUnitData("legacy_user.xml") // populates DB with 1 user (id=1) with type=1
public void shouldNotGetLegacyUser() {
assertThat(em.find(User.class, 1L)).isNull();
}
Is there a way of inheriting a class with Hibernate's #Where annotation?
What you are really looking for is not the #Where but the #DiscriminatorColumn and #DiscriminatorValue. These annotations allow you to map two #Entity objects to the same table based on a #DiscriminatorColumn.
The Hibernate manual has a paragraph on it:
Mapping inheritance
You would basically create a superclass, BaseUser and two Sub classes, LegacyUser and User:
#Entity
#Table(name = "COM_ORDER")
#DiscriminatorColumn(name = "COM_ORDER_TYPE", discriminatorType = DiscriminatorType.INTEGER)
public class BaseUser {
#Id
private Long id;
<Enter your generic columns here, you do not need to add the user_type column>
}
#Entity
#DiscriminatorValue("1")
public class LegacyUser extends BaseUser {
<Enter your legacy specific fields here>
}
#Entity
#DiscriminatorValue("2")
public class LatestUser extends BaseUser {
<Enter your new and improved user fields here>
}
With this setup, you can easliy expand the number of user types by creating new classes which extend the BaseUser class. You need to keep in mind that the fields on the actual table can only be not-null for fields in the BaseUser class. Fields in the UserType related classes should always be nullable in the database since they will only ever be used by a specific user type.
Edit:
I've edit the example to conform to the setup I'm currently using in my own project. This setup works fine for me.

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;
}

Categories