How can I let Hibernate auto-create tables in my database? - java

here is the background:
I am using IntelliJ 2021.3 for Spring development.
My current task is to let an embedded H2 database get initialized with a table schema and test data.
My current application should basically support that behaviour. But what am I missing?
Now I did my research googling through many articles that describe the auto-generation of tables in a database with hibernate mostly in the same way: I have to set spring.jpa.hibernate.ddl-auto in the aplication.properties to create.
spring.h2.console.enabled=true
spring.jpa.hibernate.ddl-auto=create
I have set up my Entities like this here:
package com.learning.classes;
import javax.persistence.*;
import java.util.List;
#Entity
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Course_Id")
private long courseId;
#Column(name = "Course_Name")
private String courseName;
#OneToMany(mappedBy = "course", cascade = CascadeType.PERSIST)
private List<Student> student;
public Course() {
}
public Course(long courseId, String courseName) {
this.courseId = courseId;
this.courseName = courseName;
}
public long getCourseId() {
return courseId;
}
public void setCourseId(long courseId) {
this.courseId = courseId;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public List<Student> getStudent() {
return student;
}
public void setStudent(List<Student> student) {
this.student = student;
}
}
and
package com.learning.classes;
import javax.persistence.*;
#Entity
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "Student_Id")
private long studentId;
#Column(name = "First_Name")
private String firstName;
#Column(name = "Last_Name")
private String lastName;
#ManyToOne(fetch = FetchType.EAGER)
private Course course;
public Student() {
}
public Student(long studentId, String firstName, String lastName) {
this.studentId = studentId;
this.firstName = firstName;
this.lastName = lastName;
}
public long getStudentId() {
return studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
}
Now I know that there ain't any data to get inserted when starting the app, but at least I should see my tables when I access my H2 via the console.
Thanks in advance.

So I found out why nothing is working, and to be fully open about it: it was my fault.
I arranged the packages in a way where the Main was not able to read the other packages. The main package was not in the same directory as the others so nothing could happen.
I am writing this so anyone can account on that.
Nevertheless thanks for everyone who has contributed to this topic.
This question is closed.

Related

Spring- Postman too many information with ManyToMany

