How can i model in hibernate o table which the primary key is also the foreign key
as this schema :
CREATE TABLE policies (
policy_id int,
date_issued datetime,
-- // other common attributes ...
);
CREATE TABLE policy_motor (
policy_id int,
vehicle_reg_no varchar(20),
-- // other attributes specific to motor insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
CREATE TABLE policy_property (
policy_id int,
property_address varchar(20),
-- // other attributes specific to property insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
Is this possible in hibernate mapping or the best is to separate
them and use a new PK ?
and also it's okey if i use auto increment on the main table
Sure you can. This is useful for one-to-one associations where you can have:
#Id
#Column(name="policy_id")
private policyId;
#MapsId
#OneToOne
#JoinColumn(name = "policy_id")
private Policy policy;
You can use AUTO-INCREMENT on the main table, which means you need to use an IDENTITY generator in JPA/Hibernate.
The #MapsId allows you to share the same SQL column with the entity identifier property. It instructs Hibernate to use this column for resolving the #OneToOne or #ManyToOne association. Changes should always be propagated through the #Id property, rather than the to-one side (which is ignored during inserts/updates).
Related
I want to create an association between two entities Parent and Child with the constraint that Child ID correspond to a non unique property of Parent. Is it possible with Hibernate or shall I cope this without Hibernate ?
Here is Parent entity
public class Parent {
#column("parent_id")
private Integer parent_id
#column("identifiant")
private Integer identifiant
#column("version")
private Integer version
With a UNIQUE constraint on (identifiant, version)
and here is the Child Entity
public class Child{
#column("identifiant")
private Integer identifiant
#column("document1")
private byte[] document1
#column("document2")
private byte[] document2
with identifiant as primary key and also as foreign key on (Parent, identifiant)
Is it possible to manage this association with Hibernate and if so how, or shall I manage this association without hibernate and manage the Child Entity as a independant Entity ?
UPDATE
I found another issue. When I add a constraint of foreign key on (PARENT_TABLE, identifiant) like the following :
ALTER table PARENT_TABLE ADD CONSTRAINT cts_1 FOREIGN KEY (identifiant) REFERENCES CHILD_TABLE(identifiant);
it complaints that there are non existent row in CHILD_TABLE
I am new to Hibernate. I have a OneToMany relationship with bidirectional mapping between Account and Transaction. I am not using #JoinColumn in either class and using #mappedBy in the non owning Account class. And everything is working fine. Using H2 in memory database, new join column is created in Transaction table. Then what is the use of #JoinColumn in OneToMany relationships? Is it for unidirectional mapping only? Below is the code. I also read for reference JPA JoinColumn vs mappedBy
public class Account {
#OneToMany( mappedBy="account", cascade=CascadeType.ALL)
List<Transaction> list= new ArrayList<Transaction>();
}
public class Transaction {
#ManyToOne
Account account;
}
Application class :
Account a = new Account("savings");
Transaction t1 = new Transaction("shoe purchase", 45);
t1.setAccount(a);
a.getList().add(t1);
accountRepository.save(a);
output:
Transaction table has an entry with foreign key which is account number in that one row in Account table. ACCOUNT_ID column in created in Transaction table.
There are no extra tables created.
Jpa works on the idea of configuration by convention. So, it will perform configuration on your behalf whenever it can. Think of the #Column annotation, you don't have to apply it on every entity attribute, you would need it only when you have to change something about the attributes.
It's the same with #JoinColumn, when you added #ManyToOne, Jpa already knows that you will need the join column and thus was added for you and the default naming convention for the foreign key was applied (attributename_primarykeyoftheothertype).
Use of
mappedBy
is instruct framework to enable bi-directional relationship. Because of #ManyToOne on Transaction class you Your Transaction Table will have foreign key referring to Account table primary key. By default, Hibernate generates the name of the foreign key column based on the name of the relationship mapping attribute and the name of the primary key attribute. In this example, Hibernate would use a column with the name account_id to store the foreign key to the Account entity.
#JoinColum
can be used If you would like override default foreign key name like #JoinColum(name="acc_id")
MappedBy intructs Hibernate that the key used for the association is on the other side of the association.Like in this case ACCOUNT_ID is being created on Account table.
That means that although you associate two tables together, only one table is having foreign key constraint to the other one.
MappedBylets you to still associate from the table not having foreign key constraint to the other table.
I want to map a Map in JPA Hibernate. The set up looks like
#Entity(name = "reservation")
#Table(name = "reservation")
#Access(AccessType.FIELD)
#Audited
public class ReservationEntity {
// other fields
#ElementCollection(fetch = FetchType.EAGER)
#MapKeyColumn(name = "discountType")
#Column(name = "discountAmount")
#CollectionTable(
name="discountTypeAndAmount",
joinColumns=#JoinColumn(name="reservation_id")
)
private Map<DiscountType, BigDecimal> discountTypeAndAmount;
}
I can write the entity to the database the first time, but when I update the entity, I get the following error upon entitymanager.getTransaction().commit():
Caused by: javax.persistence.EntityExistsException: A different object with
the same identifier value was already associated with the session :
[discountTypeAndAmount_AUD#{REV=DefaultRevisionEntity(id = 3,
revisionDate = Dec 20, 2016 8:52:45 PM), element=10.00,
ReservationEntity_reservation_id=1, mapkey=CASE_STUDY}]
In the exception, CASE_STUDY is one of the enums. discountTypeAndAmount_AUD is the audit log table auto generated.
It looks like the audit table discountTypeAndAmount was generated with a composite key made up of REV (revision id), reservation_id, discountType, and discountAmount and the error is thrown because envers doesn't know how to handle BigDecimal as part of a primary key.
Is there an annotation to set the primary key for the audit table to be a composite of just REV (revision id), reservation_id, and discountType? Since the field is a map anyway, there is really no need to have discountAmount as part of the primary key.
#MapKeyColumn is used to specify mapping for the key column if key is a basic type, however I am not sure, but I guess that DiscountType is entity itself, so you need to map your key using #MapKeyJoinColumn.
I'm trying to map a HashMap similar to the one that is specified as example 3 in the JavaDoc for #MapKeyJoinColumn (see http://www.objectdb.com/api/java/jpa/MapKeyJoinColumn):
#Entity
public class Student {
#Id int studentId;
...
#ManyToMany // students and courses are also many-many
#JoinTable(name="ENROLLMENTS",
joinColumns=#JoinColumn(name="STUDENT"),
inverseJoinColumns=#JoinColumn(name="SEMESTER"))
#MapKeyJoinColumn(name="COURSE")
Map<Course, Semester> enrollment;
...
}
The generated join table (generated with EclipseLink 2.3) has the following layout:
TABLE enrollments (
student_id bigint NOT NULL,
semester_id bigint NOT NULL,
course_id bigint,
CONSTRAINT enrollments_pkey PRIMARY KEY (student_id, semester_id)
)
Why is the primary key generated for Student and Semester and not for Student and Course? This doesn't make any sense in this case. With this primary key, a Student can participate in only one course per semester. 'student_id' and 'course_id' should be defined as primary key! This would also match the Java map definition (the key must be unique, but the same value may be assigned to different keys)
JPA sees the relationship as being between Student and Semester, as in a traditional #ManyToMany without the #MapKeyJoinColumn, and in traditional #ManyToMany duplicates would not be allowed, and the items are deleted by source/target ids, so the pk/index is desired to be on these.
For a finer level of control of the model, consider mapping the ENROLLMENTS table to an Enrollment Entity instead.
I can see from the Java model how you may desire different, so please log a bug/enhancement for this.
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;
}