Unable to use inherited composite key as derived id in JPA2 - java

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.

Related

JPA With composite key non standard

I'm trying to do a JPA mapping for an existing database. I can't really change the existing structure.
I managed to make it works, but Intellij is telling me that some column doesn't exist even if it works. So I don't know if there's a better way to do this or if it's Intellij that doesn't support all the use cases.
I simplified my mapping and table for the question.
The 2 tables with primary composite keys are:
Table_A
some_id_a
some_seq_a
Table B
some_id_a
some_seq_a
some_seq_b
And my mapping is:
#Data
#Entity(name="Table_A")
public class TableA {
#EmbeddedId
private Key key;
#OneToMany
#JoinColumn(name = "someIdA")
#JoinColumn(name = "someSeqA")
private List<TableB> tableBs;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
}
}
#Data
#Entity(name="Table_B")
public class TableB {
#EmbeddedId
private Key key;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
private long someSeqB;
}
}
So like I said it works but I have an error in Intellij saying that the #JoinColumn(name ="someIdA") #JoinColumn(name = "someSeqA") don't exist and is expecting something like #JoinColumn(name ="some_id_a") #JoinColumn(name = "some_seq_a").
Using it the way Intellij is telling me, JPA has en error that says: Table [table_b] contains physical column name [some_id_a] referred to by multiple logical column names: [some_id_a], [someIdA].
My mapping is ok despite Intellij but is there's a better alternative ?
Thanks
You can use a "derived identity" and map your classes like this:
#Data
#Entity(name="Table_A")
public class TableA {
#EmbeddedId
private Key key;
#OneToMany(mappedBy = "tableA")
private List<TableB> tableBs;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
}
}
#Data
#Entity(name="Table_B")
public class TableB {
#EmbeddedId
private Key key;
#MapsId("tableAKey") // maps tableAKey attribute of embedded id
#JoinColumns({
#JoinColumn(name="some_id_a", referencedColumnName="some_id_a"),
#JoinColumn(name="some_seq_a", referencedColumnName="some_seq_a")
})
#ManyToOne
private TableA tableA;
#Data
#Embeddable
public static final class Key implements Serializable {
private TableA.Key tableAKey; // corresponds to PK type of TableA
private long someSeqB;
}
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

Hibernate could not determine type

I'm stuck with this error :
org.hibernate.MappingException: Could not determine type for: com.mywebsite.entity.Image, at table: User, for columns: [org.hibernate.mapping.Column(profilePhoto)]
I know it have been asked a lot but i'm stuck with this. It have been trying for hours and I can't find anything that work ...
Here are my 2 classes :
#Entity
public class Image extends com.mywebsite.entity.Entity{
//System
#Id
#GeneratedValue
private int id;
[...]
}
#Entity
public class User extends com.mywebsite.entity.Entity{
//System
#Id
#GeneratedValue
private int id;
[...]
//Data
private Image profilePhoto;
[...]
}
Can someone help me there ?
EDIT:
Try this:
#ManyToOne
#JoinColumn(name="profilephoto_id", foreignKey = #ForeignKey(name = "put_name_here_If_You_HaveAForeignKeyConstraint"))
private Image profilePhoto;
ORIGINAL:
From the docs:
Every non static non transient property (field or method depending on the access type) of an entity is considered persistent, unless you annotate it as #Transient.
So Hibernate thinks that field is from the database, and it's trying to find that column in your table. Either that column does not exist and it should, in which case you should have an annotation mapping for it like #Column; or it doesn't exist on the database and it shouldn't exist, in which case you should use #Transient like the documentation suggests.

unable to map entity with composite #IdClass

