Hibernate Bidirectional one-to-many with composite key - java

I am trying to create a bidirectional one-to-many association between two entities where the many side has a compound key. And one of the keys of the many side is coming from the one side. Also, I need to have the many side the owner of the association. Below is an example code showing what my code looks like.
Without Jointable
Parent Class which is the one side. I need to have this side owner of the association.
public class parent{
#Id
#Column(name = "NAME")
private String name;
#OneToMany(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="NAME", nullable = false),
#JoinColumn(name="PARENT", nullable = false)})
private Set<Child> childs;
}
Child Class which is the many side. Its primary key is "name" and "parent". "parent" is coming from the association.
public class child{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="parent", column=#Column(name="PARENT", nullable=false)),
#AttributeOverride(name="name", column=#Column(name="NAME", nullable=false))})
private ChildId id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="PARENT", nullable = false, updatable = false, insertable = false),
#JoinColumn(name="NAME", nullable = false, updatable = false, insertable = false)})
private Parent parent;
}
ChildId is the Embedded id.
#Embeddable
public class childId{
#Column(name = "PARENT")
private String parent;
#Column(name = "NAME")
private String name;
}
With Jointable
Parent Class
public class parent{
#Id
#Column(name = "NAME")
private String name;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name="PARENTCHILD",
joinColumns= {#JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
inverseJoinColumns = {
#JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
#JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
private Set<Child> childs;
}
Child Class
public class child{
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name="parent", column=#Column(name="PARENT", nullable=false)),
#AttributeOverride(name="name", column=#Column(name="NAME", nullable=false))})
private ChildId id;
#MapsId("parent")
#ManyToOne(fetch=FetchType.LAZY)
#JoinTable(name="PARENTCHILD",
inverseJoinColumns = {#JoinColumn(name="PNAME", referencedColumnName = "NAME", nullable = false)},
joinColumns = {
#JoinColumn(name="CNAME", referencedColumnName = "NAME", nullable = false),
#JoinColumn(name="CPNAME", referencedColumnName = "PARENT", nullable = false)})
private Parent parent;
}
Question1:
This code doesn't work. In case of "without jointable", it gives the below exception.
Caused by: org.hibernate.AnnotationException: A Foreign key refering com.entity.Parent from com.entity.Child has the wrong number of column. should be 1
Question2:
And in case of "with jointable", it gives below exception:
SQLCODE=-530, SQLSTATE=-23503, SQLERRMC=PARENTCHILD.FK_PARENTCHILD_CHILD

You don't need to keep Parent name as Id separately in Child , Hibernate will do that for you . I have made a simpler design .And you can control the relation by using mappedBy = childs ,in the #ManyToOne or mappedBy = parent in the #ManyToOne side.
#Entity
public class Parent{
#Id
private String name;
#OneToMany(fetch= FetchType.LAZY)
private Set<Child> childs;
public Parent(String name) {
this.name = name;
}
public Parent(){}
}
#Entity
public class Child{
#Id
private String name;
#ManyToOne(fetch=FetchType.LAZY)
private Parent parent;
}
Three tables will be generated by Hibernate
Child Table with columns name(Primary Key), parent_name(Foreign Key)
Parent Table with one column name(Primary Key)
Parent_child table with two columns parent_name and child_name
EDIT : Solution changed as per amir's needs, just add mappedBy whichever side you need to control the relationship.
#Entity
public class Child implements Serializable {
#Id
private String name;
#Id
#ManyToOne(fetch=FetchType.LAZY)
private Parent parent;
}
#Entity
public class Parent{
#Id
private String name;
#OneToMany(fetch= FetchType.LAZY)
private Set<Child> childs;
public Parent(String name) {
this.name = name;
}
public Parent(){}
}
EDIT - To name column in child side
#Id()
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="xyz")
private Parent parent;

To use One-To-Many bidirectional mapping using join table,use following code:
Parent Class
public class parent{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.....
.....
#OneToMany(cascade=CascadeType.ALL)
#JoinTable(name="Parent_Child", joinColumns={#JoinColumn(name ="parentId", referencedColumnName ="id")},
inverseJoinColumns={#JoinColumn(name ="childId", referencedColumnName ="id")})
private Set<Child> children;
.....
.....
}
Child Class
public class Child{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.....
.....
#OneToOne(cascade=CascadeType.ALL)
#JoinTable(name="Parent_Child", joinColumns={#JoinColumn(name ="childId", referencedColumnName ="id")},
inverseJoinColumns={#JoinColumn(name ="parentId", referencedColumnName ="id")})
private Parent parent;
.....
.....
}
Note that I have used OneTOMany mapping in Parent class - cause as per your logic a parent can have multiple children and I have used OneToOne mapping in Child class cause one child will have one parent(as mentioned in your requirements).
Hope this helps

Related

JPA Parent Child relationship on same entity(view)

I have a view which has a parent-child relationship. So I have my entity this way:
#Entity
#Table(name = "assessment_v", schema = "hlt_hrsc")
public class HrscLabellingAssessmentVEntity {
#Id
private String id;
#Column(name = "child_id")
private String childId;
#ManyToOne(cascade = {CascadeType.ALL})
#JoinColumn(name = "id")
private HrscLabellingAssessmentVEntity parent;
#OneToMany(mappedBy = "parent")
private Set<HrscLabellingAssessmentVEntity> child = new HashSet<>();
}
when I start my application it throws an error :
nested exception is org.hibernate.MappingException: Could not determine type for: java.util.Set
Please find the below image of the view for parent child eg:
id child_id
120.35871 120.35872
120.35872 null
Here 120.35872 is the parent record and 120.35871 is the child record. The parentId is maintained on the child_id column. My requirement is when I try to fetch the parent it should have the child records also.
what am I doing wrong??
Please check this example
#Entity
#Table(name = "assessment_v", schema = "hlt_hrsc")
public class HrscLabellingAssessmentVEntity {
#Id
private String id;
#Column(name = "child_id")
private String childId;
#JsonBackReference
#JoinColumn(name = "parent_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
#Fetch(FetchMode.JOIN)
private HrscLabellingAssessmentVEntity parentId;
#JsonManagedReference
#OneToMany(mappedBy = "parentId", cascade = CascadeType.ALL)
private Set<HrscLabellingAssessmentVEntity> child = new HashSet<>();
}

JPA : Foreign key not getting inserted in child class with #IdClass in both parent and child classes

I have a parent with composite PK (id + serial_no).
The child also has a PK (id + serial_no + attribute), where FK is (id + serial_no)
Note : I have used #IdClass instead of #EmbeddedId, as in my Parent I have id to be generated from a sequence (And I read on stackoverflow that sequencegenerator doesn't work with embeddedid).
Problem :
Caused by: oracle.jdbc.OracleDatabaseException: ORA-01400: cannot insert NULL into ("schema_name"."child"."id")
Below is my save logic
Parent parent = new Parent();
parent.setSerialNo(0L);
//Note I don't want to set the Id as its supposed to be generated by the sequence
Child child = new Child();
child.setAttribute("abc");
//so in the below line am setting parent in child class
child.setParent(parent);
//note in the child am not setting the id and serial_no coz i want it to come from parent
repository.save(parent);
// I am expecting the cascade so that both the parent and child get saved
Below is my entity code :
#Entity
#Table(name = "parent")
#IdClass(ParentPK.class)
public class Parent implements Serializable {
#Id
#Column(name = "id", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator", sequenceName="o_seq", allocationSize=1)
private Long id;
#Id
#Column(name="serial_no")
Long serialNo;
#JsonManagedReference
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
private List<Child> childs;
}
#Entity
#Table(name="child")
#IdClass(ChildPK.class)
public class Child implements Serializable{
#Id
#Column(name="id")
Long id;
#Id
#Column(name="serial_no")
Long serialNo;
#Id
#Column(name="attribute")
String attribute;
#JoinColumns(value = {
#JoinColumn(name = "id", referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(name = "serial_no", referencedColumnName = "serial_no", insertable = false, updatable = false)
})
#JsonBackReference
#ManyToOne(fetch=FetchType.LAZY, optional=true)
private Parent parent;
}
Many thanks in advance
#Entity
#Table(name="child")
#IdClass(ChildPK.class)
public class Child implements Serializable{
#Id
#Column(name="attribute")
String attribute;
#Id
#JoinColumns(value = {
#JoinColumn(name = "id", referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(name = "serial_no", referencedColumnName = "serial_no", insertable = false, updatable = false)
})
#JsonBackReference
#ManyToOne(fetch=FetchType.LAZY, optional=true)
private Parent parent;
}
class ChildPK implements Serializable {
Parent parent;
String attribute;
...
}
See the Hibernate ORM documentation about composite identifiers for more details.

Spring Data Rest Projection to get value from different entity

I have two entities. One is the parent (for which I have a projection) and the other entity has FK relation with the parent. My parent entity does "not" have bidirectional relationship (mappedBy).
How do I expose/get the child entity in the projection I have for the parent.
Here is how I want.
Parent:
public class EntityA implements java.io.Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "ENTITYAID", unique = true, nullable = false)
private Integer entityAID;
......
}
Child:
public class EntityB {
#EmbeddedId
private EntityBPk entityBPk;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ENTITYAID", referencedColumnName = "ENTITYAID",insertable=false,updatable=false)
private EntityA entityA;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ENTITYCID", referencedColumnName = "ENTITYCID",insertable=false,updatable=false)
private EntityC entityC;
#Column(name = "DUMMY")
private String dummy;
}
I want to access EntityB from EntityA's projection. Something like below.
Projection for EntityA:
#Projection(name = "projEntityA", types = { EntityA.class })
public interface EntityAProjection {
.....
Set<EntityB> getEntityBs();
}
Note:
But I don't have any reference of EntityB inside EntityA (since I don't want bidirectional relationship)
This may not be needed. But just FYI.
public class EntityBPk implements Serializable {
#Column(name = "ENTITYAID", nullable = false)
private Integer entityAID;
#Column(name = "ENTITYCID", nullable = false)
private Integer entityCId;
}
Thanks
Bharath

