I have make the rest api using Spring boot. It has two entities employee and department. Employee has department object so relation is ManyToOne. It is unidirectional. Department do not have the employees list. When I hit the employees GET method endpoint I am getting com.fasterxml.jackson.databind.exc.InvalidDefinitionException Exception. Please tell me how to solve it.
Employee Class:
#Entity
#Table(name = "employees")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String firstName;
private String lastName;
private Integer salary;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="department_id",referencedColumnName = "id",nullable = false)
private Department department;
Department Class:
#Entity
#Table(name = "departments")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
Employee Service:
#Service
public class EmployeeService {
private final EmployeeRepository employeeRepository;
#Autowired
public EmployeeService(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
public List<Employee> getAllEmployees(){
return employeeRepository.findAll();
}
public Employee getEmployeeById(Integer id){
Optional<Employee> optionalEmployee = employeeRepository.findById(id);
if(optionalEmployee.isEmpty()){
throw new IllegalStateException("Department does not exists");
}
return optionalEmployee.get();
}
public Employee createEmployee(Employee employee){
return employeeRepository.save(employee);
}
Employee Controller:
#RestController
#RequestMapping(path = "/employees")
public class EmployeeController {
private final EmployeeService employeeService;
#Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
#GetMapping
public List<Employee> getAllDepartments(){
return employeeService.getAllEmployees();
}
#PostMapping
public Employee createEmployee(#RequestBody Employee employee){
return employeeService.createEmployee(employee);
}}
You have to put #JsonProperty(access = JsonProperty.Access.WRITE_ONLY) annotation in ManyToOne relationship mapping
Related
So I got these two classes in a Spring Boot/Hibernate project, Doctor:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Doctor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotBlank
private String name;
#OneToMany(mappedBy = "doctor")
#JsonManagedReference
private List<Patient> patients;
}
And the Patient:
#Data
#AllArgsConstructor
#NoArgsConstructor
#Entity
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotBlank
private String name;
#Range(min=1, max=150)
private int age;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "doctor_id")
#JsonBackReference
private Doctor doctor;
#OneToOne
private Receipt receipt;
}
They got the following columns generated :
Doctor : id, name
Patient: id, age,name, doctor_id,receipt_id (dont worry about the receipt part)
The problem is that by updating a Patient (with a regular JPARepository save() method) I can get a updated Patient with findall/findbyId but it disappears from the Doctor's list.
I figure my associations annotations are the problem, any idea how would it work?
!Edit!
here is the Controller for Patient:
#RestController
#RequestMapping("/patient")
public class PatientController {
private PatientService patientService;
public PatientController(PatientService patientService) {
this.patientService = patientService;
}
#GetMapping
public List<Patient> getAllPatients() {
return patientService.getAllPatients();
}
#GetMapping ("/{id}")
public Patient getPatientById(#PathVariable Long id){
return patientService.getPatientById(id);
}
#DeleteMapping("/{id}")
public void deletePatientById(#PathVariable Long id) {
patientService.deletePatientById(id);
}
#PostMapping
public void savePatient(#RequestBody Patient patient) {
patientService.save(patient);
}
#PutMapping("/{id}")
public void updatePatientById(#PathVariable Long id, #RequestBody Patient patient) {
patient.setId(id);
patientService.save(patient);
}
}
The save in the service layer is just the regular JPARepository save method.
I have basicly user and role entities in my project.
#Entity
#Table(name="`User`")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String username;
private String password;
#ManyToMany(fetch = EAGER)
private Collection<Role> roles = new ArrayList<>();
}
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToMany(mappedBy = "roles")
private List<User> users= new ArrayList<User>();
}
This is my controller class
#RestController
#RequestMapping("/api")
#RequiredArgsConstructor
public class UserResource {
private final UserService userService;
#GetMapping("/users")
public ResponseEntity<List<User>> getUsers(){
return ResponseEntity.ok().body(userService.getUsers());
}
And this is my UserManager class
#Service #RequiredArgsConstructor #Transactional #Slf4j
public class UserManager implements UserService {
private final UserRepo userRepo;
private final RoleRepo roleRepo;
#Override
public List<User> getUsers() {
log.info("Fetching all users {}" );
return userRepo.findAll() ;
}
}
When I request http://localhost:8080/api/users, I get wrong data like this
[{"id":5,"name":"Emirhan Ay","username":"emrhn1888","password":"1234","roles":[{"id":1,"name":"ROLE_USER","users":[{"id":5,"name":"Emirhan Ay","username":"emrhn1888","password":"1234","roles":[{"id":1,"name":"ROLE_USER","users":[{"id":5,"name":"Emirhan Ay","username":"emrhn1888","password":"1234","roles":[{"id":1,"name":"ROLE_USER","users":[{"id":5,"name":"Emirhan Ay","username":"emrhn1888","password":"1234","roles":[{"id"}]}]}]}]
But the data saved in the db is like this
Where is my mistake? Thanks in advance for your answers.
You have 'circular dependency'. User has roles, the role has users, etc. You should probably first map entities to DTOS and then maybe add #JsonManagedReference or #JsonBackReference.
Or you can simply put #JsonIgnore on private List<User> users= new ArrayList<User>();
I'm new to the Spring boot JPA and struggling to find out the relationships between multiple entities.
I have a User Entity, a Product Entity, and a Review Entity.
A user has many reviews.
A product has many reviews.
A review has a product and a user.
At the moment, I'm using one-to-many relationships for user&reivew, product&review. However, the error occurred when deleting a review: ERROR: update or delete on table "users" violates foreign key constraint "fkcgy7qjc1r99dp117y9en6lxye" on table "reviews".
My question:
How can I delete a Review Entity without deleting the Product entity and User entity?
Which cascade type should I use?
User Entity:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "user_name")
private String userName;
#Column(name = "email")
private String email;
#Column(name = "password")
private String password;
#JsonManagedReference("reviews")
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "user")
private List<Review> reviews = new ArrayList<>();
//constructor + getter+ setter
Product Entity:
#Entity
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Float price;
#Transient
private Float rate;
private String category;
private String brand;
#JsonManagedReference("reviews")
#JsonIgnore
#OneToMany(mappedBy = "product")
List<Review> reviews = new ArrayList<>();
//constructor + getter+ setter
Review Entity:
#Entity
#Table(name = "reviews")
public class Review {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Float rate;
private String comment;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id",referencedColumnName = "id")
#JsonBackReference("user")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JsonBackReference("product")
#JoinColumn(name = "product_id",referencedColumnName = "id")
private Product product;
//constructor + getter+ setter
User Controller:
#CrossOrigin(origins = "http://localhost:3000")
#RestController
#RequestMapping(path="users/")
public class UserController {
private final UserService userService;
#Autowired
public UserController(UserService userService) {
this.userService = userService;
}
...
#DeleteMapping("{userid}")
public User deleteUser(#PathVariable("userid") Long userid){
return userService.deleteById(userid);
}
}
User service:
#Service
public class UserService {
private final UserRepository userRepository;
private final ReviewRepository reviewRepository;
//dependency injection
#Autowired
public UserService(UserRepository userRepository, ReviewRepository reviewRepository) {
this.userRepository = userRepository;
this.reviewRepository =reviewRepository;
}
...
public User getUserById(Long id){
return userRepository.findById(id).orElseThrow(()->
new UserNotFoundException(id));
}
public User deleteById(Long id){
User user = getUserById(id);
userRepository.delete(user);
return user;
}
}
Simple run:
#SpringBootApplication
public class GroceryShoppingAppApplication {
public static void main(String[] args) {
ConfigurableApplicationContext configurableApplicationContext =
SpringApplication.run(GroceryShoppingAppApplication.class, args);
UserRepository userRepository = configurableApplicationContext.getBean(UserRepository.class);
ProductRepository productRepository =configurableApplicationContext.getBean(ProductRepository.class);
ReviewRepository reviewRepository = configurableApplicationContext.getBean(ReviewRepository.class);
User debbi= new User("Debbi","debbi#gamil.com","password");
Product apple = new Product("Apple",(float)3.40,"Fruit","Gala");
Product milk = new Product("Milk",(float)5.22,"Dairy","Anchor");
Review review1 = new Review(debbi,(float)4.5,"Good taste",apple);
Review review2 = new Review(debbi,(float)5.0,"Good milk",milk);
productRepository.save(apple);
productRepository.save(milk);
userRepository.save(debbi);
reviewRepository.save(review1);
reviewRepository.save(review2);
I think I should not use casacadeType.All because when deleting a user, I shouldn't delete the product in the review. I tried other types, the error still remains. Thus, currently I didn't use any casacadeType and need to save each entity one by one.
Please help me with this.
You are getting an error because the user in the review model does not have a referenced Column value.
Try this code:
#JoinColumn(name = "user_id",referencedColumnName = "id")
I have two Entities. One is UserEntity and other is TaskEntity.
#Entity
#Table(name="user")
public class UserEntity {
#Id
private String userEmail;
private String password;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="user_email")
private List<TaskEntity> tasks;
//getter setter for variables
}
#Entity
#Table(name="task")
public class TaskEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String description;
private String statusDate;
private String status;
//getter setter for variables
}
Now I want to create a new task based on userEmail, so this I am doing as follow in DAO class:
#PersistenceContext
EntityManager em;
public Integer addNewTaskByUserEmail(Task task, String userEmail) {
UserEntity userEntity = em.find(UserEntity.class, userEmail);
TaskEntity taskEntity = new TaskEntity();
taskEntity.setName(task.getName());
taskEntity.setDescription(task.getDescription());
taskEntity.setStatus(task.getStatus());
taskEntity.setStatusDate(task.getDate());
userEntity.getTasks().add(taskEntity);
return taskEntity.getId();
}
But in the return statement of I am getting null in service class. How can I return the auto-generated taskId?
A possible issue is you are not saving a task associated with the user. Save the task and then you might be able to get the taskId.
public Integer addNewTaskByUserEmail(Task task, String userEmail) {
UserEntity userEntity = em.find(UserEntity.class, userEmail);
TaskEntity taskEntity = new TaskEntity();
taskEntity.setName(task.getName());
taskEntity.setDescription(task.getDescription());
taskEntity.setStatus(task.getStatus());
taskEntity.setStatusDate(task.getDate());
em.getTransaction().begin();
em.persist(taskEntity);
em.getTransaction().commit();
userEntity.getTasks().add(taskEntity);
return taskEntity.getId();
}
OR
#Autowired TaskRepository taskRepository
public Integer addNewTaskByUserEmail(Task task, String userEmail) {
UserEntity userEntity = em.find(UserEntity.class, userEmail);
TaskEntity taskEntity = new TaskEntity();
taskEntity.setName(task.getName());
taskEntity.setDescription(task.getDescription());
taskEntity.setStatus(task.getStatus());
taskEntity.setStatusDate(task.getDate());
taskEntity = taskRepository.save(taskEntity)
userEntity.getTasks().add(taskEntity);
return taskEntity.getId();
}
Where TaskRepository will be
#Repository
public interface TaskRepository extends JpaRepository<TaskEntity, Integer>
{
}
is there a chance to fetch join entity with using predicate?
#Entity
public class Student {
#Id
private int id;
private String hobby;
private String favouriteColor;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name = "CLASS_ID", referencedColumnName = "ID")
private Classroom classroom;
}
#Entity
public class Classroom {
#Id
private int id;
private String classRoom;
private List<Student> students;
}
I have some predicate
public class StudentPredicate {
private StudentPredicate() {}
public static Predicate createPredicate(final Student filter) {
QStudent student = QStudent.student;
BooleanBuilder builder = new BooleanBuilder();
if (isNotBlank(filter.getHobby())) {
builder.and(student.hobby.eq(filter.getHobby());
}
if (isNotBlank(filter.getFavouriteColor())) {
builder.and(student.favouriteColor.eq(filter.getFavouriteColor()));
}
return builder.getValue();
}
}
and repository
#Repository
public interface StudentRepository extends CrudRepository<Student, Integer>, QueryDslPredicateExecutor<Student> {
}
and now how can I find all students with fetched classrooms?
studentRepository.findAll(predicate)
How to say to query dsl that these findAll should fetch classroom?
As there's FetchType.LAZY for classroom field in Student class, so here while you call getClassRoom() will actually fetch the related entity from db or either you can use FetchType.EAGER.