JPA: difference between #JoinColumn and #PrimaryKeyJoinColumn? - java

What's the exact difference between #JoinColumn and #PrimaryKeyJoinColumn?
You use #JoinColumn for columns that are part of a foreign key. A typical column could look like (e.g. in a join table with additional attributes):
#ManyToOne
#JoinColumn(name = "...")
private OtherClass oc;
What happens if I promote the column to be a/the PK, too (a.k.a. identifying relationship)? As the column is now the PK, I must tag it with #Id:
#Id
#ManyToOne
#JoinColumn(name = "...")
private OtherClass oc;
Now the question is:
Are #Id + #JoinColumn the same as just #PrimaryKeyJoinColumn?:
#ManyToOne
#PrimaryKeyJoinColumn(name = "...")
private OtherClass oc;
If not, what's #PrimaryKeyJoinColumn there for?

What happens if I promote the column to be a/the PK, too (a.k.a. identifying relationship)? As the column is now the PK, I must tag it with #Id (...).
This enhanced support of derived identifiers is actually part of the new stuff in JPA 2.0 (see the section 2.4.1 Primary Keys Corresponding to Derived Identities in the JPA 2.0 specification), JPA 1.0 doesn't allow Id on a OneToOne or ManyToOne. With JPA 1.0, you'd have to use PrimaryKeyJoinColumn and also define a Basic Id mapping for the foreign key column.
Now the question is: are #Id + #JoinColumn the same as just #PrimaryKeyJoinColumn?
You can obtain a similar result but using an Id on OneToOne or ManyToOne is much simpler and is the preferred way to map derived identifiers with JPA 2.0. PrimaryKeyJoinColumn might still be used in a JOINED inheritance strategy. Below the relevant section from the JPA 2.0 specification:
11.1.40 PrimaryKeyJoinColumn Annotation
The PrimaryKeyJoinColumn annotation
specifies a primary key column that is
used as a foreign key to join to
another table.
The PrimaryKeyJoinColumn annotation
is used to join the primary table of
an entity subclass in the JOINED
mapping strategy to the primary table
of its superclass; it is used within a
SecondaryTable annotation to join a
secondary table to a primary table;
and it may be used in a OneToOne
mapping in which the primary key of
the referencing entity is used as a
foreign key to the referenced
entity[108].
...
If no PrimaryKeyJoinColumn
annotation is specified for a subclass
in the JOINED mapping strategy, the
foreign key columns are assumed to
have the same names as the primary key
columns of the primary table of the
superclass.
...
Example: Customer and ValuedCustomer subclass
#Entity
#Table(name="CUST")
#Inheritance(strategy=JOINED)
#DiscriminatorValue("CUST")
public class Customer { ... }
#Entity
#Table(name="VCUST")
#DiscriminatorValue("VCUST")
#PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer { ... }
[108] The derived id mechanisms
described in section 2.4.1.1 are now
to be preferred over
PrimaryKeyJoinColumn for the
OneToOne mapping case.
See also
Primary Keys through OneToOne Relationships
This source http://weblogs.java.net/blog/felipegaucho/archive/2009/10/24/jpa-join-table-additional-state states that using #ManyToOne and #Id works with JPA 1.x. Who's correct now?
The author is using a pre release JPA 2.0 compliant version of EclipseLink (version 2.0.0-M7 at the time of the article) to write an article about JPA 1.0(!). This article is misleading, the author is using something that is NOT part of JPA 1.0.
For the record, support of Id on OneToOne and ManyToOne has been added in EclipseLink 1.1 (see this message from James Sutherland, EclipseLink comitter and main contributor of the Java Persistence wiki book). But let me insist, this is NOT part of JPA 1.0.

I normally differentiate these two via this diagram:
Use PrimaryKeyJoinColumn
Use JoinColumn

I know this is an old post, but a good time to use PrimaryKeyColumn would be if you wanted a unidirectional relationship or had multiple tables all sharing the same id.
In general this is a bad idea and it would be better to use foreign key relationships with JoinColumn.
Having said that, if you are working on an older database that used a system like this then that would be a good time to use it.

