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;
}
Related
I have one JPA mapped superclass (joined inheritance strategy) that haves also a composite id class:
#Entity
#IdClass(MyCompositeIdClass.class)
#Inheritance(strategy = InheritanceType.JOINED)
public class MySuperClass {
#Id
private Long myFirstId;
#Id
private Long mySecondId;
}
public class MyCompositeIdClass implements Serializable {
private static long serialVersionUID = 1L;
#Id
private Long myFirstId;
#Id
private Long mySecondId;
}
Therefore, I have an extension of this superclass and it's repository interface.
#Entity
#IdClass(MyCompositeIdClass.class)
public class MySubClass extends MySuperClass {
private String myAttr;
}
#Repository
public interface MySubClassRepository extends JpaRepository<MySubClass, MyCompositeIdClass> {
}
And when executing findAll() repository interface method, Hibernate is executing the join query with the wrong fields comparison on the join clause (inverting both PK columns):
select
mysuperc0_.my_first_id
mysuperc0_.my_second_id
mysuperc0_1_.my_attr
from
my_super_class mysuperc0_
inner join
my_sub_class mysuperc_0_1_
on
my_superc0_.my_first_id = mysuperc_0_1_.my_second_id
and
my_superc0_.my_second_id = mysuperc_0_1_.my_first_id
Was it configuration issues or a framework internal problem? Tried to research about inheritance issues with composite id, but got only opened issues ran from exceptions, but, in this case, I do not have exceptions, it just returns an empty array.
I'm trying to override a property of an embedded column existing in the superclass of an entity.
My entities look like this:
#Embeddable
public class Key {
#Column
private String a,
#Column
private String b
}
#MappedSuperclass
public abstract class Superclass {
#EmbeddedId
private Key key;
}
#Entity
#Table(name = "my_entity")
#AttributeOverride(name = "b", column = #Column(name="renamed_b"))
public class MyEntity extends Superclass {
}
I have tried using AttributeOverride on MyEntity, but it doesn't do anything.
It would work if I would move the AttributeOverride annotation on the embedded field, but I cannot modify the superclass.
Is there any solution?
Look, read documentation carefully:
To override mappings at multiple levels of embedding, a dot (".")
notation form must be used in the name element to indicate an
attribute within an embedded attribute.
The name "b" is incorrect.
You should use "key.b"
#Entity
#Table(name = "my_entity")
#AttributeOverride(name = "key.b", column = #Column(name="renamed_b"))
public class MyEntity extends Superclass
}
I have an #Entity with three fields A, B, C out of which A and B act as composite primary key. I created an #EmbeddedId class holding A and B. To ease the burden of defining getters and setters i used lombok #Data annotation.
#Entity
#Data
public class MyClass {
#EmbeddedId
private PrimaryKey id;
}
#Embeddable
#Data
public class PrimareyKey implements Serializable {
private String A;
private String B;
}
I would not like to expose that A and B are the primary key and access A and access all fields in the same way.
//Exposes primary key
myObject.getid().getA();
myObject.getid().getB();
myObject.getC();
//Hides primary key
myObject.getA();
myObject.getB();
myObject.getC();
Currently one could use #IdClass tagging each filed as #Id as suggested in this answer but if I still need to use #EmbeddedId (or any #Embedded actually) the only way (I know) to achieve this is to write ad hoc getters and setters bypassing the id variable such as
#Entity
#Data
public class MyClass {
#EmbeddedId
private PrimaryKey id;
public String A getA(){
return id.getA()
}
public String A setA(String a){
id.setA(a);
}
//same for B and any other fiels in PrimaryKey
}
This looks like a lot of boilerplate code to write and maintain.
Is there an annotation to expose #EmbeddedId getters and setters?
In MyClass, add Lombok #Delegate annotation to your PrimaryKey. It should look like:
#Entity
#Data
public class MyClass {
#Delegate
#EmbeddedId
private PrimaryKey id;
}
Then you can set/get PrimaryKey fields directly from MyClass. Here is a link for you to read more about it.
You can use the AccessLevel with #Getter and #Setter as follows:
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
private PrimaryKey id;
When using #Data, you have the public access to accessors by default, and using AccessLevel.NONE will overwrite the default behaviour and will not allow to access the accessors.
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 ...
}
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.