How to join two tables which has many to one relationship? - java

I have two tables and they got many to one relationship. Below entity creates a join column called school_id. Problem is project manager said never use join column but use join table.
I think when I use join table it will create a new table like "school_student_relationship" and will store relationship with this table.Why do I need to use join table instead of join column? Is it about performance or something else?
#Entity
#Table(name = "student")
public class Student implements Serializable {
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "school_id")
private School school;
}
#Entity
#Table(name = "school")
public class School implements Serializable {
#Id
#Column(name = "school_id")
private String schoolId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "school", fetch = FetchType.LAZY)
private List<Student> students= new ArrayList<>();
}

Related

Spring data jpa operation between table views

in my spring data application i have two TABLE VIEW mapped:
the first view
#Entity
#Immutable
#Table(name="VD_CONT")
#NamedQuery(name="VdContr.findAll", query="SELECT d FROM VdContr d")
public class VdContr {
#Id
#Column(name="CONTR_ID")
private Long id;
#Column(name="CF")
private String cf;
#OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="vdcontr")
private List<VdArr> vdArr;
}
and the second view
#Entity
#Immutable
#Table(name="VD_ARR")
#NamedQuery(name="VdArr.findAll", query="SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name="ARR_ID")
private Long id;
#Column(name="FK_CONTR_ID")
private Long fkContrId;
#ManyToOne(fetch=FetchType.LAZY)
public VdContr vdcontr;
}
If i put a relationship "OneToMany" and "ManyToOne" (1, first view : many, second view), i receive errors.
My question is: is it possibile create a relationship between two table view?
you need to add a #JoinColumn to VdContr.
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "vdcontr_id", nullable = false)
In general, views are mapped in the same way as tables.
By looking at your classes, the problem is that Hibernate cannot find the correct join column. You need to specify it.
Also, in your VdArr you should delete the fkContrId, because hibernate will need to use this column to map the VdContr relationship.
By looking at your code, the join column is FK_CONTR_ID, so you need to specify it by using #JoinColumn.
#Entity
#Immutable
#Table(name = "VD_ARR")
#NamedQuery(name = "VdArr.findAll", query = "SELECT v FROM VdArr v")
public class VdArr {
#Id
#Column(name = "ARR_ID")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_CONTR_ID")
public VdContr vdcontr;
}

Spring boot: JPA incorrectly adds where clause

I have three entity classes; Student, Subject and StudentSubject.
Student has one to many relation on StudentSubject, and Subject also has one to many relation on StudentSubject.
Student class
#Entity
#Getter
#Setter
public class Student {
#Id
private String email;
#OneToMany(mappedBy = "student")
#JsonManagedReference
private List<StudentSubject> subjects;
//more elements
}
Subject class
#Entity
#Getter
#Setter
public class Subject {
#Id
#GeneratedValue
private Long id;
#ManyToOne
#JsonBackReference
private Teacher teacher;
#OneToMany(mappedBy = "student")
#JsonManagedReference
private List<StudentSubject> students;
//more elements
}
StudentSubject class
#Entity
#IdClass(StudentSubjectId.class)
#Getter
#Setter
public class StudentSubject implements Serializable {
//Primary keys
#Id
#Column(name = "subject_id")
Long subjectId;
#Id
#Column(name = "student_email")
String studentEmail;
String uid;
#ManyToOne
#JsonBackReference
#JoinColumn(name = "subject_id", insertable = false, updatable = false)
private Subject subject;
#ManyToOne
#JsonBackReference
#JoinColumn(name = "student_email", insertable = false, updatable = false)
private Student student;
}
I have 3 classes, and not 2, because there are attributes specific to each student subject pair. Hence this arrangement.
When I read a subject from repository, as such
Subject subject = subjectRepository.findByNameAndTeacher(subjectName, teacher);
subject.getStudents();
all it's details are correct, except for list of students. It is always empty.(checked this by adding breakpoint)
The queries that are executed by Hibernate/JPA are,
To get subject(?)
select
subject0_.id as id1_3_,
subject0_.name as name2_3_,
subject0_.teacher_email as teacher_3_3_
from
subject subject0_
left outer join
teacher teacher1_
on subject0_.teacher_email = teacher1_.email
where
subject0_.name =?
and teacher1_.email =?
To select student list(?)
select
students0_.student_email as student_1_2_0_,
students0_.subject_id as subject_2_2_0_,
students0_.student_email as student_1_2_1_,
students0_.subject_id as subject_2_2_1_,
students0_.uid as uid3_2_1_,
subject1_.id as id1_3_2_,
subject1_.name as name2_3_2_,
subject1_.teacher_email as teacher_3_3_2_,
teacher2_.email as email1_5_3_,
teacher2_.name as name2_5_3_
from
student_subject students0_
left outer join
subject subject1_
on students0_.subject_id = subject1_.id
left outer join
teacher teacher2_
on subject1_.teacher_email = teacher2_.email
where
students0_.student_email =?
and some more.
I think the issue here is that the last where clause is incorrectly added, and common attributes in tables are not shown once. How do I fix this?
Your mapping has a typo. In Subject class, it should be #OneToMany(mappedBy = "subject") instead of mappedBy="student" hence your wrong where clause.
This is the reason it is using
where students0_.student_email =?
instead of
where students0_.subject_id =? as it thinks the way to get to students from subject is through student_email column as indicated by your mapping.
You have not specified fetch type. This should fix it.
#OneToMany(mappedBy = "student", fetch = FetchType.EAGER)
#JsonManagedReference
private List<StudentSubject> students;

