JPA inheritance: Mapped Superclass vs Table Per Class - java

I have an abstract parent class with two child classes, where I want the child classes to have their own tables. I also have another class that has a relationship to the parent class:
// Class that has the mapping to the abstract class
#Entity
#Table(name="telephone_numbers")
public class TelephoneNumber implements Serializable {
#Id
#Column(name="number")
private String number;
#Column(name="originating_carrier")
private String originatingCarrier;
#OneToOne(mappedBy = "number")
private TelephoneNumberAssignment assignment;
... getters and setters ...
}
// Classes involved in inheritance
public abstract class TelephoneNumberAssignment implements Serializable {
#Id
#OneToOne
#JoinColumn(name="number")
private TelephoneNumber number;
... getters and setters ...
}
#Entity
#Table(name="telephone_numbers_fixed_line")
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class FixedLineNumberAssignment extends TelephoneNumberAssignment {
#Column(name="recorded")
private Boolean recorded;
public FixedLineNumberAssignment() {
}
... getters and setters ...
}
#Entity
#Table(name="telephone_numbers_mobile")
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class MobileNumberAssignment extends TelephoneNumberAssignment {
#ManyToOne
#JoinColumn(name = "customer_id")
private Customer customer;
public MobileNumberAssignment() {
}
... getters and setters ...
}
According to the answer to this question, in order for my JPA annotations in the superclass to carry over to the child class I need to use #MappedSuperclass. However, due to the requirement of the mapping to the superclass, a following exception occurs:
Caused by: org.hibernate.AnnotationException: Unknown mappedBy in: com.vtsl.domain.numbering.TelephoneNumber.assignment, referenced property unknown: com.vtsl.domain.numbering.TelephoneNumberAssignment.number
According to the answer to this question I could use TABLE_PER_CLASS to solve this issue. However, if I do that my superclass JPA annotations don't seem to carry over; if I perform JPQL query
return entityManager.createQuery("SELECT mob FROM MobileNumberAssignment as mob INNER JOIN FETCH mob.number", MobileNumberAssignment.class).getResultList();
the results returned do not have their number field populated (upon further inspection I found that hibernate does not detect the number property when trying to resolve the properties on the result object). However, when I perform the following:
//Result set has 1 object
entityManager
.createQuery("SELECT mob FROM MobileNumberAssignment as mob INNER JOIN FETCH mob.number WHERE mob.number.number = :number", MobileNumberAssignment.class)
.setParameter("number", "number that exists")
.getResultList()
//Result set has 0 object
entityManager
.createQuery("SELECT mob FROM MobileNumberAssignment as mob INNER JOIN FETCH mob.number WHERE mob.number.number = :number", MobileNumberAssignment.class)
.setParameter("number", "number that does not exist")
.getResultList()
the results seem to indicate that the parameter got successfully resolved after all.
Why is it that the number property does not get populated?

It appears I have drawn all the wrong conclusions from the symptoms. The issue was not at all with the inheritance specification, but rather with the specification of the mapped primary key. According to every resource I found
#Id
#OneToOne
#JoinColumn(name="number")
private TelephoneNumber number;
should work just fine, however it seems that in the version of hibernate I am using (4.10) it does not. Changing the mapping to use #MapsId resolved the issues I was observing, making the new (and now working) TelephoneNumberAssignment to look like:
#Entity
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class TelephoneNumberAssignment implements Serializable {
#Id String numberString;
#MapsId
#OneToOne
#JoinColumn(name="number")
private TelephoneNumber number;
... getters and setters ...
}

Related

Hibernate extending entity for read with additional joins

I have an entity that corresponds to some table in DB
#Entity
#Table
public class MyEntity {
#Id
private Long id;
#Column
private String description;
#OneToMany
private List<Foo> foos;
...
//getters setters
...
}
And I would like to extend that entity for read-only and have additional joins to another table. I need this joins only in one case for specific read user-case:
#Entity
public class ExtendedEntity extends MyEntity {
#ManyToMany(fetch = FetchType.EAGER)
private List<Bar> bars;
}
Just using extensions will create a discriminator column in DB, which I don't need since I'm using the ExtendedEntity only for the read. I found I hack with using #DiscriminatorFormula that will be resolved to the same Integer value like this:
#DiscriminatorFormula("0")
#DiscriminatorValue("0")
public class MyEntity
#DiscriminatorValue("00")
public class ExtendedEntity extends MyEntity
But this really looks like a hack. With using #MappedSuperclass I cannot have associations, but this is the main reason I need to extend my entity.
What other options do I have and what are the best practices for this use-case?