Child table not having parent's id as foreign key - JPA/Hibernate

Maybe a very obvious answer exists but I cant see it.
I have a parent table Parent and a child Table Child.
#Entity
#Table(name="PARENT")
public class Parent implements Serializable {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="PARENT_ID")
private int parentId;
#JsonBackReference
#OneToMany(mappedBy="parent", cascade = CascadeType.ALL)
private List<Child> childs;
....
}
And the child entity.
#Entity
#Table(name="CHILD")
public class Child implements Serializable {
#EmbeddedId
private ChildPK id;
#ManyToOne
#JsonManagedReference
#JoinColumn(name="PARENT_ID",insertable = false, updatable = false, referencedColumnName = "PARENT_ID")
private Parent parent;
....
}
The composite primary key being:
#Embeddable
public class ChildPK implements Serializable {
#Column(name="CTGRY_ID", insertable=false, updatable=false)
private int ctgryId;
#Column(name="PARENT_ID", insertable=false, updatable=false)
private int parentId;
....
}
Now in my service method, i set the relation both ways
childItem.setParent(parent);
parent.setChilds(childItemList);
and call save on the entity:
parentDao.save(parent);
This creates a new entry for the PARENT table as expected with a pk generated by the table identity. Creates a new insert in the CHILD table as expected with a pk generated by the table identity.
However, the fk of the child entry should have been the parentId, but it comes as 0!
Am i missing something?
Although this problem is not unique, when I tried looking for similar issues online, few of the suggestions didn't appear to be relevant.
Please let me know in case you need more details.
Thanks!
You are making both id's entries not insertable (one must be insertable, or the JPA implementation wont know from where take the id):
#ManyToOne
#JsonManagedReference
#JoinColumn(name="PARENT_ID", insertable = false, updatable = false, referencedColumnName = "PARENT_ID")
private Parent parent;
And
#Column(name="PARENT_ID", insertable=false, updatable=false)
private int parentId;
You should remove the insertable = false from parent object (first example).
Other option is removing the insertable false from the second one and making it "by hand" in the #prepersist:
#PrePersist
protected setIdsBeforeSave() {
this.id = new ChildPK(parent.Id, categoryId);
}
Edit
Option 1
#Entity
#Table(name="CHILD")
public class Child implements Serializable {
#EmbeddedId
private ChildPK id;
#ManyToOne
#JsonManagedReference
#JoinColumn(name="PARENT_ID", referencedColumnName = "PARENT_ID")
private Parent parent;
....
}
And the other classes remain the same
Option 2
#Entity
#Table(name="CHILD")
public class Child implements Serializable {
#EmbeddedId
private ChildPK id;
....
#PrePersist
protected void setIdsBeforeSave() {
//You set the parent ID, so wont be null, idk what category means, that why i just put the 0
this.id = new ChildPK(parent.Id, 0);
}
}

