I'm having trouble getting my head arround this.
How can I create an one-to-one bidirectional mapping between Organisation and OrganisationAddress? This is what I've tried so far:
I got two tables:
**ORGANISATION**
ORGNO <pk>
BANKNO <pk>
**ORGANISATION_ADDRESS**
ORGNO <pk, fk>
BANKNO <pk, fk>
A Java composite key class for each table:
#Embeddable
public class OrganisationPK implements Serializable {
#Column(name="ORGNO", length=9)
private String orgno;
#Column(name="BANKNO", length=4)
private String bankno;
... getters/setters ...
}
and
#Embeddable
public class OrganisationAddressPK implements Serializable {
#Column(name="ORGNO", length=9)
private String orgno;
#Column(name="BANKNO", length=4)
private String bankno;
... getters/setters ...
}
This is the Java classes for Organisation and OrganisationAddress:
#Entity
#Table(name="ORGANISATION")
public class Organisation implements Serializable{
#Id
private OrganisationPK comp_id;
/** bi-directional one-to-one association to OrganisationAddress */
#OneToOne(mappedBy = "organisation", cascade = CascadeType.ALL)
private OrganisationAddress organisationAddress;
}
and
#Entity
#Table(name="ORGANISATION_ADDRESS")
public class OrganisationAddress implements Serializable {
#Id
#AttributeOverrides({
#AttributeOverride(name = "orgno", column=#Column(name="ORGNO")),
#AttributeOverride(name = "bankno", column=#Column(name="BANKNO"))
})
private OrganisationAddressPK comp_id;
/** bi-directional one-to-one association to Organisation */
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "ORGNO", referencedColumnName = "ORGNO"),
#PrimaryKeyJoinColumn(name = "BANKNO", referencedColumnName = "BANKNO")
})
private Organisation organisation;
When I run the tests, it gives me this error:
org.hibernate.TypeMismatchException: Provided id of the wrong type for class org.hibernate.OrganisationAddress.
Expected: class org.hibernate.OrganisationAddressPK, got class org.hibernate.OrganisationPK
From this call:
private void deleteOrganisation(Organisation organisation){
Organisation hibOrganisation = (Organisation) getCurrentSession().get(
Organisation.class, new OrganisationPK(
organisation.getOrgNo(), BANKNO.getValue()));
...
Related
I created the following models:
"Vendor"
"PickupStation"
And both of them have a OneToMany Relationship to a composite Key
"PickupStationVendorDetails" which has an embedded Id "PickupStationVendorKey"
It works to save the "PickupStationVendorDetails" with the corresponding Vendor and PickupStation but when I want to fetch them from e.g the Vendor nothing is found.
Vendor.java
#Entity
public class Vendor {
...
#OneToMany(mappedBy = "vendor")
private Set<PickupStationVendorDetails> pickupStations;
}
PickupStation.java
#Entity
public class PickupStation {
#OneToMany(mappedBy = "pickupStation")
private Set<PickupStationVendorDetails> vendors;
}
PickupStationVendorDetails.java
#Entity
public class PickupStationVendorDetails {
#EmbeddedId
private PickupStationVendorKey id;
#ManyToOne
#MapsId("vendorId")
#JoinColumn(name = "vendor_id")
private Vendor vendor;
#ManyToOne
#MapsId("pickupStationId")
#JoinColumn(name = "pickup_station_id")
private PickupStation pickupStation;
}
PickupStationVendorKey.java
#Embeddable
public class PickupStationVendorKey implements Serializable {
#Column(name = "vendor_id", columnDefinition = "BINARY(16)")
private UUID vendorId;
#Column(name = "pickup_station_id")
private Long pickupStationId;
public PickupStationVendorKey() {
}
public PickupStationVendorKey(UUID vendorId, Long pickupStationId) {
this.vendorId = vendorId;
this.pickupStationId = pickupStationId;
}
....
}
How I persist the entities:
At first I create the embeddedID and save the details via repository:
PickupStationVendorDetails pickupStationVendorDetails = new PickupStationVendorDetails();
pickupStationVendorDetails.setVendor(vendor);
pickupStationVendorDetails.setPickupStation(pickupStation);
pickupStationVendorDetails.setDeliveryDays(relationship.getDeliveryDays());
PickupStationVendorKey embeddedId = new PickupStationVendorKey(vendor.getId(),pickupStation.getId());
pickupStationVendorDetails.setId(embeddedId);
PickupStationVendorDetails d = pickupStationVendorDetailsRepository.save(pickupStationVendorDetails);
Afterwards I add them to the Set<> of the corresponding Entities and save them too.
vendor.getPickupStations().add(d);
pickupStation.getVendors().add(d);
vendorService.save(vendor);
pickupStationRepository.save(pickupStation);
And when I try to call vendor.getPickupStations() there seems to be no relationship.
Except I call pickupStationVendorDetailsRepository.findAll() the composite Key is correctly persisted and saved, and from there on I would be able to get the PickupStation and the Vendor. But that's not how it should work I guess.
Am I missing something?
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.
Can I use #EmbeddedId in case more than 3 relations with composite key ?
relation
parent -> child -> grandchild -> great-grandchild
parent
#Entity
#Table(name="mig_club_info")
public class MigGolfClub {
#Id #Column(name = "club_id")
private String club_id;
}
child
#Embeddable
public class MigCourseId implements Serializable{
private String club_id;
private String course_id;
}
#Entity
#Table(name="mig_course_info")
public class MigCourse {
#EmbeddedId
private MigCourseId migCourseId;
#MapsId("club_id")
#ManyToOne
#JoinColumn(name = "club_id")
private MigGolfClub migGolfClub;
}
grandchild
#Embeddable
public class MigHoleId implements Serializable{
private MigCourseId migCourseId;
private int hole_num;
}
#Entity
#Table(name="mig_hole_info")
public class MigHole {
#EmbeddedId
private MigHoleId migHoleId;
#MapsId("migCourseId")
#ManyToOne
#JoinColumns( {
#JoinColumn(name="club_id"),
#JoinColumn(name="course_id")
})
private MigCourse migCourse;
}
great-grandchild
#Embeddable
public class MigTeeId implements Serializable{
private MigHoleId migHoleId;
private String t_name;
}
#Entity
#Table(name="mig_tee_info")
public class MigTee {
#EmbeddedId
private MigTeeId migTeeId;
#MapsId("migHoleId")
#ManyToOne
#JoinColumns( {
#JoinColumn(name="club_id"),
#JoinColumn(name="course_id"),
#JoinColumn(name="hole_num")
})
private MigHole migHole;
}
I tested and got a error message
nested exception is org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a #MapsId: xxx.xxx.xxx.MigHole"
I succeeded with grandchild (2-relations) , but I failed with greate-grandchild (3-relations)
Can't I use #EmbeddedId in case more than 3 relations with composite key?? --;;
I am working on JPA project and I need your help.
I have two classes, “Person” and “Leader” which inherits from Person.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
}
And
#Entity
public class Leader extends Person implements Serializable {
private List < Person > listTeam;
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
My question Is, do I need to have JPA annotations #OneToMany or something else before private List listTeam in class Leader?
Thank you very much
You need to specify a mapping between the two classes because for Hibernate the association is not relevant here, you have to use annotations in both sides and I guess you will need a OneToMany mapping here :
Here's the mapping that you are seraching for:
In Person class:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(unique = true)
private String personId;
#ManyToOne
#JoinColumn(name="leader_id")
private Leader leader;
//getter and setter
}
In Leader class:
#Entity
public class Leader extends Person implements Serializable {
#OneToMany(mappedBy = "leader")
private List <Person> listTeam;
//getter and setter
public void addPersonInTeam(Person e) {
listTeam.add(e);
}
}
For further information you can see these links:
Hibernate – One-to-Many example (Annotation).
Hibernate One To Many Annotation tutorial.
Note:
I don't see the use of the field personId in the Person class, there's no need to use two differents ids.
EDIT:
To answer your questions:
The #JoinColumn(name="leader_id") is not mandatory, but it's used to specify the foreign key name.
If the relation is ManyToMany the mappedBy property is used to specify the owner of the relationship, you can see this answer for more details.
I've ran into problem with composite primary key handling by Hibernate as a JPA provider.
My entities look like below
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
Looks pretty simple yet no matter what I do I keep getting the following exception (lines are splitted for readability):
org.hibernate.MappingException:
Repeated column in mapping for entity: ExternalMatch
column: external_object_id (should be mapped with insert="false" update="false")
I've tried placing annotation on entity class fields and key class fields together as well as separately, moving all annotations from fields to getters on each one of the classes, using key calss as #Embeddable and putting it into the entity class with #EmbeddedId. Nothing seems to work.
This case seems trivial so maybe it's something wrong with our setup but I can't even imagine where to look for the issue.
Any advice is much appreciated.
It appears that I shot myself in the foot with this.
The issue was that I had a biderectional mapping between ExternalMatch and ExternalObject I forgot about trying to replace the actual entity with its integer id.
So changing
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "external_object_id", nullable = false)
private List<ExternalMatch> matches;
// ...
}
to reprsent actual mappings like this
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#ManyToOne
#JoinColumn(name = "external_object_id", referencedColumnName = "id")
private ExternalObject externalObject;
#Id
#ManyToOne
#JoinColumn(name = "place_id")
private Poi place;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Poi place;
private ExternalObject externalObject;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "externalObject")
private List<ExternalMatch> matches;
// ...
}
resolved the repeated mapping issue yet leaving us with all the familiar troubles a biderectional mapping creates :)