I'm using postman with springboot i have already used the GET/POST/DELETE requests and they all work fine but PUT request doesn't update content .
In intellij i'm using these files :
Student.java(with it's setters and getters) :
#Entity
#Table
public class Student {
#Id
#SequenceGenerator(
name="student_sequence",
sequenceName="student_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "student_sequence"
)
private Long id;
private String name;
private LocalDate dob;
private String email;
#Transient
private Integer age;
StudentController.java :
#PutMapping(path ="{studentId}")
public void updateStudent(
#PathVariable("studentId") Long studentId,
#RequestParam(required = false) String name,
#RequestParam(required = false) String email)
{
studentService.updateStudent(studentId,name,email);
}
StudentService.java :
#Transactional
public void updateStudent(Long studentId,String name, String email)
{
Student student = studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalStateException(
"student with id="+studentId+"does not exist"));
if (name !=null && name.length()>0 && !Objects.equals(student.getName(),name))
{
student.setName(name);
}
if (email !=null && email.length()>0 && !Objects.equals(student.getEmail(),email))
{
Optional<Student> studentOptional= studentRepository.findStudentByEmail(email);
if (studentOptional.isPresent())
{
throw new IllegalStateException("email taken");
}
student.setEmail(email);
}
}
These are the students that i have in database
And basically i want to update the name and email of the student with id=1.
That is postman header
And that is postman not showing any error after sending request
using #RequestParam(required = false) String name the params are expected as header or query param. You're sending a request body, so use a pojo instead...
class StudentDto {
public String name;
//...
}
and the controller ...
#PutMapping(path ="{studentId}")
public void updateStudent(
#PathVariable("studentId") Long studentId,
#RequestBody StudentDto) {
//...
}
To leave it be and make it work, you have to put your data as query params, lik ethis
PUT http://localhost:8080/api/v1/student/1?name=newName&email=newEmailToSet
So after this answer https://stackoverflow.com/a/72698172/19354780
i have tried changing StudentController to :
#PutMapping(path = "{studentId}")
public void updateStudent(
#PathVariable("studentId") Long studentId,
#RequestBody Student new_student) {
studentService.updateStudent(studentId, new_student);
}
and StudentService to :
#Transactional
public void updateStudent(Long studentId,Student new_student)
{
Student student = studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalStateException(
"student with id="+studentId+"does not exist"));
if (new_student.getName() !=null && new_student.getName().length()>0 && !Objects.equals(student.getName(),new_student.getName()))
{
student.setName(new_student.getName());
}
if (new_student.getEmail() !=null && new_student.getEmail().length()>0 && !Objects.equals(student.getEmail(),new_student.getEmail()))
{
Optional<Student> studentOptional= studentRepository.findStudentByEmail(new_student.getEmail());
if (studentOptional.isPresent())
{
throw new IllegalStateException("email taken");
}
student.setEmail(new_student.getEmail());
}
}
and it worked but without the exceptions .
Related
In the previous tests, I saw that while it was able to delete without any problems, it does not work now.
This is my entity class :
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name="subcomments")
public class SubComment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public long id;
#Column(name="uid")
public String uid;
#Column (name="name")
public String name;
#Column (name="above_uid")
public String above_uid;
#Column (name="url")
public String url;
#Column(name="coin")
public String coin;
#Column (name="comment")
public String comment;
#Column (name="top_date")
public int top_date;
#Column (name="top_time")
public int top_time;
}
This is my jpa repository :
public interface SubCommentRepo extends JpaRepository<SubComment , Long>{
// All CRUD database methods
#Query( value="Select * from subcomments where uid=:uid and above_uid=:above_uid and coin=:coin and comment=:comment and top_date=:top_date and top_time=:top_time" , nativeQuery=true )
public SubComment info(String uid, String above_uid, String coin, String comment,int top_date, int top_time);
}
The info method in the repository is designed to check the existence of the current data in the database.
This is my rest controller :
#RestController
#RequestMapping(path="/api/v1/user")
public class ServiceController {
#Autowired
SubCommentRepo subRepo ;
// Delete Sub Comment ***
#DeleteMapping(path="/comment/sub/delete")
public ResponseEntity<String> delete( #RequestBody SubComment smt ) {
if ( subRepo.info(smt.uid, smt.above_uid, smt.coin, smt.comment, smt.top_date, smt.top_time)!= null ) {
subRepo.delete(smt);
return ResponseEntity.status( HttpStatus.OK ).body ( "Deleted" );
} else {
return ResponseEntity.status( HttpStatus.BAD_REQUEST ).body( "Not available already" );
}
}
}
I am using mysql as database.
I am trying spring boot for the first time. :)
Use deleteById method instead of the delete method.
#RestController
#RequestMapping(path="/api/v1/user")
public class ServiceController {
#Autowired
SubCommentRepo subRepo ;
// Delete Sub Comment ***
#DeleteMapping(path="/comment/sub/delete")
public ResponseEntity<String> delete( #RequestBody SubComment smt ) {
if ( subRepo.info(smt.uid, smt.above_uid, smt.coin, smt.comment, smt.top_date, smt.top_time)!= null ) {
subRepo.deleteById(smt.id);
return ResponseEntity.status( HttpStatus.OK ).body ( "Deleted" );
} else {
return ResponseEntity.status( HttpStatus.BAD_REQUEST ).body( "Not available already" );
}
}
}
#PutMapping(path = "{studentId}")
public void updateStudent(
#PathVariable("studentId") Long studentId,
#RequestParam(required = false) String name,
#RequestParam(required = false) String email){
studentService.updateStudent(studentId, name, email);
}
#Transactional
public void updateStudent(Long studentId, String name, String email){
Student student = studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalStateException("Student with " +studentId+" does not exist" ));
student.setName(name);
student.setEmail(email);
}
}
The problem is when I do PUT method in POSTMAN it gives no error but values in "name" and "email" are NULL. How can I fix it?
Here is my POSTMAN request.
https://i.stack.imgur.com/KHYVW.png
Your api expects optional request params and you are giving request body in postman. The above api will work for /student/1?name=John&email=example#abc.com
If you want to use request body to work with this api i.e., form data submit them change the api to use request body which will be (#RequestBody Student studentData) where Student is a class having 2 string variables name & email. You can create a new class StudentRequest having only request attributes for your put/post apis or you can reuse Student class.
#PutMapping(path = "{studentId}")
public void updateStudent(
#PathVariable("studentId") Long studentId,
#RequestBody(required = true) Student student){
studentService.updateStudent(studentId, student);
}
#Transactional
public void updateStudent(Long studentId, Student studentData){
Student student = studentRepository.findById(studentId)
.orElseThrow(() -> new IllegalStateException("Student with " +studentId+" does not exist" ));
student.setName(studentData.getName());
student.setEmail(studentData.getEmail());
studentRepository.save(student);
}
I'm creating eCommerce for merchants using spring boot with JPA.
I have an issue while creating the order service.
I want to only pass the ID of the nested objects in the request body instead of sending the full nest objects because the size will be extremely big.
Here is my code.
Merchant can do many orders
Order
#Entity
#Table(name = "Orders")
#XmlRootElement
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Order extends BasicModelWithIDInt {
#Basic(optional = false)
#Column(name = "Quantity")
private Integer quantity;
#Basic(optional = false)
#Size(min = 1, max = 150)
#Column(name = "Notes")
private String notes;
#JoinColumn(name = "ProductID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JsonIgnoreProperties
private Product productID;
#JoinColumn(name = "MerchantID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Merchent merchent;
#JoinColumn(name = "OrderSatusID", referencedColumnName = "ID")
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private OrderStatus orderStatus;
// Getters and Setters
}
Order Holder
public class OrderHolder {
#NotNull
private Order order;
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
}
OrderRepo
public interface OrderRepo extends JpaRepository<Order, Integer> {
}
Order Controller
#RestController
#RequestMapping(value = "order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderRestController extends BasicController<OrderHolder>{
#Autowired
private OrderRepo orderRepo;
#PostMapping("create")
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
log.debug( "create order {} requested", orderHolder.toString());
Order order = new Order();
order = orderHolder.getOrder();
System.out.println("###############"+order);
try {
order = orderRepo.save(order);
log.info( "Order {} has been created", order );
} catch (Exception e) {
log.error( "Error creating Order: ", e );
e.printStackTrace();
throw new GeneralException( Errors.ORDER_CREATION_FAILURE, e.toString() );
}
return ResponseEntity.ok( order );
}
}
I need request body to look like the below instead of including the full Merchant and Product objects inside the request.
You can make use of JsonView to return only id of product and merchant
public class OrderView {}
...
public class Product{
#Id
#JsonView(OrderView.class)
private Integer id
private String otherFieldWithoutJsonView
...
}
and then in your controller
#PostMapping("create")
#JsonView(OrderView.class) // this will return the product object with one field (id)
public ResponseEntity<?> create(#RequestBody #Valid OrderHolder orderHolder, Principal principal) throws GeneralException {
...
}
hope this can help you
Just have a separate contract class.
public class OrderContract {
private int merchantID;
private String notes;
....
//getter, setters
}
public class OrderHolder {
#NotNull
private OrderContract orderContract;
public OrderContract getOrderContract() {
return orderContract;
}
public void setOrder(OrderContract orderContract) {
this.orderContract = orderContract;
}
}
And before making a call to the Repository , translate from OrderContract to Order.
I would like to share something regarding this.
I have searched a lot on internet and tried lot of things, but the solution given here suited well for this scenario.
https://www.baeldung.com/jackson-deserialization
You need to create a Custom-deserializer for your model by extending StdDeserializer from com.fasterxml.jackson.databind.deser.std.StdDeserializer, where you just want to pass id's and not the whole object in the request.
I have given below example for User Model with Address object.
User(long userId, String name, Address addressId)
Address(long addressId, String wholeAddress)
Writing Deserializer for User class
public class UserDeserializer extends StdDeserializer<User> {
public User() {
this(null);
}
public User Deserializer(Class<?> vc) {
super(vc);
}
#Override
public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = p.getCodec().readTree(p);
long id = 0;
long addressId = (Long) ((IntNode) node.get("addressId")).numberValue().longValue();
return new User(id, name, new Address(addressId, null)
}
Now you have to use
#JsonDeserialize(using = UserDeserializer.class)
public Class User {
...
}
POST request
Before custom deserialization
{
"name" : "Ravi",
"addressId" : { "id" : 1}
}
After custom Deserialization
{
"name" : "Ravi",
"addressId" : 1
}
Also while GET /user/:id call you will get the whole obj like
{
"name" : "Ravi",
"addressId" : { "id" : 1, "wholeAddress" : "Some address"}
}
I am trying to update some user information by passing List of User-Ids as parameter
i want to update isActive field of User fo which i am passing the user ids.
Below is my controller
#PutMapping
#ResponseStatus(HttpStatus.OK)
#RequestMapping("/UserUpdate")
public ResponseEntity<?> updateUsers(List<Long> userIds) {
**userService.updateUsers(userIds);**
return ResponseEntity.ok(200);
}
updateUsers() is a method in my Service where i have to write the logic
I tried something like below but it's not working
public void updateUsers(List<Long> userIds) {
List<Users> userList= userRepository.findAll();
for (Long i : userIds) {
for ( Users user : userList)
{
if(userRepository.findById(i) != null)
{
user.setIsActive(9L);
user.setUserName("Update Test");
}
my dto
public class UserDto {
private List<Users> userList;
private String appName="Users Project";
// getters and setters removed for brevity
And my Users entity class
#Entity
#Table(name="USERS")
public class Users {
#Id
#Column(name="USER_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long userId;
#Column(name="NAME")
private String userName;
#Column(name="ACTIVE")
private Long isActive;
// getters and setters removed for brevity
Alternatively you can use the following code
#Modifying
#Query("update Users u set u.isActive = ?1, u.userName = ?2 where u.userId in ?3")
void updateUsers(Long isActive, String userName, List<Long> userId);
Add this code in your userRepository and use the method.
public void updateUsers(List<Long> userIds) {
for (Long i : userIds) {
User user = userRepository.findById(i);
if(user != null){
user.setIsActive(9L);
user.setUserName("Update Test");
// call your update method here (this is not stated in your code)
}
}
}
I have small problem to create correctly mathod adding people to teams. The reason is my problem is that i can't add two arguments in requestbody (or PathVariable).
I want to take a person id and team id and return both connected. I create separated class where i generate id but i have problem to imput this correctly id from database:
This is my method in cotroller:
#PostMapping("/addPeopleToTeams/{teamId}/{personId}")
#ResponseBody
public String addPeopleToTeam(#RequestBody TeamsAndPersonsId teamsAndPersonsId){
System.out.println(" " + teamsAndPersonsId.getPersonId());
System.out.println(" " + teamsAndPersonsId.getTeamId());
teamService.findTeamById(teamsAndPersonsId.getTeamId());
personService.findById(teamsAndPersonsId.getPersonId());
return teamsAndPersonsId.getTeamId() + " " + teamsAndPersonsId.getPersonId();
}
This is my method find by id in service:
public Optional<TeamDto> findTeamById(Long id) {
Assert.notNull(id, "ID must exist ");
return teamRepository
.findById(id)
.map(p -> modelMapper.map(p, TeamDto.class));
}
Other my class is Team - this is entity, teamDto, and TeamsAndPersonsId.
TeamsAndPersonsId class:
public class TeamsAndPersonsId {
private Long personId;
private Long teamId;
public TeamsAndPersonsId(Long personId, Long teamId) {
this.personId = personId;
this.teamId = teamId;
}
public TeamsAndPersonsId(){}
public Long getPersonId() {
return personId;
}
public void setPersonId(Long personId) {
this.personId = personId;
}
public Long getTeamId() {
return teamId;
}
public void setTeamId(Long teamId) {
this.teamId = teamId;
}
How to correctly wrote this controller method to return real team and persons id? Like i add in surce:
#PostMapping("/addPeopleToTeams/{teamId}/{personId}"
You can actually use multiple path variables, use them to create a TeamsAndPersonsId , then keep the rest of your current code :
#PostMapping("/addPeopleToTeams/{teamId}/{personId}")
#ResponseBody
public String addPeopleToTeam(#PathVariable Long teamId, #PathVariable Long personId){
TeamsAndPersonsId teamsAndPersonsId = new TeamsAndPersonsId(teamId, personId);
// etc...