I have a problem with my Spring application- am practicing ManyToMany Relationship.
I made two Entity- student, and groups for them- everything is good but when am trying to display them in postman i have spaghetti result like :
{"id":4,"lastName":"guzik","groups":[{"id":1,"groupName":"grupka","students":
....(shorter version, its about 1000 lines)
[{"id":1,"lastName":"smith","groups":[{}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}]}{"timestamp":"2022-04-25T08:25:31.387+00:00","status":200,"error":"OK","path":"/api/student/4"}
What am doing wrong ?
enter code here
#RestController
#RequestMapping("/api")
public class MainController{
#Autowired
GroupRepo groupRepo;
#Autowired
StudentRepo studentRepo;
#GetMapping("/student/{id}")
ResponseEntity<?> getStudent(#PathVariable long id){
Optional<Students> student=studentRepo.findById(id);
return ResponseEntity.ok().body(student);
}
#GetMapping("/group/{id}")
ResponseEntity<?> getGroup(#PathVariable long id){
Optional<Groupssss> group=groupRepo.findById(id);
return ResponseEntity.ok().body(group);
}
#GetMapping("/student/{id}/groups")
ResponseEntity<?> studentGroups(#PathVariable long id){
Students student=studentRepo.findById(id).orElseThrow(()-> new UsernameNotFoundException("student not found"));
return ResponseEntity.ok().body(student.getGroups());
}
#PostMapping("/add")
#Transactional
ResponseEntity<?> addStudentToGroup(#RequestHeader long id) throws Exception{
Students student=studentRepo.findById(id)
.orElseThrow(()-> new UsernameNotFoundException("student not found"));
Groupssss group=groupRepo.findByGroupName("grupka").orElseThrow(Exception::new);
student.addGroup(group);
studentRepo.save(student);
return ResponseEntity.ok().build();
}
#Entity
public class Groupssss {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String groupName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "groups")
private Set<Students> students=new HashSet<>();
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setStudents(Set<Students> students) {
this.students = students;
}
public Set<Students> getStudents() {
return students;
}
public String getGroupName() {
return groupName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
#Entity
#Table(name="students")
public class Students {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
long id;
String firstName;
String lastName;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name="student_group",
joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="group_id"))
Set<Groupssss> groups=new HashSet<>();
public Students(){}
public Students(String firstName, String lastName){
this.firstName=firstName;
this.lastName=lastName;
}
public void addGroup(Groupssss group){
this.groups.add(group);
group.getStudents().add(this);
}
public Set<Groupssss> getGroups() {
return groups;
}
public String getLastName() {
return lastName;
}
public String getName() {
return firstName;
}
public void setGroups(Set<Groupssss> groups) {
this.groups = groups;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setName(String firstName) {
this.firstName = firstName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
If you use ManyToMany relationship, It calls the Parent and the Child entity recursively when you try to get List of it. There must be ignorance in child entity like:
In Parent entity you could call #JsonManagedReference
In Child entity you could call #JsonBackReference
Documentation is here:
Bidirectional Relationships
public class User {
public int id;
public String name;
#JsonManagedReference
public List<Item> userItems;
}
public class Item {
public int id;
public String itemName;
#JsonBackReference
public User owner;
}
Or you can use #JsonIgnore on top of the Parent declaration in Child entity. Some of the case it gets worst when you use JsonIgnore.
Documentation is here:
Jackson Ignore Properties
public class MyDto {
private String stringValue;
#JsonIgnore
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
Or use #JsonIgnoreProperties when you need to ignore many fields
#JsonIgnoreProperties(value = { "intValue" })
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;
public MyDto() {
super();
}
}
!Enjoy

H2 database columns and values don't converge JAVA Spring

I am new one at java and spring framework and have this problem. I have class, which has fields, that should be columns in H2. It looks like this:
package com.bankapp.bankwebapplication.models;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
#Entity
public class PersonClient implements Client {
#Id
#Column(nullable = false, unique = true)
private Long id;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
#Column(nullable = false)
private String firstName;
public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
#Column(nullable = false)
private String lastName;
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
#Column(nullable = false)
private String address;
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
#Column
private String workPhone;
public String getWorkPhone() { return workPhone; }
public void setWorkPhone(String workPhone) { this.workPhone = workPhone; }
#Column
private String homePhone;
public String getHomePhone() { return homePhone; }
public void setHomePhone(String homePhone) { this.homePhone = homePhone; }
#Override
public void getDetails() {
}
}
Also, I have data.sql file that inserts 1 value into that table:
INSERT INTO person_client VALUES (1, 'firstName', 'lastName', 'paper street', '+123123', '+321321')
So, the problem is that it looks like this:
Why? And how can I fix that?
Always specify the target columns in INSERT statements:
INSERT INTO person_client
(id, first_name, last_name, address, home_phone, work_phone)
VALUES
(1, 'firstName', 'lastName', 'paper street', '+123123', '+321321')
If you don't specify the target columns, the values are matched by position and apparently the columns are created in a different order than you think they are.
agree with #a_horse_with_no_name, if you not specify column names it will insert based on the position/index. And all your java variables are in string that is the reason it does't throw any classcast exception.

How to solve the targeting an unmapped class error from hibernate?

I'm trying to map 2 entities (Course and Student), I have 2 Java classes and 2 MySQL tables, having a ManyToMany relationship. I created the junction table and java class Enrolment (as I want extra information such as the date of enrolment of a student to a course).
I'm trying to insert data using hibernate in this Enrolments table in the MySQL but I keep getting errors. Here are my POJO classes:
Course class:
#Entity
#Table(name = "course")
public class Course {
private int id;
#Column(name = "chapter_id")
private int chapterId;;
#Column(name = "name")
private String title;
#Column(name = "teacher_user_id")
private int teacherId;
#OneToMany(targetEntity=Enrolment.class, mappedBy="course", fetch=FetchType.LAZY)
// #JoinTable(name = "enrolment",
// joinColumns = #JoinColumn(name = "course_id"),
// inverseJoinColumns = #JoinColumn(name = "student_user_id"))
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Course(){}
public Course(int id, int chapterId, String title, int teacherId) {
super();
this.id = id;
this.chapterId = chapterId;
this.title = title;
this.teacherId = teacherId;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getChapterId() {
return chapterId;
}
public void setChapterId(int chapterId) {
this.chapterId = chapterId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getTeacherId() {
return teacherId;
}
public void setTeacherId(int teacherId) {
this.teacherId = teacherId;
}
#OneToMany(mappedBy = "course")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
Student class (this class is inherited from User parent class, I will attach User Class down below as well. In the database there are different tables as well: User and then Student and Teacher that inherit User parent entity):
#Entity
#Table(name = "student")
#PrimaryKeyJoinColumn(name = "user_id")
#OnDelete(action = OnDeleteAction.CASCADE)
public class Student extends User {
private int grade;
private List<Enrolment> enrolments = new ArrayList<Enrolment>();
public Student(){}
public Student(String fname, String lname, String email, String password, String address, String phone,
int userType, int grade, boolean isAdmin)
{
super(fname, lname, email, password, address, phone, userType, isAdmin);
this.grade=grade;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public void setEnrolments(List<Enrolment> courses) {
this.enrolments = courses;
}
#OneToMany(mappedBy = "student")
public List<Enrolment> getEnrolments() {
return enrolments;
}
public void addCourse(Enrolment course) {
this.enrolments.add(course);
}
public void addEnrolment(Enrolment enrolment) {
this.enrolments.add(enrolment);
}
}
User Class:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String firstname;
private String lastname;
private String email;
private String password;
private String address;
private String phone;
#Column(name = "user_type_id")
private int userType;
#Column(name = "is_admin")
private boolean isAdmin;
public User(String fname, String lname, String email, String password, String address, String phone,
int userType, boolean isAdmin) {
//super();
this.firstname = fname;
this.lastname = lname;
this.email = email;
this.password = password;
this.address = address;
this.phone = phone;
this.userType = userType;
this.isAdmin = isAdmin;
}
public User() {}
//getters & setters
And finally this is the Enrolment class:
#Entity
#Table(name = "enrolment")
public class Enrolment {
private int id;
private Student user;
private Course course;
#Column(name = "enrolment_date")
private Date enrolmentDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "student_user_id")
public User getUser() {
return user;
}
public void setUser(Student user) {
this.user = user;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "course_id")
public Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
public Date getEnrolmentDate() {
return enrolmentDate;
}
public void setEnrolmentDate(Date enrolmentDate) {
this.enrolmentDate = enrolmentDate;
}
So I'm trying to read a course and a student from database and insert the information in this Enrolment table but it gives errors since trying to read a Course. Here is the DAO method:
#SuppressWarnings("deprecation")
#Transactional
public List<Course> getCoursesOfChapter(int chapterId) {
Configuration con = new Configuration().configure("hibernate.cfg.xml").addAnnotatedClass(Course.class);
SessionFactory sf = con.buildSessionFactory();
Session session = sf.openSession();
Transaction tx = session.beginTransaction();
Query query = session.createQuery("from Course where chapter_id = :chapterId");
query.setParameter("chapterId",chapterId);
// List list = query.list();
tx.commit();
return (List<Course>) query.list();
It throws the error at the session factory building:
Exception in thread "main" org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: models.Course.enrolments[models.Enrolment]
at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:1255)
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:808)
at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:733)
at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:54)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1696)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1664)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:287)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:84)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:474)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:85)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)
at dao.CourseDAO.getCourse(CourseDAO.java:52)
at webapp.Main.main(Main.java:132)
Finally, this is my call:
CourseDAO cdao = new CourseDAO();
Course course = cdao.getCourse(1);
I've tried playing with the annotations, make them ManyToMany instead of ManyToOne. I tried to map the User class instead of Student but still didn't work. I tried to make it without the junction class of Enrolment and just generate it without having an actual class for it but still didn't work (as I had to work with 2 session.save() methods one after the other which also gave some error that I couldn't solve). Probably it's a small thing that I'm missing here but I just can't figure it out, sorry for too long code but I really need to solve it fast. If you read through here, I really thank you!
So my question is: Am I missing something here from these mappings and annotations or I should change the structure of my classes?
Boiling down a problem to the bare minimum greatly helps others help you. Here are simpler versions of your student, course and enrollment classes that can be unit tested easily. The many-to-many association between course and student is separated into two many-to-one associations from Enrollment. Note that the associations are bidirectional and that the many side is mapped by the one side. Student cascades persistence operations to Enrollment, which reflects how schools normally work: students enroll in courses, not the other way around.
Course.java
#Entity
public class Course {
#Id
#GeneratedValue
private Long id;
private String title;
#OneToMany(mappedBy = "course")
private List<Enrollment> enrollments;
Course() {
}
Course(String title) {
this.title = title;
this.enrollments = new ArrayList<>();
}
void add(Enrollment enrollment) {
enrollments.add(enrollment);
}
Long getId() {
return id;
}
List<Enrollment> getEnrollments() {
return enrollments;
}
}
Student.java
#Entity
public class Student {
#Id
#GeneratedValue
private Long id;
private String name;
#OneToMany(mappedBy = "student", cascade = ALL, orphanRemoval = true)
private List<Enrollment> enrollments;
Student() {
}
Student(String name) {
this.name = name;
this.enrollments = new ArrayList<>();
}
void enroll(Course course) {
enrollments.add(new Enrollment(course, this));
}
}
Enrollment.java
#Entity
public class Enrollment {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private Course course;
#ManyToOne
private Student student;
Enrollment() {
}
Enrollment(Course course, Student student) {
this.course = course;
this.student = student;
course.add(this);
}
}
The test case below checks that the entities are mapped and associated correctly. You can run it with Spring Boot.
SchoolTest.java
#RunWith(SpringRunner.class)
#SpringBootTest
#Transactional
public class SchoolTest {
#Autowired
private CourseRepository courseRepository;
#Autowired
private StudentRepository studentRepository;
#Test
public void run() {
Course course = courseRepository.save(new Course("cs_101"));
int studentCount = 3;
for (int i = 1; i <= studentCount; i++) {
Student student = new Student("student_" + i);
student.enroll(course);
studentRepository.save(student);
}
// push changes to the database and clear the existing entities
// to make the subsequent operations load from the database
entityManager.flush();
entityManager.clear();
Optional<Course> savedCourse = courseRepository.findById(course.getId());
assertTrue(savedCourse.isPresent());
assertEquals(studentCount, savedCourse.get().getEnrollments().size());
}
}
As the warning said, your Enrollment is not registered in Hibernate. If you really don't need it. Please use transient annotation. read more here