i have the following model (a very simplified version of a much bigger model)
public enum EntityType {
DOG, HAMMER
}
#Entity
public class Entity {
#EmbeddedId
private EntityKey key;
private String someProp;
}
#Embeddable
public class EntityKey implements Serializable{
private Long serial;
#Enumerated(EnumType.STRING)
private EntityType type;
}
#Entity
#IdClass(MetricKey.class)
public class Metric {
#Id
private EntityKey entityKey;
#Id
private Long timestamp;
private Double sampledValue;
}
public class MetricKey implements Serializable {
private EntityKey entityKey;
private Long timestamp;
}
Entity in reality is an abstract class at the root of a large hierarchy. Metric represents some data sampled on an entity at a particular point in time. Entity has a composite key consisting of type and serial number, and metric should have a composite key consisting of the entity key + timestamp.
the above code gets me this exception from hibernate:
org.hibernate.HibernateException: Unable to instantiate default tuplizer [org.hibernate.tuple.component.PojoComponentTuplizer]
...
Caused by: java.lang.reflect.InvocationTargetException
...
Caused by: org.hibernate.PropertyNotFoundException: field [serial] not found on Metric
at org.hibernate.property.DirectPropertyAccessor.getField(DirectPropertyAccessor.java:166)
... 45 more
it looks like it spotted the #Embeddable on EntityKey and is trying to break it apart here as well.
i know i can work around this by making MetricKey #Embeddable and rewriting some code:
#Entity
public class Metric {
#EmbeddedId
private MetricKey key;
private Double sampledValue;
}
#Embeddable
public class MetricKey implements Serializable{
private EntityKey entityKey;
private Long timeStamp;
}
but i'd much prefer the original design, since EntityKey is widely used across the system and having to convert back and forth between EntityKey and MetricKey would annoy developers.
what am i doing wrong?
The only working solution for I can propose you is changing the implementation of Metric to
#Entity
#IdClass(MetricKey.class)
public class Metric {
#Id
#ManyToOne
private Entity entity;
#Id
private Long timestamp;
private Double sampledValue;
}
You can also consult 2.4.1.1 Specification of Derived Identities of the JPA 2.1 spec.
If an Id attribute in the entity is a many-to-one or one-to-one relationship to a parent
entity, the corresponding attribute in the id class must be of the same Java type as the
id class or embedded id of the parent entity (if the parent entity has a composite primary
key) or the type of the Id attribute of the parent entity (if the parent entity has a
simple primary key).
My solution was tested against Hibernate 4.3.6

Use Objects as Primary keys and Composite keys in JPA

