I have 2 classes with OneToMany one-directional relationship.
class A {
#Column(name = "a_id")
Integer id;
#OneToMany
#JoinColumn(name = "a_id")
private List<B> listOfB;
//getters and setters
}
class B {
Integer id;
String name;
#Column("a_id")
Integer aId;
//getters and setters
}
In database I already have saved instance of B.
I need to do:
a.listOfB.add(b);
save(a);
In object b in database I have id and name, but fk is null. I need to update fk, but before it I need to fetch object b;
How can I do it?
Only by writing custom method, or Hibernate/JPA have its own method to fetch objects?
Your mapping is not very common. Common way to have #JoinColumn in B and mappedBy = "a" in A. And you can specify cascade. In the simplest case cascade = CascadeType.ALL.
class A {
#Column(name = "a_id")
Integer id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL)
private List<B> listOfB = new ArrayList<B>;
public void addB(B b) {
b.setA(this);
listOfB.add(b);
}
}
class B {
Integer id;
String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
}
To solve your issue
save(a);
b.setA(a);
saveOrUpdate(b);
And cascade will help you to do the same by almost your way with a help of addB() method
a.addB(b);
save(a);
Related
I have a ClassA entity, which contains set of ClassB entities.
public class ClassA {
#Id
#Column(name = "id")
#EqualsAndHashCode.Include
private Long id;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "classA")
#Cascade({org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.REFRESH, org.hibernate.annotations.CascadeType.DETACH})
private Set<ClassB> classB = new HashSet<>();
#PrePersist
#PreUpdate
void setClassA() {
if (nonNull(this.classB)) {
this.classB.forEach(b -> b.setA(this));
}
}
}
ClassB entity has composite key, which points to two other entities' ids.
#Embeddable
public class ClassBId implements Serializable {
private String cId;
private Long aId;
}
public class ClassB {
#AttributeOverrides({
#AttributeOverride(name = "c", column = #Column(name = "c_id")),
#AttributeOverride(name = "a", column = #Column(name = "a_id"))
})
#EmbeddedId
private ClassBId id;
#OneToOne
#Cascade({org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.REFRESH, org.hibernate.annotations.CascadeType.DETACH})
#JoinColumn(name = "c_id", referencedColumnName = "id")
#MapsId("cId")
private ClassC classC;
#ManyToOne
#Cascade({org.hibernate.annotations.CascadeType.MERGE, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.REFRESH, org.hibernate.annotations.CascadeType.DETACH})
#JoinColumn(name = "a_id", referencedColumnName = "id")
#MapsId("aId")
private ClassA classA;
#PrePersist
#PreUpdate
private void setEmbeddedId() {
id.setCId(c.getId());
id.setAId(a.getId());
}
}
When I save my ClassA entity and CrudRepository returns me a result, everything is nicely filled in but that embedded id remains null, with or without setting id manually in pre-operation method.
What kind of join or mapping am I missing so on save or update I can not only have ClassC and ClassA inside of ClassB entity (which takes place already) but also somehow have that composite key consisting of PKs of ClassA and ClassC automagically filled in?
I think you have to assign the id to at least an empty object for this to work:
#EmbeddedId
private ClassBId id = new ClassBId();
I'd like to create a OneToOne relation between two entities(also tables in mysql). When i get a post request i want to save the A entity and then B entity is saved automatically, because there is a column in B ("a_id") that is foreign key of the primary key in A ("id_a"). How can i map them correctyl?
#Table("a_tb")
public class A(){
#Column(name="id_a")
private Long idA;
#OneToOne(mappedBy = "aId", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, orphanRemoval = true, optional = false)
private B b;
}
#Table("b_tb")
public class B(){
#Column(name="id_b")
private Long idB;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private Long aId;
}
Have tried to set idA to A type, but when i call the get method he will loop calling A with B innested that as A innested and so on..
I just want to save the id of A in B, not the entire object, otherwise he will return the entire object when i call the get method and the loop is back.
The mapping is correct, not the autoset of the value in a_id in B.
What I expect is: when i create A, B gets automatically the id of A and save it to its column ("id_a") in db when i request to save object A. That's should work with delete too (when i delete A, B is deleted too).
The matched JSON will be:
{
"a":{
"id_a": "value",
},
"b":{
"id_b": "value",
"a_id": "id_a"
}
}
Thanks in advice.
from the first look it looks like
#OneToOne
#JoinColumn(name = "idA_id")
A a;
would be the change you need and
You need to provide one to one mapping [Reference] in both the classes.
If parent reference, not setup automatically you can create a transient variable and set parent's primary key using it.
Try this Example:
#Table("a_tb")
public class A(){
#Column(name="id")
private Long id;
#OneToOne(mappedBy = "aId", cascade = CascadeType.ALL,
fetch = FetchType.LAZY, orphanRemoval = true, optional = false)
private B b;
}
#Table("b_tb")
public class B(){
#Column(name="id")
private Long id;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
#Transient
private Long aId;
// getters and setters
public void setAId(Long aId){
if(aId != null){
this.a = new A();
this.a.setId(aId);
}
}
}
I am new to hibernate.
I have the following situation,
#Entity(name="A")
Class A {
#Id
int id;
Set<B> listB;
}
#Entity(name="B")
Class B {
#Id
CompositebPK bPk;
}
Class CompositebPK {
#Column(name="id")
int aId;
#Column(name="user_name")
String uname;
}
#Entity (name = "user")
Class User {
#Column (name ="user_name")
String uname;
}
table Structures is as follows,
Table A:
int id
Table B:
int id
String uname
User:
String uname
String password
A to B is 1 to many relation.
B to User is 1 to many.
I want to have the list of B's in A.
What is the best approach to solve the situation?
In the net when I search I am just getting information for inverse join for simple columns, if I try that its not working out, so is there a special way to achieve this?
Try the following mapping configuration:
In the A entity class:
#Entity(name="A")
class A {
#Id
int id;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "bPk.a")
#Cascade(value = { CascadeType.DELETE })
Set<B> listB;
}
In CompositebPk:
class CompositebPK {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(referencedColumnName = "id", nullable = false) })
A a;
#Column(name="user_name")
String uname;
}
I am using:
Spring 3.2
Hibernate 4.1.9
I need to map, with JPA, three classes. Class A has a ManyToMany relationship with Class B. A unique combination of Class A and Class B need to own a collection of Class C.
Table A
foo
id | name
Table B
bar
id | name
Table C
data
id | xrefId
Join Table -- Unique Key on (fooId,barId)
xref
id | fooId | barId
Altering the existing data structure is not an option.
Edit 1:
Goal: Load a Foo, get its collection of Bars. From each Bar, get its (their!) collection of Data.
Class A
#Entity
public class Foo {
#Id
private UUID id;
#ManyToMany(optional = false)
#JoinTable(name = "xref",
joinColumns = { #JoinColumn(name = "fooId") },
inverseJoinColumns = { #JoinColumn(name = "barId") })
private List<Bar> lstBar = new ArrayList<Bar>();
}
Class B
public class Bar {
#Id
private UUID id;
#ManyToMany(mappedBy = "lstBar")
private List<Foo> lstFoo = new ArrayList<Foo>();
}
Class C
public class Data {
#Id
private UUID id;
}
Just KISS. Make another class Xref, which contains id, foo, bar and Set<Data> fields. Make a DAO method to find an Xref using two parameters foo and bar (implement it with a simple HQL). The unique requirement could be achieved by an unique constraint in the database.
It doesn't look good trying to express it just by the class hierarchy, better to use DAOs.
Your join table, xref, has an extra id field, in order to be able to create such a table with JPA you need an extra entity class XRef and then you have to map the relation between A and XRef and betweem B and XRef (both are one-to-many). Then, you can create the entity class C and map the relation between C and XRef. Do you need more help? I don't have time right now to provide the code, but if you need ask and I will try to add it as soon as possible.
Look at this example (used Integer instead of UUID for simplicity, the rest should be OK).
Bar class:
public class Bar {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "barId")
private Collection<Xref> xrefCollection;
}
Foo class:
public class Foo {
#Id
private Integer id;
#Column(name = "name")
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "fooId")
private Collection<Xref> xrefCollection;
}
Xref class:
public class Xref {
#Id
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "xrefId")
private Collection<Data> dataCollection;
#JoinColumn(name = "bar_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Bar barId;
#JoinColumn(name = "foo_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Foo fooId;
}
Data Class:
public class Data {
#Id
private Integer id;
#JoinColumn(name = "xref_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private Xref xrefId;
}
This code has been automatically generated by NetBeans, provided that all tables and indexes are correctly defined in the DB
I have these two class(table)
#Entity
#Table(name = "course")
public class Course {
#Id
#Column(name = "courseid")
private String courseId;
#Column(name = "coursename")
private String courseName;
#Column(name = "vahed")
private int vahed;
#Column(name = "coursedep")
private int dep;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "student_course", joinColumns = #JoinColumn(name = "course_id"), inverseJoinColumns = #JoinColumn(name = "student_id"))
private Set<Student> student = new HashSet<Student>();
//Some setter and getter
and this one:
#Entity
#Table(name = "student")
public class Student {
#Id
#Column(name="studid")
private String stId;
#Column(nullable = false, name="studname")
private String studName;
#Column(name="stmajor")
private String stMajor;
#Column(name="stlevel", length=3)
private String stLevel;
#Column(name="stdep")
private int stdep;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "student_course"
,joinColumns = #JoinColumn(name = "student_id")
,inverseJoinColumns = #JoinColumn(name = "course_id")
)
private Set<Course> course = new HashSet<Course>();
//Some setter and getter
After running this code an extra table was created in database(student_course), now I wanna know how can I add extra field in this table like (Grade, Date , and ... (I mean student_course table))
I see some solution but I don't like them, Also I have some problem with them:
First Sample
If you add extra fields on a linked table (STUDENT_COURSE), you have to choose an approach according to skaffman's answer or another as shown bellow.
There is an approach where the linked table (STUDENT_COURSE) behaves like a #Embeddable according to:
#Embeddable
public class JoinedStudentCourse {
// Lets suppose you have added this field
#Column(updatable=false)
private Date joinedDate;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="STUDENT_ID", insertable=false, updatable=false)
private Student student;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="COURSE_ID", insertable=false, updatable=false)
private Course course;
// getter's and setter's
public boolean equals(Object instance) {
if(instance == null)
return false;
if(!(instance instanceof JoinedStudentCourse))
return false;
JoinedStudentCourse other = (JoinedStudentCourse) instance;
if(!(student.getId().equals(other.getStudent().getId()))
return false;
if(!(course.getId().equals(other.getCourse().getId()))
return false;
// ATT: use immutable fields like joinedDate in equals() implementation
if(!(joinedDate.equals(other.getJoinedDate()))
return false;
return true;
}
public int hashcode() {
// hashcode implementation
}
}
So you will have in both Student and Course classes
public class Student {
#CollectionOfElements
#JoinTable(
table=#Table(name="STUDENT_COURSE"),
joinColumns=#JoinColumn(name="STUDENT_ID")
)
private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();
}
public class Course {
#CollectionOfElements
#JoinTable(
table=#Table(name="STUDENT_COURSE"),
joinColumns=#JoinColumn(name="COURSE_ID")
)
private Set<JoinedStudentCourse> joined = new HashSet<JoinedStudentCourse>();
}
remember: #Embeddable class has its lifecycle bound to the owning entity class (Both Student and Course), so take care of it.
advice: Hibernate team suppports these two approachs (#OneToMany (skaffman's answer) or #CollectionsOfElements) due some limitations in #ManyToMany mapping - cascade operation.
regards,
The student_course table is there purely to record the association between the two entities. It is managed by hibernate, and can contain no other data.
The sort of data you want to record needs to be modelled as another entity. Perhaps you could a one-to-many association between Course and StudentResult (which contains the grade, etc), and then a many-to-one association between StdentResult and Student.
Drop the many-to-many, create a class called StudentCourseRelationship and set up one to manys on Student and Course to the StudentCourseRelationship.
You can put all sorts of things on it, like DateEnrolled, DateKickedOut etc. etc.
IMO the many-to-many mapping is a bit of a con.
The accepted answer unfortunately doesn't work for me, hibernate generates the join table in a weird way (all join columns are duplicated). However the variant with dedicated entity for the join table works fine. Here it is described in great detail: http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/