You use #JoinColumn when you want to manage (change the column name, set nullable and so on) the foreign key column in the target entity table. Here, the Address table will contains User table id like foreign key but the column it's will be name user_id (the second scheme of #Sam YC)
#Entity
public class Address implements Serializable {
#Id
#GeneratedValue
private String id;
private String city;
#OneToOne(optional = false)
#JoinColumn(name = "user_id", updatable = false)
private User user;
}
You use #PrimaryKeyJoinColumn, when you want to use the primary key of the referencing entity like the target entity primary key. Here the Address know the referencing User but Address table hasn't foreign key column, because it's has the same id than User Id (the first scheme of #Sam YC)
#Entity
public class Address implements Serializable {
#Id
#GeneratedValue(generator = "foreignKeyGenerator")
#GenericGenerator(
name = "foreignKeyGenerator",
strategy = "foreign",
parameters = #org.hibernate.annotations.Parameter(
name = "property", value = "userT"
)
)
private String id;
private String city;
#OneToOne(optional = false)
#PrimaryKeyJoinColumn
private User userT;
}

Related

What is the difference between #PrimaryKeyColumn and #PrimaryKey in Spring Data Cassandra?

I have recently started using Cassandra for my Spring Boot applications. I have always just used #PrimaryKeyColumn and #Id annotation to mark the Primary Key in the Java Class but yesterday I saw a github repos where people were using #PrimaryKeyColumn annotations with #Id annotation and some other repos where people are just using #PrimaryKey annotations.
What am I missing here, I feel so lost.
A good example for this can be found in the Git repo for DataStax's E-commerce workshop.
Consider this table/PK definition:
CREATE TABLE user (
user_id UUID,
...
PRIMARY KEY (user_id));
As there is a single column defined as the primary key, the UserEntity class only needs to use the #PrimaryKey annotation:
#PrimaryKey("user_id")
private UUID userId;
Now consider this table/PK definition:
CREATE TABLE cart_products (
cart_id uuid,
product_timestamp timestamp,
product_id text,
...
PRIMARY KEY (cart_id, product_timestamp, product_id)
) WITH CLUSTERING ORDER BY (product_timestamp DESC, product_id ASC)
AND default_time_to_live = 5184000;
cart_products uses three columns for its PRIMARY KEY: cart_id as the partition key; product_timestamp and product_id for the clustering keys. So the CartProductEntity class still uses the #PrimaryKey annotation, but it references the CartProductsPrimaryKey class:
#PrimaryKey
private CartProductsPrimaryKey key;
Inside that class, the individual primary key columns are annotated with #PrimaryKeyColumn:
#PrimaryKeyClass
public class CartProductsPrimaryKey {
#PrimaryKeyColumn(
name = "cart_id",
ordinal = 0,
type = PrimaryKeyType.PARTITIONED)
private UUID cartId;
#PrimaryKeyColumn(
name = "product_timestamp",
ordinal = 1,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.DESCENDING)
private Date productTimestamp;
#PrimaryKeyColumn(
name = "product_id",
ordinal = 2,
type = PrimaryKeyType.CLUSTERED,
ordering = Ordering.ASCENDING)
private String productId;
Basically, the annotations used depend largely on the complexity of the primary key definition.
As for the #Id annotation, it serves the same function as #PrimaryKey. This is mentioned in the Spring Data Cassandra repository.
The #Id annotation tells the mapper which property you want to use for the Cassandra primary key. Composite primary keys can require a slightly different data model.
https://github.com/spring-projects/spring-data-cassandra/blob/main/src/main/asciidoc/reference/mapping.adoc#metadata-based-mapping
#Id: Applied at the field or property level to mark the property used for identity purposes.
#PrimaryKey: Similar to #Id but lets you specify the column name.
#PrimaryKeyColumn: Cassandra-specific annotation for primary key columns that lets you specify primary key column attributes, such as for clustered or partitioned. Can be used on single and multiple attributes to indicate either a single or a composite (compound) primary key. If used on a property within the entity, make sure to apply the #Id annotation as well.
#PrimaryKeyClass: Applied at the class level to indicate that this class is a compound primary key class. Must be referenced with

Why we use #ForeignKey(name="FK_COUNTRY") annotation?