How to update one field in my Entity using JPA Repository

I have Entity with 3 fields: id, lastname and phoneNumber. I want to create method which works for update all fields or only one or two.
I use Hibernate and JPA Repository.
When I try to update all fields everything works well but when for example i want to update only lastname without changing of phoneNumber I have in output null insted of old phoneNumber.
Here is my method from Controller:
#PutMapping("/students/update/{id}")
public String updateStudentById(#ModelAttribute Student student, #ModelAttribute StudentDetails studentDetails,
String lastname, String phoneNumber,
#PathVariable Long id) {
Optional<Student> resultOptional = studentRepository.findById(id);
//Student result =resultOptional.get();
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber()); result.getStudentDetails().setLastname(studentDetails.getLastname());
studentRepository.save(result);
});
return "Student updated";
}
The class for update:
#DynamicUpdate
#Entity
public class StudentDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name="lastname")
private String lastname;
#Column(name="phone_number")
private String phoneNumber;
public StudentDetails() {
}
public StudentDetails(Long id, String lastname, String phoneNumber) {
this.id = id;
this.lastname = lastname;
this.phoneNumber = phoneNumber;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
The class which has relation with StudentDetails:
#Entity
#Table(name = "student")
#DynamicUpdate
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "email")
private String email;
//#OneToMany(mappedBy = "student")
#ManyToMany
#JoinTable(name="course_student",joinColumns = #JoinColumn(name="student_id"),
inverseJoinColumns = #JoinColumn(name="course_id"))
private List<Courses> courses;
#OneToOne(cascade = CascadeType.ALL)
// #JoinColumn(name="studen/_details_id") // with this we have dobule student_details column
private StudentDetails studentDetails;
public List<Courses> getCourses() {
return courses;
}
public void setCourses(List<Courses> courses) {
this.courses = courses;
}
public StudentDetails getStudentDetails() {
return studentDetails;
}
public void setStudentDetails(StudentDetails studentDetails) {
this.studentDetails = studentDetails;
}
// Methods for StudentViewController
public String getLastname(){
return studentDetails.getLastname();
}
public String getPhoneNumber(){
return studentDetails.getPhoneNumber();
}
public Student() {
}
public Student(String name, String email, StudentDetails studentDetails) {
// this.id = id;
this.name = name;
this.email = email;
this.studentDetails = studentDetails;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
#Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
'}';
}
}
I was looking for solution and I added #DynamicUpdate but still it doesn't work.
Your code works properly. When you only provide lastName parameter in your request, then the phoneNumber parameter will be mapped to null so you override the phoneNumer property in your entity with this null value.
Change the code in the following way:
resultOptional.ifPresent((Student result) -> {
if(studentDetails.getPhoneNumber()!=null) {
result.getStudentDetails().setPhoneNumber(studentDetails.getPhoneNumber());
}
if(studentDetails.getLastname()!=null) {
result.getStudentDetails().setLastname(studentDetails.getLastname());
}
studentRepository.save(result);
});
Unfortunately it raises an other problem: How will you delete these fields? (How can you set them explicitly to null? )
A possible solution if you check for the "" (empty string) and set the property to null if the parameter is empty string.
It will be a quite messy code anyway...
You should consider using the Spring Data Rest package. It automatically creates all of the standard REST endpoints for your entities and handles all of these PUT/PATCH/POST/DELETE issues out of the box.
why don't you just set the params of your request in you setters?
resultOptional.ifPresent((Student result) -> {
result.getStudentDetails().setPhoneNumber(phoneNumber);
result.getStudentDetails().setLastname(lastname);
studentRepository.save(result);
});
You forget set #OneToOne mapping in StudentDetails - StudentDetails also need field of type Student which will be annotated #OneToOne.
Also you have to ensure, that all of entity fields will be filled - read more about fetch types.

