I have one question related to JPA relations.
Is it possible to define one relation to different classes?
Ej: Foo has a one-to-one relation (or many-to-one) with Boo or Too class (both can extend the same abstract class) but not a relation with both at the same time.
Thanks!
More details
It will be something like:
#Entity
class Foo {
#Id
int id;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="MOO_ID")
Moo moo;
}
Interface Moo{}
#Entity
class Too implements Moo{}
#Entity
class Boo implements Moo{}
And my database needs only one reference to the correct table Too or Boo.
If you made Moo an abstract class and specified an inheritance strategy you could achieve this.
i.e.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Moo {
#Id int id;
}
and then for Foo:
#Entity
class Foo {
#Id
int id;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="MOO_ID")
Moo moo;
}
And
#Entity
class Too extends Moo{}
#Entity
class Boo extends Moo{}
That way the Moo reference can be either Boo or Too.
You should also thenn choose whichever inheritance strategy works best for your scenario. From TABLE_PER_CLASS, SINGLE_TABLE and JOINED.
Related
I'm using JPA JOINED inheritance strategy with 3 abstract classes and 2 concrete.
Like this (code reduced for example purpose):
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
abstract class Employee {
#Id
private Long id;
private String firstName;
private String secondName;
}
#Entity
abstract class ProjectManager extends Employee {
#OneToMany(mappedBy="manager")
private List<Developer> developers;
}
#Entity
abstract class Developer extends Employee {
#ManyToOne
#JoinColumn(name="project_manager_id")
private ProjectManager manager;
}
#Entity
class JavaProjectManager extends ProjectManager {
private String someCustomProperty;
}
#Entity
class JavaDeveloper extends Developer {
private String skill;
}
This way, JPA creates 5 tables. But one of this tables, abstract ProjectManager it is empty. I mean, ProjectManager table only have ID column due to inheritance strategy.
My question is: how I can avoid this extra table for ProjectManager abstract class but keeping the same hierarchy of classes ?
I could not remove #Entity on ProjectManager class because I still need bidirectional relationship with Developer class.
Edit:
Also, I don't want move developers OneToMany from ProjectManager to JavaProjectManager, because I have more classes that extends from ProjectManager and need to have developers.
InheritanceType.TABLE_PER_CLASS does exactly what you want.
This type only creates/uses tables for non-abstract tables.
Read more about the various types here:
https://www.thoughts-on-java.org/complete-guide-inheritance-strategies-jpa-hibernate/
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 have a superclass that is an #Entity, something like this one:
#Entity
#Table(name = "utente")
public class Utente implements Serializable{
...
}
The subclass has just a #Transient field besides:
#Entity
public class UtenteSub extends Utente{
#Transient
private String newField;
}
To make it work, I should add #DiscriminatorValue, #Inheritance and add a field on the table.
This is a lot of work to do, taking into account that all I need in the subclass is just a #Transient field (I need it to "check" the object Utente after its "submission" in a form).
Is there a better and easier way to extend the #Entity in my scenario?
Thank you.
You could try creating an abstract base class UtenteBase:
#MappedSuperClass
public abstract class UtenteBase implements Serializable
{
//all mapped columns go here
}
All your mapped columns which were in Utente before are now in this class.
You can then extend this class with your two above mentioned classes:
#Entity
#Table(name = "utente")
public class Utente extends UtenteBase
{
public Utente {}
}
#Entity
#Table(name = "utente")
public class UtenteSub extends UtenteBase
{
#Transient
private String newField;
}
The class Utente is the concrete implementation class and is used for the communication with the database.
Both classes are in the same inheritance tree and you don't need to add a DiscriminatorValue and change the table.
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).
I have this strange hierarchy:
Class A is abstract (but it is an entity)
Class B (abstract) and C both extend class A (TABLE_PER_CLASS).
Class B is also extended by classes D and E but this time it is JOINED.
I want the ID of the A class to be the ID of all others.
Can this be done?
I get strange errors of IDs. can any one show me how to map?
This is how I did it:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class A {
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
protected Integer id;
}
Class C:
#Entity
#Table(name = "managers")
public class C extends A {
Class B:
#Entity
#Table(name = "bb")
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "discriminator", discriminatorType = DiscriminatorType.STRING)
public abstract class B extends A {
Class C,D:
#Entity
#Table(name = "cc"/"dd")
#DiscriminatorValue("CC"/"DD")
public class C (or D) extends B {
While mixing inheritance strategies in an inheritance tree can be sort of accomplished (https://stackoverflow.com/a/3916998/131929 refers to Java Persistence with Hibernate
-> 5.1.5 Mixing inheritance strategies (p207-p210)) I, and everybody else I know, advise against it.
Sidenote, I suggest you always use #MappedSuperclass rather than #Entity for abstract classes.