Can't put #entity annotation in auditable class - java

This is my class:
#Data
#Entity
#Table(name="usercontext")
public class UserContext extends AbstractAuditable<UserDO, Long> {
#Column(name="name") private String name;
#Column(name="value") private String value;
...
}
I am using lombok (hence #Data annotation). Eclipse editor complains that "The entity has no primary key attribute defined" although the code compiles properly and runs. This same error is shown in JPA problems in markers list in eclipse. AbstractAuditable extends AbstractPersistable which already has a primary key called "id" defined. Is there any way to remove these errors?

AbstractAuditable needs to be annotated with #MappedSuperclass in order for id to be visible to subclasses, and that id should be annotated with #Id.

Related

spring-data-jdbc error Required identifier property not found for class

Getting below error while loading data from database
java.lang.IllegalStateException: Required identifier property not found for class com.sudhirt.practice.springdatajdbcpractice.entity.AuthorRef!
at org.springframework.data.mapping.PersistentEntity.getRequiredIdProperty(PersistentEntity.java:105)
at org.springframework.data.jdbc.core.EntityRowMapper.readEntityFrom(EntityRowMapper.java:143)
at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:124)
at org.springframework.data.jdbc.core.EntityRowMapper.lambda$createInstance$0(EntityRowMapper.java:167)
Below is the entity class AuthorRef
#Data
#Table("BOOK_AUTHOR")
#NoArgsConstructor
#AllArgsConstructor
public class AuthorRef {
private Long author;
}
What might be the reason for above error?
Source code is available at https://github.com/sudhirtumati/spring-data-jdbc-sample
You are referencing AuthorRef in a Set inside your aggregate root Book.
public class Book {
#Id
private Long id;
private String name;
// ...
private Set<AuthorRef> authorRefList;
// ...
}
Without an id column Spring Data can't determine a primary key for AuthorRef.
Just adding an #Id annotation to author should be sufficient.
Alternatively you could use a List which will add an additional book_key column which together with the book column form a primary key.

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

JPA inheritance: Mapped Superclass vs Table Per Class

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

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).

Unable to use inherited composite key as derived id in JPA2

Am I doing something wrong or is this not supported in JPA2/eclipselink, let me explain by code;
#Embeddable
public class MemberID implements Serializable {
private String e_mail;
private String password;
//...no-arg constructor, getter and setter
the entity below uses MemberID as composite key
#Entity
#Table(name="MEMBER_DETAILS")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="GROUPNAME", discriminatorType=DiscriminatorType.STRING, length=20)
public class Member_Details implements Serializable {
#EmbeddedId
private MemberID memberIdentity;
...other code
the entity below extends Member_Details and therefore inherits its key
#Entity
#Table(name="INDIVIDUAL_USER")
#DiscriminatorValue("INDIVIDUAL_USER")
public class Individual_User extends Member_Details implements Serializable {
#OneToMany(mappedBy="userinfo", fetch=FetchType.EAGER)
private List<UserComment> userComments = new ArrayList<UserComment>();
... other code
the following is a composite key that contains MemberID as part of it.
#Embeddable
public class CommentID implements Serializable {
private MemberID memberId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="TIME_POSTED")
private Date timeOfComment;
...other code
the entity below uses CommentID as its composite key. I want it to be dependent on the entity Individual_User, and therefore use a derived id.That is why MemberID is part of its composite key.
#Entity
#Table(name="USER_COMMENTS")
public class UserComment implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private CommentID commentIdentity;
#MapsId("memberId")
#ManyToOne
#JoinColumns({
#JoinColumn(name="E_MAIL", referencedColumnName="E_MAIL"),
#JoinColumn(name="PASSWORD", referencedColumnName="PASSWORD")
})
private Individual_User userinfo;
...other code
The problem comes when I try to deploy, the following exception is thrown:
Caused by: Exception [EclipseLink-7321] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.ValidationException
Exception Description: The field [MEMBER_DETAILS.PASSWORD] from the derived id mapping [userinfo] from class [kariro.semaplace.talk.entities.UserComment] is an invalid id field from the reference class [kariro.semaplace.registration.entity.Individual_User]. Ensure there is a corresponding id mapping to that field.
But when I change the #ManyToOne relationship from UserComment to reffer to type Member_Details instead of its subtype Individual_User, it works with no problems, but I am afraid this will bring in more problems later, or compromise the functioning of the app.
I really don't know whether eclipselink does not allow inherited ids to be used as derived ids or am doing something wrong. someone please help me out.
I'm not sure why you are getting the error, but you could avoid the issue entirely by simplifying your entities.
The first thing that jumps out to me is the fact that your user's PK includes password. Passwords are generally changeable, primary keys are not. Also, would you ever expect to have two distinct users with the same email but different passwords? Probably not. Drop MemberID and change Member_Details to have a simple #Id of just e_mail instead:
#Entity
#Table(name="MEMBER_DETAILS")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="GROUPNAME", discriminatorType=DiscriminatorType.STRING, length=20)
public class Member_Details implements Serializable
{
#Id
private String e_mail;
private String password;
// ...
}
CommentID would change also:
#Embeddable
public class CommentID implements Serializable
{
private String e_mail;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="TIME_POSTED")
private Date timeOfComment;
}
And the #MapsId on UserComment would change to #MapsId("e_mail").
The changes above should be enough to avoid your issue, but if it were me, I would get rid of the composite primary key on UserComment as well. To simply things, you could give it a UUID and then put a unique constraint on e_mail and timeOfComment.

Categories

Resources