Use Objects as Primary keys and Composite keys in JPA - java

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.

Related

SQL JPA - Multiple columns as primary key

If i want severeal Column to make up an ID.
SQL example :
CONSTRAINT [PK_NAME] PRIMARY KEY ([Column1],[Column2],[Column3])
How can i do that with a Jpa Entity class ? through columndefinition ?
just setting the id field as:
value = Column1 + Column2 + Column3 // aint working.
You need to have a class for your composite key:
public class CompositeKey implements Serializable {
private int column1;
private int column2;
private int column3;
}
and then in your entity class use the #IdClass annotation:
#Entity
#IdClass(CompositeKey.class)
public class EntityExample {
#Id
private int column1;
#Id
private int column2;
#Id
private int column3;
...
...
}
I think this should work.
There is also the other solution that #jklee mentioned. Both work, it's a matter of preference.
Use #Embeddable and #EmbeddedId.
Example:
#Entity
public class Project implements Serializable {
#EmbeddedId ProjectId id;
}
#Embeddable
class ProjectId implements Serializable {
int departmentId;
long projectId;
}
More information here http://www.objectdb.com/java/jpa/entity/id#Embedded_Primary_Key_
If all fields in the class are part of primary key, then solution would be pretty simple (extending solution provided by #raul-cuth):
#Entity
#IdClass(EntityExample.class)
public class EntityExample implements Serializable {
#Id
private int column1;
#Id
private int column2;
#Id
private int column3;
}
Using #IdClass annotation on the #Entity class followed by #Id annotation on individual fields that are part of composite primary key.
Alternatively can make use of #Embeddable class which can consist of individual fields of the composite primary key and then a reference of this class can be used as an attribute with #Embedded annotation in #Entity class.
Hope this helps.
Be aware that the hibernate Entity-Class-to-SQL-DDL-Script generator will sort all the fields, and irrespective of the order in which it appears in the definitions, will create the table definition and the index / constraint definitions in this sorted order of the fields.
While the order of appearance of the fields in the table definition may not matter much, the order of fields in a composite index definitely do. So your key-fields must be named so that when sorted by their names they are in the order you desire for the index).

Hibernate JPA, one to one relationship with a composite key and a constant value

I'm attempting to implement a limited type of object level ACL and its lead me to a place where I'm attempting to create a #OneToOne relationship using a composite key with a constant and dynamic value.
I have an Entity with a database id and a constant value defined in the class.
public class Entity{
private static final int objectType = 1;
#Id
Integer id;
}
I have an access_levels table with a composite key of objectId and objectType.
public class AccessLevel {
#EmbeddedId
private AccessLevelKey accessLevelKey;
#Embeddable
class AccessLevelKey implements Serializable{
private Integer objectType;
private Integer objectId;
....
}
}
Schema of access_levels
CREATE TABLE access_levels(
object_type INTEGER NOT NULL,
object_id INTEGER NOT NULL,
....
CONSTRAINT access_levels_type_id PRIMARY KEY (object_type, object_id)
);
I'm attempting to come up with a one to one relationship that Entity can use to fetch and update its associated AccessLevel
After taking a look a the docs on Non-Standard Joins it seems like I need something like this,
Inside of Entity:
#OneToOne
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "object_id"),
#JoinColumn(name = "access_levels.object_type", referencedColumnName = "1"),
})
private AccessLevel accessLevel;
However this throws a hibernate MappingException at app launch
Caused by: org.hibernate.MappingException: Unable to find column with logical name: 1 in access_levels
Thanks!

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.

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

How to define unidirectional OneToMany relationship in JPA

I have a following problem with entity mapping in JPA. I have two entities, first one is Lookup and the second is Text which represents translations for entities. Now I need to bound Lookup to the Text but I don't want Text to have reference to Lookup. To make this more complicated, Text does not use its primary key in this relationship but a metacode defined in a TXTHEAD_CODE column.
Lookup.java
#Entity
#Table(name = "DATREG")
public class Lookup implements PersistableEntity {
#Id
#Column(name = "DATREG_META_CODE")
private String metaCode;
#OneToMany
#JoinTable(name="TXT",
joinColumns=#JoinColumn(name="DATREG_META_CODE", referencedColumnName="TXTHEAD_CODE"),
inverseJoinColumns=#JoinColumn(name="DATREG_META_CODE"))
private List<Text> text;
Text.java
#Entity
#Table(name = "TXT")
public class Text {
#Id
#Column(name = "TXT_ID")
private Long id;
#Column(name = "TXTHEAD_CODE")
private String code;
So I have tried this (and few other variations) but with no result. I also can't create join table in the DB and I don't want bound Lookup to my Text class. So can anyone please tell me if there is some other way?
My bible for JPA work is the Java Persistence wikibook. It has a section on unidirectional OneToMany which explains how to do this with a #JoinColumn annotation. In your case, i think you would want:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE")
private Set<Text> text;
I've used a Set rather than a List, because the data itself is not ordered.
The above is using a defaulted referencedColumnName, unlike the example in the wikibook. If that doesn't work, try an explicit one:
#OneToMany
#JoinColumn(name="TXTHEAD_CODE", referencedColumnName="DATREG_META_CODE")
private Set<Text> text;

Categories

Resources