SQL JPA - Multiple columns as primary key - java

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

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.

Primary Key Column Name with Spring Data JPA and Hibernate

I have the following setup with Spring Data JPA and Hibernate as the persistence provider. All of my entities inherit from a base class
#MappedSuperclass
public class BaseEntity {
#Id
private id;
#Version
private String version;
//more common fields
}
For example:
#Entity
public class Foo extends BaseEntity {
}
This leads to a primary key column with name "ID" to be generated on the "FOO" table. I would like to change the naming of the primary key column. It should reflect the name of class or table. So it should be "FOO_ID" instead of just "ID".
I know that I could do this statically by using #Column(name = "FOO_ID"). But that would mean I have to do this for every Entity. Is there a more dynamic way to achieve this?
I know this is an old question, but stumbled across this looking for an answer... Eventually found this solution elsewhere:
#Entity
#AttributeOverride(name="id", column=#Column(name="FOO_ID"))
public class Foo extends BaseEntity {
}
All your subClasses will have the same ID column name because of the inheritance, you can specify a common id colum name for all subClasses in the Base entity Class
Why use inheritance then? Just do it without inheritance.
You could use getters/setters to rename your fields
Ex:
class Foo {
private Long id;
public Long getFooId() {
return this.id;
}
public void setFooId(Long fooId) {
this.id = fooId;
}
}

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.

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.

Hibernate composite key which are foreigen key to another table

I have two table Part and SubPart. Part table has general fields like id, name, desc etc. The SubPart table has part_id, sub_part_id as composite key. Both of these columns are referring to Part table and has a one to many mapping for each of them, like for each part_id in the Part table there can be multiple entries in SubPart table for both the columns. I'm having problem defining the composite key for the SubPart table. I tried the Embedded tag but its not working. How can I address this problem. Thanks a lot.
Part table like this.
#Entity
#Table(name="Part")
public class Part {
#Id
#GeneratedValue
#Column(name="Part_Id")
private int id;
#Column(name="Part_Number")
private String partNumber;
#Column(name="Part_Name")
private String partName;
}
Sub Part Table
#Entity
#Table(name="SubPart")
public class SubPart {
// part and subPart combination is the compound key here.
#ManyToOne
#JoinColumn(name="Part_Id")
private Part part;
#ManyToOne
#JoinColumn(name="Sub_Part_Id")
private Part subPart;
#Column(name="Quantity")
private Integer quantity;
}
You said
I'm having problem defining the composite key for the SubPart table
When you have a compound primary key, you must define a class (Usually a static inner class) which defines your compound primery key (Just an advice: because Hibernate makes use of proxies, prefer to put your annotated mapping on the getter's instead of field members)
/**
* When both entity class and target table SHARE the same name
* You do not need #Table annotation
*/
#Entity
public class SubPart implements Serializable {
#EmbeddedId
private SubPartId subPartId;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="PART_ID", insertable=false, updateable=false)
private Part part;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="SUP_PART_ID", insertable=false, updateable=false)
private SubPart subPart;
/**
* required no-arg constructor
*/
public SubPart() {}
public SubPart(SubPartId subPartId) {
this.subPartId = subPartId;
}
// getter's and setter's
/**
* It MUST implements Serializable
* It MUST overrides equals and hashCode method
* It MUST has a no-arg constructor
*
* Hibernate/JPA 1.0 does not support automatic generation of compound primary key
* You SHOULD set up manually
*/
#Embeddable
public static class SubPartId implements Serializable {
#Column(name="PART_ID", updateable=false, nullable=false)
private Integer partId;
#Column(name="SUB_PART_ID", updateable=false, nullable=false)
private Integer subPartId;
/**
* required no-arg constructor
*/
public SubPartId() {}
public SubPartId(Integer partId, Integer subPartId) {
this.partId = partId;
this.subPartId = subPartId;
}
// getter's and setter's
#Override
public boolean equals(Object o) {
if(!(o instanceof SubPartId))
return null;
final SubPartId other = (SubPartId) o;
return new EqualsBuilder().append(getPartId(), other.getPartId())
.append(getSubPartId(), other.getSubPartId())
.isEquals();
}
#Override
public int hashCode() {
return new HashCodeBuilder().append(getPartId())
.append(getSubPartId())
.toHashCode();
}
}
}
Notice Part and SubPart mapping has been marked as insertable=false, updateable=false Because the mapping has been defined in the compound primary key. Hibernate does not allow you mapping two properties WITH THE SAME COLUMN unless you mark insertable=false, updateable=false. Otherwise you will see this nice exception
Should be marked with insertable=false, updateable=false
I think I would declare a field of type Map<Part,SubPart> in class Part and declare it as #OneToMany.
Actually, in this particular case, it could event be a Map<Part,Integer> because the only other field is the quantity. The SubPart class is not needed.

Categories

Resources