I have been going through some relational stuff in hibernate where I get this solution for relation between tables I tried this it works fine but when I remove #ForeignKey(name="FK_COUNTRY") nothing change then why are we using this annotation is that came under best practice?
#Entity
#Table(name = "state")
public class State {
#Id
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#ForeignKey(name="FK_COUNTRY")
private Country country;
}
Hibernate should reflect DB structure,
You should read about the importance of foreign key
Referential Integrity
Easier Detective Work
Better performance
If you read the javadoc of #ForeignKey, you'll find:
Used to specify the handling of foreign key constraints when schema generation is in effect. If this annotation is not specified, the persistence provider's default foreign key strategy will be used.
If you don't generate the database schema from the class definitions (e.g. CREATE TABLE SQL statement), then the annotation has no effect.

foreign key constraint in One To One relationship in JPA hibernate

I have two entities A and B with one-to-one relationship. When I want to insert them into DB, I get the following error:
Cannot add or update a child row: a foreign key constraint fails
(mydb.a, CONSTRAINT
FK_77pkrkrin5nqsx16b6nw6k9r7 FOREIGN KEY (id) REFERENCES
b (b_id))
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true, value={"hibernateLazyInitializer", "handler"})
#Generated("org.jsonschema2pojo")
#Inheritance(strategy = InheritanceType.JOINED)
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonIgnore
private Integer id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn(referencedColumnName="AId")
#JsonIgnore
private B b;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true, value = {"hibernateLazyInitializer", "handler"})
#Generated("org.jsonschema2pojo")
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int bId;
#OneToOne()
#JsonIgnore
private A a;
}
The insert operation will work perfectly if I remove the optional=false. I checked the objects before inserting them into DB and I make sure that A contains B and B contains A.
The SQL scripts for A and B creation are:
Hibernate: create table b (b_id integer not null auto_increment,
string_results longtext, a_id integer, primary key (b_id))
Hibernate: create table a (id integer not null auto_increment, primary
key (id))
Hibernate: alter table b add constraint FK_o3oen721etlltdc7ls82524nh
foreign key (detail_id) references a (id)
Hibernate: alter table a add constraint FK_77pkrkrin5nqsx16b6nw6k9r7
foreign key (id) references b (b_id)
When I see the following sentence:
I checked the objects before inserting them into DB and I make sure that A contains B and B contains A.
I guess that you want to create a bidirectional one-to-one relationship. If so, your current mapping does not work as expected. Let us see what the JPA 2.0 spec(download link) states about this to understand the matter:
Relationships may be bidirectional or unidirectional. A bidirectional relationship has both an owning side and an inverse (non-owning) side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.4.
The following rules apply to bidirectional relationships:
• The inverse side of a bidirectional relationship must refer to its owning side by use of the mappedBy element of the OneToOne, OneToMany, or ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.
•The many side of one-to-many / many-to-one bidirectional relationships must be the owning side, hence the mappedBy element cannot be specified on the ManyToOne annotation.
• For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.
• For many-to-many bidirectional relationships either side may be the owning side.
So, according to the specification in a bidirectional one-to-one relationship one of the entities must be made the owning side and the other one the inverse side. Let assume entity A is the owning side the following mapping should work:
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JoinColumn(name="b_id", referencedColumnName="ID")
private B b;
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(mappedBy = "b")
private A a;
}
In order to make the above mapping work either the physical tables must be generated automatically or if you want to create the tables yourself, the corresponding SQL should look the following:
create table a (id integer not null auto_increment,
b_id integer not null,
primary key (id),
foreign key b_id references b (id));
create table b (id integer not null auto_increment,
string_results longtext,
primary key (id));
NOTE:
I removed the JSON-specific annotation to make the code shorter (I don't have any knowledge about them)
If you want to make entity B the owning side, you have to adjust the relationship mapping accordingly.
The #JoinColumn annotation is always on the owning side.
Due to lack of time I haven't tested the code. If you find any bug (especially the MySQL syntax) just leave me a comment.
From the hibernate docs:
(Optional) Whether the association is optional. If set to false then a non-null relationship must always exist.
Right at the time of creation of B, the b_id is not available, it is created when the flush happens to database.
Since A has a foreign key relation on B(b_id), B needs to be created along with its id before you can insert A or mark this foreign relation as optional.
Create B and flush it to database, then create A. B has A is not required as its A who defines a foreign key to B and not vice-versa, B's reference to A is only reflexive, two-way foreign key will create cyclic reference issue.

When Should I Use #JoinColumn or #JoinTable with JPA?

#JoinColumn gives an Entity a foreign key to another Entity whereas #JoinTable will list the relationship between all relationships between Entity A and Entity B. As far as I can tell, they both appear to do similar things. When should I use one or the other?
Let's say you have an entity A which has a #ManyToOne association ot an entity B
#JoinColumn will define the target table Foreign Key (e.g B_ID) while using the target Entity table (e.g. B).
#Entity
public class A {
private Long id;
#ManyToOne
#JoinColumn(name="B_ID")
private B b;
}
#JoinTable will use a separate table to hold the relationship between A and B.
#Entity
public class A {
private Long id;
#ManyToOne
#JoinTable(
name = "A_B",
joinColumns = #JoinColumn(name = "B_ID"),
inverseJoinColumns = #JoinColumn(name = "A_ID")
)
private B b;
}
This time neither A nor B contain any Foreign Key, because there's a separate table (e.g. A_B) to hold the association between A and B.
#JoinTable stores the id of both the table into a separate table while #JoinColumn stores id of the another table in a new column.
#JoinTable : This is the default type. Use this when you you need a more normalized database. ie. to reduce redundancy.
#JoinColumn : Use this for better performance as it does not need to join extra table.
one important difference: #JoinColumn always depends upon the context it is used:
If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the
source entity or embeddable.
If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of
the target entity.
If the join is for a ManyToMany mapping or for a OneToOne or bidirectional ManyToOne/OneToMany mapping using a join table, the
foreign key is in a join table.
If the join is for an element collection, the foreign key is in a collection table.