Hibernate JPA Metamodel Generator, missing #ManyToOne Attribute in #Embedabble

We're using Hibernate to generate JPA Metamodel - Classes for our Entities.
That's working quite fine for most cases, but if there's a relation (#ManyToOne) to an Entity in an #Embeddable, there's no SingularAttribute generated.
Classes are implemented like this (following a "Generation Gap Pattern"):
#Entity
public class EntityA extends EntityABase {
....
}
#MappedSuperClass
public abstract class EntityABase {
#EmbeddedId
private EntityAPrimaryKey primaryKey;
}
#Embeddable
public class EntityAPrimaryKey extends EntityAPrimaryKeyBase {
...
}
#MappedSuperClass
public class EntityAPrimaryKeyBase {
#ManyToOne
#NotNull
private EntityB entityB;
private String someText;
}
Result is like this
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EntityAPrimaryKeyBase.class)
public abstract class EntityAPrimaryKeyBase_ {
public static volatile SingularAttribute<EntityAPrimaryKeyBase, String> someText;
}
So, the "ordinary" field someText is generated fine, but the Attribute for the relationship to EntityB is missing.
The expected output would be
#Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
#StaticMetamodel(EntityAPrimaryKeyBase.class)
public abstract class EntityAPrimaryKeyBase_ {
public static volatile SingularAttribute<EntityAPrimaryKeyBase, String> someText;
public static volatile SingularAttribute<EntityAPrimaryKeyBase, EntityB> entityB;
}
All other Metamodel-Classes are generated fine (EntityB, EntityA, EntityABase etc.)
I've tried removing the indirection between EntityAPrimaryKey and EntityAPrimaryKeyBase (and annotating the EntityAPrimaryKeyBase with #Embeddable), but that doesn't change the output.
Any ideas why the Attribute entityB isn't generated? Would be very helpful!
JPA Spec (11.1.17) - EmbeddedId Annotation
The EmbeddedId annotation is applied to a persistent field or property of an entity class or mapped superclass to denote a composite primary key that is an embeddable class. The embeddable class must be annotated as Embeddable.[104] Relationship mappings defined within an embedded id class are not supported.
JPA Spec (2.11.2) - Mapped Superclasses
A class designated as a mapped superclass can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself.
You cannot have a relationship mapping within an #Embeddable that is to be used as an #EmbeddedId. Even tho you have the relationship in the #MappedSuperclass, 2.11.2 states that mappings are applied to the subclasses, which in this case is the #Embeddable.
For the record, changing the class structure too (and thus making it according to the spec) solves the Problem:
#Entity
public class EntityA extends EntityABase {
....
}
#MappedSuperClass
public abstract class EntityABase {
#EmbeddedId
private EntityAPrimaryKey primaryKey;
#ManyToOne
#MapsId("entityBID")
private EntityB entityB;
}
#Embeddable
public class EntityAPrimaryKey extends EntityAPrimaryKeyBase {
...
}
#MappedSuperClass
public class EntityAPrimaryKeyBase {
private Long entityBID;
private String someText;
}

I am trying to extend a base class which has Id in two different subclass to hit JPA query [duplicate]