Hibernate Joined inheritance: Query records by ID ONLY from child table

I have a hierarchy of classes like next:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name="Person")
public class Person implements Serializable{
#Id
#Column(name = "PersonID", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
}
#Entity
#Table(name="Student")
#PrimaryKeyJoinColumn(name="PersonID")
public class Student extends Person{
}
#Entity
#Table(name="Bachelor")
#PrimaryKeyJoinColumn(name="PersonID")
public class Bachelor extends Student{
#OneToMany(mappedBy = "bachelor", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<BachelorExam> exams;
}
#Entity
#Table(name="Exam")
public class Exam implements Serializable {
#Id
#Column(name = "ExamID", unique = true, nullable = false)
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
}
#Entity
#Table(name="BachelorExam")
public class BachelorExam implements Serializable {
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "PersonID_FK", referencedColumnName = "PersonID")
private Bachelor bachelor;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "ExamID_FK", referencedColumnName = "ExamID")
private Exam exam;
}
I want to get users (regular student or bachelor) from appropriate table by ID using generic method like next:
<T extends Person> T getStudentById(Long studentId);
This method can be something like
public <T extends Person> T getUserById(Long personId) {
List<Class<?>> studentTypes = new LinkedList<>();
studentTypes.add(Student.class);
studentTypes.add(Bachelor.class);
for (Class aClass : studenTypes) {
List<T> results = getDatabaseProvider().getDataFromDatabase(String.format("select u %s as u " +
"where u.userId = '%d'", aClass.getName(), userId));
return results.get(0);
}
}
The problem is that when I save a bachelor object in database, hibernate also saves bachelor's id to 'Student' table so when I get data from database going through whole list of inherited classes, query returns record from table Bachelor and also record from table Student, because both contain required student ID.
I've tried to use InheritanceType Table_Per_class but in this case hibernate doesn't create foreign key for bachelor in table BachelorExam.
How can I receive only records from table Bachelor by id?
Thanks!

how to spring jpa hibernate create Entity that has many to one but without foreign key

I got two tables that has the many to one relationship, but they do not have a foreign key.
Like the Student are many, and the Teacher is the one,
the entities for example:
#Entity
#Table(name = "student")
class Student {
#Column(name = "TeacherName")
private String teacherName;
#ManyToOne
private Teacher teacher
}
#Entity
#Table(name = "teacher")
class Teacher {
private String name;
}
When I query the students, the sql is :
select * from Student as st INNER JOIN Teacher as tcr ON st.TeacherName = tcr.name;
I found the #ManyToOne can not work and it looks like it needs a foreign key. The table can not provide such though.
Can someone tell me how to configure the entity?
You would have to use the following mapping:
public class Teacher {
#OneToMany(mappedBy = "teacher")
private Set<Student> students;
#Column(name = "name")
private String name;
}
public class Student {
#ManyToOne
#JoinColumn(name = "teacherName", referencedColumnName = "name")
private Teacher teacher;
}
Then in HQL:
select s from Student s INNER JOIN s.teacher t where t.name = :name
Alternatively
If you want to stay with your current mapping and then you would need to use the 'old' style of join in hql to achieve joining by non-foreign-key columns:
select s from Student s, Teacher t where t.name = s.teacherName and t.name = :name

Persistence: ManyToMany to same class

I am trying to set a ManyToMany annotation on my code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany(mappedBy = "promotors", cascade = CascadeType.PERSIST)
#JoinTable(name = "user_student",
joinColumns=#JoinColumn(name="promotor_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="student_id", referencedColumnName="id")
)
private Collection<User> students;
#ManyToMany
private Collection<User> promotors;
}
However every time i try to run the application and the db gets generated, it creates 2 tables for the ManyToMany, 1 new table that is called user_student as i defined beneath, but it also creates a second table user_user which i didn't define but is generated from the promotors.
It's correct you cannot map many to many relationship on one table. As you have only one possible column to map it to. What enforces one to one relationship.
You always have to have mapping table. Its also most convenient way to map many to many relationships on different tables.
Apparently, i didn't define the second ManyToMany correctly, this is the correct code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany
#JoinTable(name = "user_student",
joinColumns={#JoinColumn(name="promotor_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="student_id", referencedColumnName="id")}
)
private Collection<User> students;
#ManyToMany(mappedBy = "students", cascade = CascadeType.PERSIST)
private Collection<User> promotors;
}

Categories