how to write java hibernate many to many relationship retrieving query

I have two tables as specified below. I want to write a query to get all the contacts of a particular group.(According to group id). Please help me.
Thanks in advance.
1.this contacts table , which has many has many to many relationship with contact groups table.
#Entity
#Table(name="contacts")
public class Contacts implements Serializable {
private Long id;
private String userId;
private String emailId;
private Set<ContactGroups> contactGroups;
private String firstName;
private String lastName;
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name="user_id")
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
#Column(name="email_id")
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
#ManyToMany(targetEntity = ContactGroups.class, cascade = {CascadeType.ALL},fetch=FetchType.EAGER)
#JoinTable(name="contact_group",
joinColumns=#JoinColumn(name="c_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="g_id", referencedColumnName="id")
)
public Set<ContactGroups> getContactGroups() {
return contactGroups;
}
public void setContactGroups(Set<ContactGroups> contactGroups) {
this.contactGroups = contactGroups;
}
#Column(name="first_name")
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
#Column(name="last_name")
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
#Entity
#Table(name="contact_groups")
public class ContactGroups implements Serializable{
private Long id;
private String groupName;
private String userName;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name="user_name")
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
#Column(name="group_name")
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
}
select c from Contacts c
inner join c.contactGroups group
where group.id = :groupId
But everything would be simpler if
you named your entities ContactGroup and Contact (without the final s)
you mapped the association as a bidirectional one. It would allow getting the ContactGroup by ID, and just call getContacts() to get its contacts.
select c from Contacts c, ContactGroups g
where c.contactGroups.id = g.id
and g.id = 'whatever id you want'

Categories