This question is kindly related to a previous one here:
getting null while posting data
I tryed a suggested answer, imported lombok and created a DTO class as follows:
package com.example.dto;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
#Data
public class TownDTO {
public String name;
public String regionid;
}
Table towns is related to a regions table in db. While I try to save data I am getting a following exception:
not-null property references a null or transient value : com.example.model.Towns.regionid
Here is my model class
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "towns")
public class Towns {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name")
private String name;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "regionid", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Regions regionid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Regions getRegionid() {
return regionid;
}
public void setRegionid(Regions regionid) {
this.regionid = regionid;
}
}
And repositories are as follows.
Towns repository
package com.example.repository;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import com.example.model.Towns;
import com.example.model.Regions;
public interface TownsRepository extends JpaRepository<Towns, Integer> {
List<Towns> findByNameContaining(String name);
Page<Regions> findByregionid(Integer regionid, Pageable pageable);
Optional<Regions> findByregionidAndId(Integer regionId, Integer id);
}
regions repository
package com.example.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Regions;
public interface RegionsRepository extends JpaRepository<Regions, Integer> {
List<Regions> findByNameContaining(String name);
}
And finaly the controller method:
#PostMapping("/add")
public ResponseEntity<TownDTO> createPost(#RequestBody TownDTO townDto) {
// convert DTO to entity
Towns townRequest = modelMapper.map(townDto, Towns.class);
//System.out.println(townDto.regionid);
//System.out.println(townRequest.getName());
Towns town = townsrepository.save(townRequest);
// convert entity to DTO
TownDTO townResponse = modelMapper.map(town, TownDTO.class);
return new ResponseEntity<TownDTO>(townResponse, HttpStatus.CREATED);
}
I am able to print out townDTO.regionid and value is correct 2 in my case. But request is still null. Here is a request from postman
{
"name": "test",
"regionid": "2"
}
What am I missing right now?
Removed #jsonignore and added getters and setters to DTO
package com.example.dto;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
#Data
public class TownDTO {
public String name;
public String regionid;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRegionid() {
return regionid;
}
public void setRegionid(String regionid) {
this.regionid = regionid;
}
}
Nothin had change. Error is the same
Your TownDTO contains String regionid whereas your Model class it's Region Object. So moddlemapper can't map.
//DTO
public String regionid;
//Model
private Regions regionid;
Related
This is probebly a repetition to my yesturday's question here
Can't save data to a database with DTO
I can get a request as DTO. But then I convert it to a model class back I get value of regionid column as null.
My DTO Class
package com.example.dto;
import java.util.HashSet;
import java.util.Set;
import lombok.Data;
#Data
public class TownDTO {
public String name;
public String regionid;
}
My model:
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "towns")
public class Towns {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "name")
private String name;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "regionid", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
private Regions regionid;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Regions getRegionid() {
return regionid;
}
public void setRegionid(Regions regionid) {
this.regionid = regionid;
}
}
Controller:
package com.example.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.dto.TownDTO;
import com.example.model.Regions;
import com.example.model.Towns;
import com.example.repository.TownsRepository;
import com.example.repository.RegionsRepository;
import java.util.stream.Collectors;
import org.modelmapper.Conditions;
import org.modelmapper.ModelMapper;
#CrossOrigin(origins = "http://localhost:8081")
#RestController
#RequestMapping("/towns")
public class TownsController {
#Autowired
TownsRepository townsrepository;
#Autowired
RegionsRepository regionsrepository;
#Autowired
private ModelMapper modelMapper;
#GetMapping("/list")
public ResponseEntity<List<Towns>> getAllTowns(#RequestParam(required = false) String name) {
try {
List<Towns> towns = new ArrayList<Towns>();
if (name == null)
townsrepository.findAll().forEach(towns::add);
else
townsrepository.findByNameContaining(name).forEach(towns::add);
if (towns.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(towns, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#GetMapping("/list/{id}")
public ResponseEntity<Towns> getTownById(#PathVariable("id") int id) {
Optional<Towns> townData = townsrepository.findById(id);
if (townData.isPresent()) {
return new ResponseEntity<>(townData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
#PostMapping("/add")
public ResponseEntity<TownDTO> createPost(#RequestBody TownDTO townDto) {
// convert DTO to entity
Towns townRequest = modelMapper.map(townDto, Towns.class);
System.out.println("reg");
System.out.println(townRequest.getName());
System.out.println(townRequest.getRegionid());
//Regions rid=regionsrepository.findById(2).get();
// townRequest.setRegionid(townDto.regionid);
Towns town = townsrepository.save(townRequest);
// convert entity to DTO
TownDTO townResponse = modelMapper.map(town, TownDTO.class);
return new ResponseEntity<TownDTO>(townResponse, HttpStatus.CREATED);
}
/*
#PostMapping("/addt")
public ResponseEntity<Towns> createtown2(#RequestBody Towns town) {
try {
Towns _town = townsrepository
.save(town);
// Towns _town = townsrepository .save(new Towns( town.getName(), regionsrepository.findByRegionId(town.getRegionid())));
return new ResponseEntity<>(_town, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
System.out.println("region");
System.out.println(town.getRegionid());
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
*/
#PutMapping("/edit/{id}")
/*
public ResponseEntity<Towns> updateTown(#PathVariable("id") int id, #RequestBody Towns town) {
Optional<Towns> townData = townsrepository.findById(id);
if (townData.isPresent()) {
Towns _town = townData.get();
_town.setName(town.getName());
_town.setRegionid(town.getRegionid());
return new ResponseEntity<>(townsrepository.save(_town), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
*/
#DeleteMapping("/delete/{id}")
public ResponseEntity<HttpStatus> deleteTown(#PathVariable("id") int id) {
try {
townsrepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Try to set id to an entety.
Regions rgid=regionsrepository.findById(2).get();
townRequest.setRegionid(rgid.getId());
use ModelMapper.
If your entity class id field uses #Id annotation with auto-generating JPA key like #GeneratedValue(strategy = GenerationType.IDENTITY) as id, then using only model mapper and saving the entity should give you the expected result.
Otherwise, if set your PKID value manually and then save the entity.
Update: for your updated question,
add the whole Region in your Towns entity:
Regions rgid=regionsrepository.findById(2).get();
townRequest.setRegionid(rgid);
I have started learning Spring Boot recently.
I am trying to Soft delete the user. I want to soft delete all the Notes of the user when I soft delete the User. But my code is only soft deleting the user, not its notes.
When I am doing soft delete only on notes table, it's working fine but on User Entity, it is only deleting the user.
Notes
package com.we.springmvcboot.Model;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Where;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
#Table(name="Notes")
#Where(clause = "deleted = 'false'")//FALSE
public class Notes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="NotesID")
private long NotesID;
#Column(name="Title")
private String Title;
#Column(name="Message")
private String Message;
#Column(name="Date")
private String date;
#Column(name="deleted")
private String deleted="false";
#Column(name="label")
private int label=1;
#ManyToOne()
#JoinColumn(name = "UserID", nullable = false)
private User user;
public Notes() {}
public String getDeleted() {
return deleted;
}
public void setDeleted(String deleted) {
this.deleted = deleted;
}
public Notes(String title, String message, String date, User user, int label) {
super();
Title = title;
Message = message;
this.date = date;
this.user = user;
this.label=label;
}
public Notes(long notesID, String title, String message, String date, int label) {
super();
NotesID = notesID;
Title = title;
Message = message;
this.date = date;
this.label=label;
}
public int getLabel() {
return label;
}
public void setLabel(int label) {
this.label = label;
}
public long getNotesID() {
return NotesID;
}
public void setNotesID(long notesID) {
NotesID = notesID;
}
public String getTitle() {
return Title;
}
public void setTitle(String title) {
Title = title;
}
public String getMessage() {
return Message;
}
public void setMessage(String message) {
Message = message;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public void setUser(User user) {
this.user = user;
}
}
User
package com.we.springmvcboot.Model;
import java.util.ArrayList;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Where;
import antlr.collections.List;
#Entity
#Table(name="User")
#Where(clause = "deleted = 'false'")//FALSE
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long UserID;
#Column(name="emailid")
private String emailID;
#Column(name="deleted")
private String deleted="false";
#OneToMany(mappedBy="user", fetch = FetchType.EAGER,cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Notes> usernotes;
public User() {}
public User(String emailID) {
super();
this.emailID = emailID;
}
public long getUserID() {
return UserID;
}
public void setUserID(long userID) {
UserID = userID;
}
public String getemailID() {
return emailID;
}
public void setemailID(String emailID) {
emailID = emailID;
}
public Set<Notes> getUsernotes() {
return usernotes;
}
public void setUsernotes(Set<Notes> usernotes) {
this.usernotes = usernotes;
}
}
NotesRepository
package com.we.springmvcboot.Repository;
import java.sql.Date;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.we.springmvcboot.Model.Notes;
#Repository
public interface NotesRepository extends JpaRepository<Notes, Long> {
#Query("update Notes e set e.deleted='true' where e.NotesID=?1")
#Transactional
#Modifying
public void softDelete(long id);
}
UserRepository
package com.we.springmvcboot.Repository;
import java.sql.Date;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import com.we.springmvcboot.Model.Notes;
import com.we.springmvcboot.Model.User;
#Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByEmailID(String email);
#Query("update User e set e.deleted='true', where e.UserID=?1")
#Transactional
#Modifying
public void softDelete(long id);
}
TodoService
package com.we.springmvcboot.Service;
import com.we.springmvcboot.Model.*;
import com.we.springmvcboot.exception.*;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import com.we.springmvcboot.Repository.NotesRepository;
import com.we.springmvcboot.Repository.UserRepository;
#Service
public class TodoService {
#Autowired
UserRepository userrepo;
#Autowired
NotesRepository notesrepo;
public Object deleteUser(Map<String, Object> input) {
long userID;
userID = ((Number) input.get("userID")).longValue();
userrepo.softDelete(userID);
return null;
}
}
I would suggest that you read this post for the simplest approach to soft delete. You should arrive at sth like the following:
#SQLDelete("UPDATE User SET deleted = TRUE WHERE id = ?")
#Where(clause = "deleted = FALSE")
public class User {
#OneToMany(mappedBy = "user", fetch = EAGER, cascade=ALL, orphanRemoval = true)
private Set<Notes> usernotes;
...
}
#SQLDelete("UPDATE Note SET deleted = TRUE WHERE id = ?")
#Where(clause = "deleted = FALSE")
public class Note {...}
The above will work if you use the following code to delete a User:
public Object deleteUser(Map<String, Object> input) {
long userID;
userID = ((Number) input.get("userID")).longValue();
User user = userrepo.deleteById(userID);
return null;
}
If you want to keep using the custom query, though, you still need to call a separate query to delete Notes by userId, because Cascade.REMOVE will not be triggered in this case. In other words, you'll want a method like:
public interface NotesRepository extends JpaRepository<Notes, Long> {
#Query("UPDATE Notes n SET n.deleted = TRUE WHERE n.user.id = :id")
public void deleteByUserId(long id);
}
which you then call from deleteUser:
long userID;
userID = ((Number) input.get("userID")).longValue();
userrepo.softDelete(userID);
noteRepo.deleteByUserId(userID);
return null;
I went to develop an application to manage students in a university, I am looking for how to find students in relation to a date entered for that I have developed the following code lines:
1- Model student.java
package com.avatar.model;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.JoinColumn;
#Entity
#Table(name = "Students")
public class Student{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String nom;
private String prenom;
private String numTel;
private String mail;
#Temporal(TemporalType.DATE)
private Date dateCurrent;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "student_techno",
joinColumns = { #JoinColumn(name = "student_id") },
inverseJoinColumns = { #JoinColumn(name = "techno_id") })
private Set<Techno> techno = new HashSet<>();
public Student() {
}
#SuppressWarnings("unchecked")
public Student(String nom, String prenom,String numTel, String mail, Date dateCurrent,
) {
super();
this.nom = nom;
this.prenom = prenom;
this.numTel = numTel;
this.mail = mail;
this.dateCurrent = dateCurrent;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getPrenom() {
return prenom;
}
public void setPrenom(String prenom) {
this.prenom = prenom;
}
public String getNumTel() {
return numTel;
}
public void setNumTel(String numTel) {
this.numTel = numTel;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getdateCurrent() {
return dateCurrent;
}
public void setdateCurrent(Date dateCurrent) {
this.dateCurrent = dateCurrent;
}
#Override
public String toString() {
return "Student[nom=" + nom + ", prenom=" + prenom + ", numTel=" + numTel + ", mail="
+ mail + ", dateCurrent=" + dateCurrent+ "]";
}
}
2- Controller
package com.avatar.web;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.avatar.dao.StudentDao;
import com.avatar.model.Student;
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/avatar")
public class StudentController {
#Autowired
StudentDao studentdao;
#GetMapping(value = "/all-students")
public List<Student> listeDesStudent() {
List<Student> students= studentdao.findAll();
if (students.isEmpty())
throw new ProductNotFoundException("No student is registered in the database");
return students;
}
#GetMapping(value = "/all-students/dateCurrent/{dateCurrent}")
public List<Student> viewStudent(#PathVariable("dateCurrent") #DateTimeFormat(pattern = "yyyy-MM-dd") Date dateCurrent) {
Calendar c = Calendar.getInstance();
c.setTime(dateCurrent);
c.add(Calendar.DATE, 1);
dateCurrent = c.getTime();
return studentdao.findByDate(dateCurrent);
}
#GetMapping(value = "/all-students /techno/{nomTechno}")
List<Student > viewStudent(#PathVariable("nomTechno") String nomTechno) {
return studentdao.findDistinctByTechnoNomTechno(nomTechno);
}
#PostMapping(value = "/add-student")
public Student addStudent(#RequestBody Student student) {
Student studentAdded= studentdao.save(Student);
return studentAdded;
}
}
3- DAO
package com.avatar.dao;
import java.util.Date;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.avatar.model.Student;
#Repository
public interface StudentDao extends JpaRepository<Student, String> {
List<Student> findByDate(Date dateCurrent);
List<> findDistinctByTechnoNomTechno(String nomTechno);
}
4- applications.properties
server.port= 8080
# MySQL Properties
spring.jpa.show-sql = true
spring.datasource.url= jdbc:mysql://localhost:3306/avatar?serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username=*****
spring.datasource.password=*****
# Hibernate Properties
spring.jpa.hibernate.ddl-auto=update
in my console i have:
Failed to create query for method public abstract java.util.List com.avatar.dao.StudentDao.findByDate(java.util.Date)! No property date found for type Student!
Please update DAO as per for below mentioned changes
If the field name is dateCurrent then findByDateCurrent.
package com.avatar.dao;
import java.util.Date;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.avatar.model.Student;
#Repository
public interface StudentDao extends JpaRepository<Student, String> {
List<Student> findByDateCurrent(Date dateCurrent);
List<> findDistinctByTechnoNomTechno(String nomTechno);
}
You are missing field name, in Entity class it is not the date by dateCurrent, thus your JPA should be findByDateCurrent
I was trying to make a crud app which has two model objects of type Muscle and Exercise. Basically One Muscle Object can have a list of Exercise objects. I wanted to implement the CRUD operations for the both the model object. For Muscle object it was very straight forward but for the Exercise Object for the put/Update operation I am getting the following error "JSON parse error: Unresolved forward references for: ; nested exception is com.fasterxml.jackson.databind.deser.UnresolvedForwardReference" . And further more if I try to delete one exercise, somehow all the data of muscle and exercise gets deleted.
This is my muscle class
package com.fazla.exercise.model;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
#Entity
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
public class Muscle {
#Id
// #Column(unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
private String name;
#OneToMany(mappedBy="muscle", cascade= CascadeType.ALL, fetch = FetchType.EAGER)
// #JoinColumn(name="muscle_id")
// #Column(nullable = true)
private List<Exercise> exercises = new ArrayList<>();
public Muscle() {
}
public Muscle(String name, List<Exercise> exercises) {
super();
this.name = name;
this.exercises = exercises;
}
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 List<Exercise> getExercises() {
return exercises;
}
public void setExercises(List<Exercise> exercises) {
this.exercises = exercises;
}
// #Override
// public String toString() {
// return "Muscle [id=" + id + ", name=" + name + ", exercises=" + exercises + "]";
// }
}
This is my Exercise Object
package com.fazla.exercise.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
//import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
#Entity
//#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="id")
public class Exercise {
#Id
#Column(unique = true, nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column
private String name;
#Column
private String description;
//As there will be many exercise under one muscle that is why manytoone
//object references an unsaved transient instance - save the transient instance before flushing
//that is why need to add the cascading dependencies
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="muscle_id")
// #JsonIgnore
// #JoinTable(name="muscle")
private Muscle muscle;
public Exercise() {
}
public Exercise(String name, String description, Muscle muscle) {
super();
this.name = name;
this.description = description;
this.muscle = muscle;
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Muscle getMuscle() {
return muscle;
}
public void setMuscle(Muscle muscle) {
this.muscle = muscle;
}
// #Override
// public String toString() {
// return "Exercise [id=" + id + ", name=" + name + ", description=" + description + ", muscle=" + muscle + "]";
// }
}
This is the MuscleController
package com.fazla.exercise.controller;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.fazla.exercise.model.Muscle;
import com.fazla.exercise.repository.MuscleRepository;
#RestController
public class MuscleController {
private MuscleRepository muscleRepository;
public MuscleController(MuscleRepository muscleRepository) {
this.muscleRepository = muscleRepository;
}
#GetMapping("/muscle")
List<Muscle> all(){
return muscleRepository.findAll();
}
#PostMapping("/muscle")
Muscle newMuscle(#RequestBody Muscle muscle) {
return muscleRepository.save(muscle);
}
#GetMapping("/muscle/{id}")
Muscle one(#PathVariable Long id) {
return muscleRepository.findById(id)
.orElse(null);
}
#PutMapping("/muscle/{id}")
Muscle updateMuscle(#RequestBody Muscle newMuscle, #PathVariable Long id) {
return muscleRepository.findById(id)
.map(muscle ->{
muscle.setName(newMuscle.getName());
muscle.setExercises(newMuscle.getExercises());
return muscleRepository.save(muscle);
})
.orElse(null);
}
#DeleteMapping("/muscle/{id}")
void deleteMuscle(#PathVariable Long id){
muscleRepository.deleteById(id);
}
}
This is the ExerciseController Class
package com.fazla.exercise.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.fazla.exercise.model.Exercise;
import com.fazla.exercise.model.Muscle;
import com.fazla.exercise.repository.ExerciseRepository;
import com.fazla.exercise.repository.MuscleRepository;
#RestController
public class ExerciseController {
private ExerciseRepository repository;
private MuscleRepository muscleRepository;
#Autowired
public ExerciseController(ExerciseRepository repository, MuscleRepository muscleRepository) {
super();
this.repository = repository;
this.muscleRepository=muscleRepository;
}
#GetMapping("/exercise")
public List<Exercise> getAll() {
return repository.findAll();
}
#PostMapping("/exercise")
public Exercise newExercise(#RequestBody Exercise newExercise, #RequestParam
Long muscleId) {
Muscle muscle = muscleRepository.findById(muscleId).orElse(null);
newExercise.setMuscle(muscle);
return repository.save(newExercise);
}
#DeleteMapping("/exercise/{id}")
public void deleteExercise(#PathVariable Long id) {
repository.deleteById(id);
}
#GetMapping("/exercise/{id}")
public Exercise one(#PathVariable Long id) {
return repository.findById(id).orElse(null);
}
#PutMapping("/exercise/{id}")
public Exercise updateExercise(#RequestBody Exercise newExercise, #PathVariable Long id) {
return repository.findById(id)
.map(//map a function which maps
e ->{
e.setName(newExercise.getName());
e.setDescription(newExercise.getDescription());
e.setMuscle(newExercise.getMuscle());
return repository.save(e);
})
.orElse(null);
}
}
This is my ExerciseRepository
package com.fazla.exercise.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.fazla.exercise.model.Exercise;
public interface ExerciseRepository extends JpaRepository<Exercise, Long> {
}
This is the MuscleRepository
package com.fazla.exercise.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.fazla.exercise.model.Muscle;
public interface MuscleRepository extends JpaRepository<Muscle, Long>{
}
This is the error if I try the put request or update the exercise object
"timestamp": "2018-10-10T06:30:46.924+0000",
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Unresolved forward references for: ; nested exception is com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Unresolved forward references for: \n at [Source: (PushbackInputStream); line: 23, column: 1]Object id [1] (for `com.fazla.exercise.model.Muscle`) at [Source: (PushbackInputStream); line: 13, column: 28], Object id [1] (for `com.fazla.exercise.model.Muscle`) at [Source: (PushbackInputStream); line: 19, column: 28].",
"path": "/api/exercise/14"
The solution was, adding orphanRemoval= true on the Parent/ Muscle model
#OneToMany(mappedBy="muscle", cascade= CascadeType.ALL, orphanRemoval= true)
private List<Exercise> exercises = new ArrayList<>();
Removing the cascade= CascadeType.ALL in the child/ Exercise model
#ManyToOne
private Muscle muscle;
And for the updateExercise changing the Request by finding the muscle which the exercise belongs to and muscleRepository.findById(muscleId) and setting it in the new exercise object.
#PutMapping("/exercise/{id}")
public Exercise updateExercise(#RequestBody Exercise newExercise, #PathVariable Long id, #RequestParam Long muscleId) {
Muscle muscle = muscleRepository.findById(muscleId).orElse(null);
return repository.findById(id)
.map(//map a function which maps
e ->{
e.setName(newExercise.getName());
e.setDescription(newExercise.getDescription());
e.setMuscle(muscle);
return repository.save(e);
})
.orElse(null);
}
on your Exercise you have
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="muscle_id")
private Muscle muscle;
and on your Musce you have
#OneToMany(cascade= CascadeType.ALL)
private List<Exercise> exercises = new ArrayList<>();
Cascade.ALL propagates all actions on your Object and if you delete a Excercise, DELETE is propagated to all referenced objects
Bacause you just want to propagate UPDATES replace
cascade = CascadeType.ALL
with
cascade = CascadeType.SAVE_UPDATE
I am working on a simple practice application for using hibernate. It has simple mapping like a manufacturer can have many mobiles. But a mobile can only be manufactured by single manufacturer. Here is what I think the code should be.
package mobileconsumers.entity.dto;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
#Entity
#Table(name="ms_ref_mobile")
public class MobileDTO {
private Long id;
private String model;
private ManufacturerDTO manufacturerDTO;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MOBILE_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="MANUFACTURER_ID")
public ManufacturerDTO getManufacturer() {
return manufacturerDTO;
}
public void setManufacturer(ManufacturerDTO manufacturer) {
this.manufacturerDTO = manufacturer;
}
}
this is the second dto
package mobileconsumers.entity.dto;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
#Entity
#Table(name="ms_ref_manufacturer")
public class ManufacturerDTO {
private Long id;
private String name;
private Set<MobileDTO> mobileDTOs = new HashSet<>(0);
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="MANUFACTURER_ID")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name="NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch=FetchType.LAZY,mappedBy="manufacturerDTO")
public Set<MobileDTO> getMobileDTOs() {
return mobileDTOs;
}
public void setMobileDTOs(Set<MobileDTO> mobileDTOs) {
this.mobileDTOs = mobileDTOs;
}
}
When I try to start my server it gives me an error saying..
org.hibernate.AnnotationException: mappedBy reference anunknown target entity property: mobileconsumers.entity.dto.MobileDTO.manufacturerDTO in mobileconsumers.entity.dto.ManufacturerDTO.mobileDTOs
The mapping seems to be fine to me. there must be something really silly that I am missing out. Just needed fresh pair of eyes to look at my code and figure out whats going wrong..
Change mappedBy value, so that it refers the manufacturer property on the #ManyToOne side of the association:
#OneToMany(fetch=FetchType.LAZY,mappedBy="manufacturer")
public Set<MobileDTO> getMobileDTOs() {
return mobileDTOs;
}