I am creating entities that are the same for two different tables. In order do table mappings etc. different for the two entities but only have the rest of the code in one place - an abstract superclass. The best thing would be to be able to annotate generic stuff such as column names (since the will be identical) in the super class but that does not work because JPA annotations are not inherited by child classes. Here is an example:
public abstract class MyAbstractEntity {
#Column(name="PROPERTY") //This will not be inherited and is therefore useless here
protected String property;
public String getProperty() {
return this.property;
}
//setters, hashCode, equals etc. methods
}
Which I would like to inherit and only specify the child-specific stuff, like annotations:
#Entity
#Table(name="MY_ENTITY_TABLE")
public class MyEntity extends MyAbstractEntity {
//This will not work since this field does not override the super class field, thus the setters and getters break.
#Column(name="PROPERTY")
protected String property;
}
Any ideas or will I have to create fields, getters and setters in the child classes?
Thanks,
Kris
You might want to annotate MyAbstractEntity with #MappedSuperclass class so that hibernate will import the configuration of MyAbstractEntity in the child and you won't have to override the field, just use the parent's. That annotation is the signal to hibernate that it has to examine the parent class too. Otherwise it assumes it can ignore it.
Here is an example with some explanations that may help.
#MappedSuperclass:
Is a convenience class
Is used to store shared state & behavior available to child classes
Is not persistable
Only child classes are persistable
#Inheritance specifies one of three mapping strategies:
Single-Table
Joined
Table per Class
#DiscriminatorColumn is used to define which column will be used to distinguish between child objects.
#DiscriminatorValue is used to specify a value that is used to distinguish a child object.
The following code results in the following:
You can see that the id field is in both tables, but is only specified in the AbstractEntityId #MappedSuperclass.
Also, the #DisciminatorColumn is shown as PARTY_TYPE in the Party table.
The #DiscriminatorValue is shown as Person as a record in the PARTY_TYPE column of the Party table.
Very importantly, the AbstractEntityId class does not get persisted at all.
I have not specified #Column annotations and instead are just relying on the default values.
If you added an Organisation entity that extended Party and if that was persisted next, then the Party table would have:
id = 2
PARTY_TYPE = "Organisation"
The Organisation table first entry would have:
id = 2
other attribute value associated specifically with organisations
#MappedSuperclass
#SequenceGenerator(name = "sequenceGenerator",
initialValue = 1, allocationSize = 1)
public class AbstractEntityId implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "sequenceGenerator")
protected Long id;
public AbstractEntityId() {}
public Long getId() {
return id;
}
}
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "PARTY_TYPE",
discriminatorType = DiscriminatorType.STRING)
public class Party extends AbstractEntityId {
public Party() {}
}
#Entity
#DiscriminatorValue("Person")
public class Person extends Party {
private String givenName;
private String familyName;
private String preferredName;
#Temporal(TemporalType.DATE)
private Date dateOfBirth;
private String gender;
public Person() {}
// getter & setters etc.
}
Hope this helps :)
Mark the superclass as
#MappedSuperclass
and remove the property from the child class.
Annotating your base class with #MappedSuperclass should do exactly what you want.
This is old, but I recently dealt with this and would like to share my solution. You can add annotations to an overridden getter.
#MappedSuperclass
public abstract class AbstractEntity<ID extends Serializable> implements Serializable {
#Column(name = "id", nullable = false, updatable = false)
#Id
private ID id;
public ID getId() {
return id;
}
...
}
#Entity
#Table(name = "address")
public final class Address extends AbstractEntity<UUID> implements Serializable {
...
#Override
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
public final UUID getId() {
return super.getId();
}
...
}

How to get inheritance entity by using joined sub entity in jpa?

Writing the case it will be more simple to explain.
I am using Seam 2.3.1 v Hibernate JPA 2.0 and in our project. I have a base Person Entity Class.
#Entity
#Name("person")
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "person")
public class Person extends BaseEntity {
private String name;
private String surName;
private String email;
private String phone;
// getter & setters
}
And I have 3 more Entity extends from Person as #Inheritance(strategy = InheritanceType.JOINED): Personel, Musteri, DisPaydas
#Entity
#Name("personel")
public class Personel extends Person {
private String appellation;
// getter & setters
}
I want to List personels, musteris and dispaydas in my bean however when I set them in my Group Entity, I want to save them as Person.
In fact in DB there is no difference between person_id and personel_id, they are same. However when I listing it, they are List<Personel>
In summary:
I want to get List<Person> from List<Personel>
or Person from Personel object.
You're going to have to define an #Id and #Column in the Person class for the person_id.
However, since the column has a different name in the child entity, you'll need to change it using an #AttributeOverride to point to the personel_id #Column.
It couldn't hurt to use a #DiscriminatorColumn in the parent and #DiscriminatorValue in the children, but I don't think Hibernate requires those.

Hibernate and #AttributeOverride

I have 3 classes:
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue
private Long id;
...
}
#Entity
public class Person extends BaseEntity {
private String name;
...
}
#Entity
#AttributeOverride(name = "id", #Column = (name = "idStudent"))
public class Student extends Person {
private float avgGrades;
...
}
I would like to override ID property so in Student table there would be a idStudent column instead of id. Unfortunately, the code above doesn't work. It looks like #AttributeOverride only works for the class you extending (and no one step further). Is there any way to override attribute name in situation I've descried (override attribute in class which exteds our class being extended) ?
Your problem is very easy to understand, if you know what the default inheritance type is: SINGLE_TABLE.
That means all entities that extending Person are in the same table. And thus Person already defines the ID column. Because you would otherwise violate the contract of the primary key column of your Person table.
I cite the JavaDoc of #AttributeOverride as well:
May be applied to an entity that extends a mapped superclass or to an embedded field or property to override a basic mapping or id mapping defined by the mapped superclass or embeddable class (or embeddable class of one of its attributes).
It always helps to read the JavaDoc first, before asking questions here.
What can you do about it? Make your Person a #MappedSuperclass (or create a BasePerson that is one).

Categories

Resources