I'm having problems with generating primary keys with one-to-one relations that use shared primary key.
Here's code:
#Entity
#Table(name = "osoba")
public class Osoba implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "osoba_id")
private Integer osobaId;
#PrimaryKeyJoinColumn
#OneToOne(cascade = CascadeType.PERSIST)
public Pracownik pracownik;
...
and second class:
#Entity
#Table(name = "pracownik")
public class Pracownik
{
#OneToOne
#JoinColumn(name = "osoba_id")
#MapsId("osobaId")
private Osoba osoba;
#Id
#Column(name = "osoba_id")
private Integer osobaId;
...
I've been similar issues and I thought that i've done everything correctly but i still get
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): entity.Pracownik
when trying to persist Pracownik objects.
You need to follow example from #MapsId documentation (in your case, with #Id instead of #EmbeddedId):
#Entity
#Table(name = "pracownik")
public class Pracownik {
#Id
#Column(name = "oboba_id")
private Integer id;
#OneToOne
#MapsId
private Osoba osoba;
...
}
Inverse side of #OneToOne relationship should be mapped with mappedBy, as usually:
#Entity
#Table(name = "osoba")
public class Osoba implements Serializable {
...
#OneToOne(mappedBy = "osoba", cascade = CascadeType.PERSIST)
public Pracownik pracownik;
...
}
This old question but it works for me. Mayby it will help someone.
SQL Script (Oracle)
DROP TABLE HIBERNATE.PRACOWNIK;
DROP TABLE HIBERNATE.OSOBA;
DROP SEQUENCE HIBERNATE.OSOBA_SEQ;
CREATE TABLE HIBERNATE.OSOBA (
osoba_id NUMBER(15),
CONSTRAINT OSOBA_PK PRIMARY KEY (osoba_id)
);
CREATE TABLE HIBERNATE.PRACOWNIK (
pracownik_id NUMBER(15),
CONSTRAINT PRACOWNIK_PK PRIMARY KEY (pracownik_id),
CONSTRAINT PRACOWNIK_FK FOREIGN KEY (pracownik_id) REFERENCES OSOBA(osoba_id)
);
CREATE SEQUENCE HIBERNATE.OSOBA_SEQ START WITH 1 INCREMENT BY 1 NOCACHE NOCYCLE;
Osoba.java
#Entity
#Table(name = "osoba")
public #Data class Osoba {
#Id
#Column(name = "osoba_id")
#GeneratedValue(generator="osoba-generator", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name="osoba-generator", allocationSize = 1, sequenceName = "OSOBA_SEQ")
private Long osobaId;
#OneToOne(fetch=EAGER, mappedBy="osoba", cascade=ALL)
private Pracownik pracownik;
}
Pracownik.java
#Entity
#Table(name="pracownik")
public #Data class Pracownik {
#Id
#Column(name = "pracownik_id")
#GeneratedValue(generator="pracownik-generator")
#GenericGenerator(name="pracownik-generator", strategy="foreign", parameters=
#Parameter(name = "property", value = "osoba")
)
private Long pracownikId;
#OneToOne(fetch=FetchType.EAGER)
#PrimaryKeyJoinColumn
private Osoba osoba;
}
#Data is Lombok
Related
This is my first attempt to map with One to One relation. I have the following entities:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Client {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Size(max = 100)
private String name;
#Email(message = "Email should be valid")
private String email;
#OneToOne
#PrimaryKeyJoinColumn
private Key key;
}
AND
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Key {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(unique = true)
private UUID number;
#OneToOne
private Client client;
public Key(UUID number) {
this.number = number;
}
}
They do not see each other, I get NULL in the foreign key section. There is a solution when using the EntityManager class in the following post:
JPA / Hibernate OneToOne Null in foreign key
Unfortunately, that method doesn't work for me.
Database snapshot:
Thank you for the answers!
We don't need to mention the #PrimaryKeyJoinColumn annotation. When we are mapping the tables it will create primary key and foreign key. We just need to map properly.
In the Client model class you have to create the mapping like below
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "key_id", referencedColumnName = "id")
private Key key;
and in the Key model class like this
#OneToOne(mappedBy = "key")
private Client client;
I have two tables that I want to join.
Lets say TableA and TableB.
TableA
#Entity
#Table(name = "TableA")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class TableA {
#Id
#EmbeddedId
private TableA.PrimaryId id = new TableA.PrimaryId();
#Column(name = "COL1")
private Integer col1;
#ManyToOne
#JoinColumn(name = "ID1")
private TableB tableB;
#Data
#Embeddable
#Builder
#AllArgsConstructor
#NoArgsConstructor
public static class PrimaryId implements Serializable {
#Column(name = "ID1")
private Integer id1;
#Column(name = "ID2")
private Integer id2;
}
}
TableB
#Entity
#Table(name = "TableB")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class TableB {
#Id
#EmbeddedId
private PrimaryId id = new PrimaryId();
#Column(name = "COLUMN1")
private String column1;
#Data
#Embeddable
#Builder
#AllArgsConstructor
#NoArgsConstructor
public static class PrimaryId implements Serializable {
#Column(name = "ID1")
private Integer id1;
#Column(name = "KEY2")
private Integer key2;
#Column(name = "KEY3")
private Integer key3;
#Column(name = "KEY4")
private Long key4;
}
}
The problem is that TableB has composite primary key (4 columns), but I need to do join only on 1 column (ID1).
It isnt a standart join, it is a partial primary key join.
So it causes an error:
nested exception is org.hibernate.AnnotationException: A Foreign key refering TableB from TableA has the wrong number of column. should be 4
If I try this
#ManyToOne
#JoinColumn(name = "ID1", referencedColumnName = "ID1", updatable=false, insertable=false)
I get another error
nested exception is org.hibernate.AnnotationException: referencedColumnNames(ID1) of TableA.TableB referencing TableB not mapped to a single property
I've looked for an answer in similar posts but found nothing useful :(
So I appreciate any help...
#Entity
#Table(name = "A")
public class A {
#Id
#Column(name = "id")
private Long id;
#Id
#Column(name = "name")
private String name;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
#Entity
#Table(name = "Test")
public class Test {
#Id
#Column(name = "test_id")
private Long testId;
}
Error Result is
" JPA trouble with OneToOne relationship: A Foreign key refering has the wrong number of column. should be 2 "
How to specific primary key for join Test table ?
Table A : column id
map with
Table B : column test_id
Since your table A has a composite key, you should separate the columns out into another key class and then join on the individual part of the key of the table.
For instance, create AKey
#Embeddable
public class AKey {
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
//getters and setters
}
Then replace the ids in class A
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private AKey key;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
Then you can do a join on Test.testId = A.key.id
Ive added 2 hibernate model objects
First table
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#Column(name = "CA_ID", nullable = false, insertable = true,updatable = true, length = 22, precision = 0)
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "CSM_ACTIVITIES_SEQ")
private Long id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "activityId", cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
List<ActivitiesProductsMO> relatedProducts;
...getters / setters
}
The other table is
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
#Column(name = "CAP_ID")
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "ACTIVITIES_PRODUCTS_SEQ")
private Long id;
#Column(name = "CAP_ACTIVITY_ID")
private Long activityId;
#Column(name = "CAP_PRODUCT_ID")
private Long productId;
...getters/setters
}
The point is to populate each db record for ActivitiesProductsMO.activityId with ActivityMO.id value
I.e.
If I create an activity record with id = 555
I'll get another activity_product record with activityId of 555
How can i get this to work?
Thank you!
Instead of manually trying to map the entitiy relations with long values you should use a bidirectional OneToMany relationship from ActivityMO to ActivitiesProductsMO
change ActivitiesProductsMO to:
#Entity
#Table(name = "ACTIVITIES_PRODUCTS")
public class ActivitiesProductsMO {
// cut unimportant code ...
#ManyToOne
#JoinColumn(name = "CAP_ACTIVITY_ID")
private ActivityMO activityId;
// cut unimportant code ...
}
If you then were to persist an ActivityMO that already has ActivitiesProductsMO entries in its relatedProducts List, the Cascade type should actually take care and create those products while filling out the CAP_ACTIVITY_ID database field with the right value.
Another Possible Solution:
Use a Unidirectional OneToMany:
#Entity
#Table(name = "ACTIVITIES")
public class ActivityMO extends ModelBase {
#OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "CAP_ACTIVITY_ID")
List<ActivitiesProductsMO> relatedProducts;
}
And remove the
private Long activityId;
from your ActivitiesProductsMO class.
This should both lead to identical database structure. But in the second case you would no longer have the "backlink" inside java from ActivitiesProductsMO to ActivityMO
I can't propper map DB tables with JPA annotation.
Tables Subject and Place is ManyToMany through JoinTable.
Subject.java
#Entity
#Table(name = "SUBJECT")
public class Subject implements Serializable {
#Id
#Column(name = "SID")
private Integer sid;
#Column(name = "NAME")
private String name;
// getters and setters
}
SubjectPlace.java
#Entity
#Table(name = "SUBJECT_PLACE")
public class SubjectPlace implements Serializable {
#Id
#Column(name = "SPID")
private Integer spid;
#ManyToOne
#JoinColumn(name = "SUB_KEY") //Subject FK
private Subject subject;
#ManyToOne
#JoinColumn(name = "PLC_KEY") //Place FK
private Place place;
// getters and setters
}
Place.java
#Entity
#Table(name = "PLACE")
public class Place implements Serializable {
#Id
#Column(name = "PID")
private Integer pid;
#Column(name = "NAME")
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "SUBJECT_PLACE",
joinColumns = { #JoinColumn(name = "PLC_KEY", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "SUB_KEY", nullable = false, updatable = false) })
private Set<Subject> subjects;
// getters and setters
}
But than I need to link Person with Subject in selected Places. I mean that each Place has its own collection of Subject. And a Person have link to Subject whitch resides in particular Place.
like This:
Subject (M) -- (M) Place through JoinTable Subject (1) -- (M) Subject_Place (M) -- (1) Place
Person (M) -- (M) Subject_Place through JoinTable Person (1) -- (M) Person_Subject_Place (M) -- (1) Subject_Place
Person.java
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
#Column(name = "PRSID")
private Integer prsid;
#Column(name = "NAME")
private String name;
// How to annotate this code?
// I experience problem in this part of code
#OneToMany
#JoinColumn(name="SPID_KEY")
private List<SubjectPlace> subjectPlaces;
// getters and setters
}
PersonSubjectPlace.java
#Entity
#Table(name = "PERSON_SUBJECT_PLACE")
public class PersonSubjectPlace implements Serializable {
#Id
#Column(name = "PSPID") // Person_Subject_Place ID
private Integer pspid;
#ManyToOne
#JoinColumn(name = "PER_KEY") //Person FK
private Person person;
// How to annotate this code?
// I experience problem in this part of code
#ManyToOne
#JoinColumn(name = "SPID_KEY") //Subject_Place FK
private SubjectPlace subjectPlace;
// getters and setters
}
And when I try so get Persons and its Subjects, I get this error:
Caused by: org.hibernate.MappingException: Foreign key (FK2C3B79384AABC975:PERSON_SUBJECT_PLACE [SPID_KEY])) must have same number of columns as the referenced primary key (SUBJECT_PLACE [PLC_KEY,SUB_KEY])
What, How shoul I map?
In your OneToMany mapping you don't need to specify the foreign key, you just need to use mappedBy property to refer your mapping object, you can learn more about it in OneToMany Mapping Documentation, and here's what you need to map Person and PersonSubjectPlace entities:
In your Person class:
#OneToMany(mappedBy="person")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="PRSID") //Specify the primary key of Person
private Person person;
For further information about the difference between JoinColumn and mappedBy you can take a look at this answer.
EDIT:
For the mapping between SubjectPlace and PersonSubjectPlace:
In your SubjectPlace class:
#OneToMany(mappedBy="subjectPlace")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="SPID") //Specify the primary key of SubjectPerson
private SubjectPlace subjectPlace;
Note:
The best approach to map those classes is to use #JoinTable between Person and SubjectPlace, take a look at this #JoinTable example, because PersonSubjectPlace is pratically an asociation-entity between Person and SubjectPlace.
You should remove #Joincolumn annotation and add mappedBy variable to #OneToMany annotation.
#OneToMany(mappedBy = "spid")
You should have a variable in SubjectPlace that has a Person where you should put #JoinColumn annotation