#ManyToOne and #OneToOne relations with #EmbeddedId

I am trying to change the id of my database entities from a single long to a composite id consisting of two long's which are encapsulated in my ID.class shown below. What annotations would you use for ManyToOne and OneToMany relations? Did I made a mistake with my annotations, especially with #JoinColumns, #Embeddable and #EmbeddedId. My problem is that relations are stored with null;
My ID class consist of an autogenerated long (pid) and a manually set secondary long (sid). This is the ID for all my entity classes:
#Embeddable
public class ID implements Serializable {
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long pid;
#Column(nullable=false)
private long sid;
}
My Entity-class (example) has a many-to-one relation to Office a one-to-one relation to Settings (both relations are uni-directional).
#Entity
#Table(name="user")
public class User {
#EmbeddedId
private ID id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "office_pid", referencedColumnName = "pid", updatable=false, insertable=false),
#JoinColumn(name = "office_sid", referencedColumnName = "sid", updatable=false, insertable=false)
})
private Office office;
#OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE}, fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "setting_pid", referencedColumnName = "pid", updatable=false, insertable=false),
#JoinColumn(name = "setting_sid", referencedColumnName = "sid", updatable=false, insertable=false)
})
private Settings setting;
#OneToMany(mappedBy = "user", cascade=CascadeType.REMOVE, fetch=FetchType.LAZY)
private List<Account> accounts= new ArrayList<>();
}
Office class (Many-To-One example):
#Entity
#Table(name = "office")
public class Office {
#EmbeddedId
private ID id;
#Column(length = 128)
private String description;
}
Account class (One-To-Many example):
#Entity
#Table(name="account")
public class Account {
#EmbeddedId
private ID id;
#Column(length = 16)
private String description;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "user_pid", referencedColumnName = "pid"),
#JoinColumn(name = "user_sid", referencedColumnName = "sid")
})
private User user;
}
Thanks in advance for your help.
Sequencing shouldn't be used if you want a composite pk - use only the sequence generated field which is unique, or composite business fields that are unique.
Second issue is the joinColumn definitions in User. You have marked them as updatable=false, insertable=false which means the foreign keys cannot be changed through the mappings, and is why they are always null regardless of setting the relationship. Remove the updatable=false, insertable=false settings from each to have the fields set when you set the relationship.

Categories