I have two entities called Student and Subject. They are stored in tables in the following format
student_id
name
grade
1
John
1
subject_id
name
1
English
2
Math
subject_id
student_id
mark
1
1
75
2
1
75
**Student:**
#Table(name = "student")
#Data
public class Student {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "grade")
private int grade;
//getters and setters left out for this example
}
**Subject:**
#Table(name = "subject")
#Data
public class Subject {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
//getters and setters left out for this example
}
**StudentRepository:**
public interface StudentRepository extends JpaRepository<Student, Long> {
}
How do I make it so that everytime I add a student using a StudentController, the subjects are automatically added to the student.
Create the third entity for the third table, create the student object and the subject object . put it in the third entity object, create the third repository and save that, all three tables will be updated together. Just make sure your relationships are correctly mentioned and you are done.
Update your Student entity to encapsulate Subject.
The idea is to explicitly define relationship between Student and Subject , and further leverag Cascade to propagate changes :
#Table(name = "student")
#Data
public class Student {
#Id
#GeneratedValue(strategy = IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#Column(name = "grade")
private int grade;
#OneToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.MERGE}, orphanRemoval = true)
#JoinColumn(name = "SUBJECT_ID")
Subject subject;
}
Note : You need to make sure that you populate Subject when storing Student.
For more clarity , explore the examples presented here : https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/
Given two entities Employee and EmployeeAddress, I am trying to implement the DTO pattern - mainly because my IDE shows a warning when using an entity as parameter in my REST controller.
In this context, I have a question regarding how to deal with the OneToOne relationship between these two entities:
The parent entity:
#Entity
#Table
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_address_id")
private EmployeeAddress employeeAddress;
}
The child entity:
#Entity
#Table
public class EmployeeAddress{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String postalCode;
#OneToOne(mappedBy="employeeAddress")
private Employee employee;
}
My first idea was to introduce the following two DTOs:
#Getter
#Setter
public class EmployeeDTO {
private Long id;
private String firstName;
private String lastName;
private EmployeeAddressDTO employeeAddress;
}
#Getter
#Setter
public class EmployeeAddressDTO {
private Long id;
private String street;
private String postalCode;
}
This doesn't seem to work, however: I have to replace the EmployeeAddressDTO inside my EmployeeDTO with the actual entity EmployeeAddress in order for this to work. This, however, seems a bit contradicting to me - why would I create an EmployeeDTO only for it to contain an entity?
So, I wonder, how do I deal with this OneToOne relationship in my DTO?
Do I have to create an EmployeeDTO as:
#Getter
#Setter
public class EmployeeDTO {
private Long id;
private String firstName;
private String lastName;
private String street;
private String postalCode;
}
Would this be the right approach?
why would I create an EmployeeDTO only for it to contain an entity?, that is the whole question about DTOs.
In small or toy apps there may even no point for DTOs anyway. When your app is growing, things are different. It is good practice to separate the persistence (entities) from the presentation (or API, or interface to the outside world).
Entities defining the app data model (the data that needs your app to do the work), DTOs are what you want to export (in a controlled way) to the world.
Of course, at the beginning it is mostly the same data.
I'm trying to fetch all Employees with a relational table EmployeeCourse and to see how many Courses the employee has. When I use employeeRepo.findAll() method inside my Controller I got JSON result like this:
[{"firstName":"Pera","lastName":"Peric","employeeCourses":[],"employeeId":1},{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":{"firstName":"Marko","lastName":"Markovic","employeeCourses":[{"id":1,"employee":
A lot of rows were removed for brevity and other entities employees were not loaded.
my Entities look like this:
#Entity
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Integer id;
private String firstName;
private String lastName;
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL)
Set<EmployeeCourse> employeeCourses;
// getters and setters implemented
// dont have hashCode() and equals overridden
}
#Entity
public class EmployeeCourse {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "employee_id")
Employee employee;
#ManyToOne
#JoinColumn(name = "course_id")
Course course;
private Date startDate;
private Date endDate;
//have getter and setters and overridden hashCode() and equals
}
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "course_id")
private Integer id;
private String name;
private String description;
#OneToMany(mappedBy = "course")
Set<EmployeeCourse> employeeCourses;
}
Why do I have this double results, and how to fetch them?
It is pretty hard to read your JSON data, but I guess that with double results I assume you are talking about the huge amount of Employee data in your JSON response. This is due to the bi-directional relationships:
Employee <--> EmployeeCourse: Employee has a reference to Set<EmployeeCourse> and EmployeeCourse has a reference to Employee.
EmployeeCourse <--> Course: EmployeeCourse has a reference to Course and Course has a reference to Set<EmployeeCourse>.
This means that when Jackson serializes your Employees it will serialize all this duplicated information.
Having said that you have now three options:
You turn the bi-directional relationship into a uni-directional one, by, for example, removing the employee attribute in EmployeeCourse.
If for whatever reason 1. is not possible, then you might use #JsonIgnore so that Jackson does not serialize them.
If 2. is also not possible you may consider using DTOs. Using DTOs instead of plain Entities is usually advisable when returning data via a Controller. With DTOs, you clearly define whatever you want to include in the response to the caller. You can even reorganize your model so that it fits better the needs of the clients. You decouple your inner model and the model your API consumers know, making it possible to rework your inner model and still keep the same public model.
Just put #JsonIgnor on top of the object which you don't want to expand. Ex. in course if you don't want to expand the result of employeeCourses then just do something like this
#OneToMany(mappedBy = "course")
#JsonIgnore
Set<EmployeeCourse> employeeCourses;
I have a Entity Student which is #ManyToOne with another Entity School, Where School is pre-existing in the database and is fixed.
Entity User:
#Data
#Entity(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String username;
#ManyToOne
private School school;
}
Entity School:
#Data
#Entity(name = "school")
public class School {
#Id
#Column(unique = true, nullable = false)
private int id;
#Column(nullable = false)
private String name;
private String shorten;
#JsonProperty(value = "logo_url")
private String logoUrl;
private float longitude;
private float latitude;
#Column(nullable = false)
private boolean opened;
}
When adding a user, I POST the following json from Postman:
{
"username": "abcd",
"school_id": 2
}
Then,
School school = new School();
school.setId(2); //"school_id" above
User user = new User();
user.setUsername("abcd");
user.setSchool(school);
userRepository.save(user);
Because I think that to add a new user, only the School id is enough, and no other School parameters are required. But every time I run, it will run the select statement to select all fields of School by id before save().
My question is: how to remove this unnecessary operation so that before the save(), there is no need to select? (I know that custom sql statements can be implemented, but I feel like this will break the object orientation of JPA)
use below annotation on entity class
#SelectBeforeUpdate(value=false)
You need to use getReference method to avoid this issue
School school = entityManager.getReference(School.class, 2);
If you are using Spring Data JPA, this method is exposed as getOne on the repository.
See How do find and getReference EntityManager methods work when using JPA and Hibernate