Hibernate embeddable list mapping with identifier

I have a Person entity with an embeddable Address and there's a one-to-many relation between them (a person can have multiple addresses). The current mapping is something like this:
#Embeddable
public class Address {
// ... attributes
}
#Entity
public class Person {
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid")
)
/*
Attribute ovverrides with annotations
*/
private java.util.Set<Address> addresses = new java.util.HashSet<Address>();
}
Using this annotation means that in the database I have a Person_addresses table which contains all the address attributes and a personid. But it also means that if I have a person with an address list and I update the address list, Hibernate deletes all the related records and inserts them (the modified ones) again.
As far as I know there's a way to have a primary key in this table for each record - in this case hibernate can decide which item of the list needs to be updated. So my question is, how can I map an embeddable list with identifiers in the joining table? (I hope it's understandable what I want:)).
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Primary_keys_in_CollectionTable
The JPA 2.0 specification does not provide a way to define the Id in
the Embeddable. However, to delete or update a element of the
ElementCollection mapping, some unique key is normally required.
Otherwise, on every update the JPA provider would need to delete
everything from the CollectionTable for the Entity, and then insert
the values back. So, the JPA provider will most likely assume that the
combination of all of the fields in the Embeddable are unique, in
combination with the foreign key (JoinColumn(s)). This however could
be inefficient, or just not feasible if the Embeddable is big, or
complex. Some JPA providers may allow the Id to be specified in the
Embeddable, to resolve this issue. Note in this case the Id only needs
to be unique for the collection, not the table, as the foreign key is
included. Some may also allow the unique option on the CollectionTable
to be used for this. Otherwise, if your Embeddable is complex, you may
consider making it an Entity and use a OneToMany instead.
So thats it, it can't be done.
As maestro's reply implies, the only portable solution is to convert this to use an entity and a one-to-many.
That said, Hibernate has a non-spec feature called an "id bag" which allows you to map a basic or embeddable collection with an identifier for each row, thereby giving you the efficient updates you want:
#Entity
public class Person {
#CollectionId( columns={"address_id"}, type="int", generator="increment" )
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid"))
private java.util.List<Address> addresses = new java.util.ArrayList<Address>();
}
Notice the switch from Set to List however. Also notice the generated table structure... looks an awful lot like an entity ;)

Categories