I want to know whetheris it a GOOD practise to use Objects as composite keys in JPA. For example I have few composite keys and the are foreign keys from another tables and mapped with them.
Composite Class
#Embeddable
public class CashInstrumentComposite implements Serializable {
private static final long serialVersionUID = -6065538857637001219L;
#Column(name = "instrument_id", nullable = false)
private String instrumentId;
#OneToOne
#JoinColumn(name = "company_id")
private Company companyId;
#Column(name = "instrument_type", nullable = true)
#Enumerated(EnumType.STRING)
private InstrumentType instrumentType;
#Column(name = "batch_no", nullable = false)
private String batchNumber;
}
Entity Class
#Entity
#Table(name = "bank_corporate_cash_instrument")
public class CashInstrument extends Model {
/* serial version id. */
private static final long serialVersionUID = 8360452197690274885L;
/* specify the composite key */
#EmbeddedId
private CashInstrumentComposite compositeId;
I can persist using this without any problem, BUT I read this,
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqa.html#bnbqf
It says there that primarykey should be one of
Java primitive types
Java primitive wrapper types
java.lang.String
java.util.Date (the temporal type should be DATE)
java.sql.Date
java.math.BigDecimal
java.math.BigInteger
So what did I do wrong here? Please explain.Thank You
I think that you have not interpreted what that piece of documentation says correctly:
The primary key, or the property or field of a composite primary key, must be one of the following Java language types:
#EmbeddedId from javadoc
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.
Example:
#EmbeddedId
protected EmployeePK empPK;
And the EmployeeKey:
#Embeddable
public class EmployeePK {
private Long key1;
private Long key2;
}
You can have a look at:
Oracle Doc
Ok, I did some sample apps I looks like I can use objects as composite keys. But I'm still confuse about whats written in this documentation
http://docs.oracle.com/javaee/6/tutorial/doc/bnbqa.html#bnbqf
But it's not a good practice to use composite keys because when u use one of the key in the composite key as a foreign key, then you have to duplicate the rest of the keys in that composite also. This is a waste specially if you have 4-5 keys as a composite.

#embeddable vs #entity for a mapping a collection

This must be quite naive but I have a doubt on when to use #Entity and #Embeddable.
Say I have a User and Notification class.
#Entity
public class User{
//other properties
#onetomany
private List<Notification> notifications;
}
#Entity
public class Notification{
//properties
}
I understand that there will be tables for class User and Notification, and a third table for mapping.
What if I do it like this?
#Entity
public class User {
//other properties
#ElementCollection
private List<Notification> notifications;
}
#Embeddable
public class Notification{
//properties
}
I know this won't create a table for Notification. But I can still store my notification objects. I went through the documentation, but couple of doubts:
Is it based on whether I want to see class B as a seperate table?
Is there a performance difference b/w creating a table and an embeddable object?
What can I not do with embeddable object that I can do with a table other than directly querying the table?
NOTES
For anyone reading this question, this question too might help you.
Is it based on whether I want to see class B as a separate table?
Yes, when you use #Embedded, You embed that #Embeddable entity in #Entity class, which makes it to add columns for embedded entity in same table of #Entity class.
Is there a performance difference b/w creating a table and an embeddable object?
When you use #Embedded, for table creation, one query is required, also for inserting and selecting a row. But if you don't use it, multiple queries are required, hence, use of #Embedded yields more performance, we can say.
What can I not do with embeddable object that I can do with a table other than directly querying the table?
Removing the respective embedded entity may be, but there may be integrity constraint violations for this.
In JPA, there’s a couple ways to create composite key fields. Lets see the method using the #Embeddable annotation.
Let’s start with the Entity class.
#Entity
#Table
public class TraceRecord {
#Id
private TraceRecordPk id;
#Version
#Transient
private int version;
#Column(columnDefinition = "char")
private String durationOfCall;
#Column(columnDefinition = "char")
private String digitsDialed;
#Column(columnDefinition = "char")
private String prefixCalled;
#Column(columnDefinition = "char")
private String areaCodeCalled;
#Column(columnDefinition = "char")
private String numberCalled;
}
This is a pretty simple Entity class with an #Id and #Version field and a few #Column definitions. Without going into too much detail, you’ll see that the #Version field is also annotated #Transient. I’ve done this simply because my table also doesn’t have a column for tracking versions, but my database is journaled, so I’m not too concerned about versioning. You’ll also notice that the #Column fields have a value of “char” set on the columnDefinition attribute. This is because the fields in my table are defined as char and not varchar. If they were varchar, I wouldn’t need to do this since a String maps to a varchar field by default.
The #Id field is what I’m interested in right now. It’s not a standard Java type, but a class I’ve defined myself. Here is that class.
#Embeddable
public class TraceRecordPk implements Serializable {
private static final long serialVersionUID = 1L;
#Temporal(TemporalType.DATE)
#Column
private Date dateOfCall;
#Column(columnDefinition="char")
private String timeOfCall;
#Column(columnDefinition="char")
private String callingParty;
/**
* Constructor that takes values for all 3 members.
*
* #param dateOfCall Date the call was made
* #param timeOfCall Time the call was made
* #param callingParty Extension from which the call originated
*/
public TraceRecordPk(Date dateOfCall, String timeOfCall, String callingParty) {
this.dateOfCall = dateOfCall;
this.timeOfCall = timeOfCall;
this.callingParty = callingParty;
}
}
To make this class capable of being an #Id field on an Entity class, it needs to be annotated with #Embeddable like I mentioned earlier. The 3 fields I’ve selected for my composite key are just normal #Column definitions. Rather than create getters/setters for each field, I’ve simply implemented a constructor that takes values for all 3 fields, making any instance immutable. When annotating a class with #Embeddable, that class will need to implement Serializable. So I’ve added a default serialVersionUID to accomodate.
Now that you have a class created and annotated with #Embeddable, you can now use it as the type for an #Id field in your Entity class. Simple stuff eh.

Categories

Resources