-1
package com.project.customer.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.project.customer.baseEntity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import javax.persistence.*;

@EqualsAndHashCode(callSuper = true)
@Entity
@Data
public class Product  extends BaseEntity {


    private String name;
    private double price;
    private double highestBid;
    @Column(name = "EndDate")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime bidEndTime;

    private LocalDate addedDate;
    boolean status;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer highestBidder;

}

Product.java : this is my Product Entity

package com.project.customer.controllers;

import com.project.customer.entity.Product;
import com.project.customer.repositories.ProductRepository;
import com.project.customer.service.AuctionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Optional;

@RestController
@RequestMapping("/products")
public class AuctionController {

    @Autowired
    private AuctionService auctionService;

    @Autowired
    private ProductRepository productRepository;

    @PostMapping("/{productId}/bid")
    public ResponseEntity<String> placeBid(@PathVariable Integer productId, @RequestBody double bidAmount) {
        Optional<Product> optionalProduct = productRepository.findById(productId);
        if (optionalProduct.isPresent()) {
            Product product = optionalProduct.get();
            if (product.isStatus() && product.getBidEndTime().isAfter(LocalDateTime.now())) {
                if (product.getHighestBid() == 0 || bidAmount > product.getHighestBid()) {
                    product.setHighestBid(bidAmount);
                    product.setHighestBidder(product.getHighestBidder()); // Set the highest bidder
                    // Reset the bid end time to give another 10 seconds
                    product.setBidEndTime(LocalDateTime.now().plusSeconds(10));
                    productRepository.save(product);
                    return ResponseEntity.ok("Bid placed successfully.");
                }
                return ResponseEntity.badRequest().body("Bid amount should be higher than the current highest bid.");
            }
        }
        return ResponseEntity.notFound().build();
    }

}

AuctionController.java : i am getting bid amount from Request Body

package com.project.customer;

import com.project.customer.config.AppConstants;
import com.project.customer.entity.Roles;
import com.project.customer.repositories.RoleRepository;
import com.project.customer.service.AuctionService;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;
//import org.springframework.security.crypto.password.PasswordEncoder;


@SpringBootApplication
public class CustomerApplication implements CommandLineRunner {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuctionService auctionService;

    @Autowired
    private RoleRepository roleRepository;
    public static void main(String[] args) {
        SpringApplication.run(CustomerApplication.class, args);
    }
    @Bean
    public ModelMapper mapper(){
        ModelMapper mapper = new ModelMapper();
        mapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        return  mapper;
    }

    @Override
    public void run(String... args){
        System.out.println(this.passwordEncoder.encode("bjp"));
        try
        {
            Roles admin = new Roles();
            admin.setId(AppConstants.ROLE_ADMIN);
            admin.setName("ROLE_ADMIN");

            Roles normal_user = new Roles();
            normal_user.setId(AppConstants.NORMAL_USER);
            normal_user.setName("ROLE_NORMAL");

            List<Roles> roleList = List.of(admin, normal_user);
            List<Roles> result = this.roleRepository.saveAll(roleList);
            result.forEach(r-> System.out.println(r.getName()));
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Scheduled(cron = "${auction.schedule}")
    public void executeAuction() {
        auctionService.startAuction();
    }

}

Main Method: i have also implemented JWT auth working.

While testing API in postman i am getting 400 Bad Request

"status": 400,
    "error": "Bad Request",
    "trace": "org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `double` from Object value (token `JsonToken.START_OBJECT`); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `double` from Object value (token `JsonToken.START_OBJECT`)\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 1]\r\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:391)\r\n\tat org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:343)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:185

Postman request

I want to implement an auction web service where multiple customer bid on single product and after bid start there is 10 sec countdowns, if any new bid come within that timeframe 10 sec timer get restart, otherwise highest bidder assigned that product.

Destello
  • 3
  • 3
  • 1
    Update question to add payload you send, the error basically says that what you send is incorrect. – Chaosfire Aug 22 '23 at 16:42
  • 1
    You can't have just a double as Request Body. Define a class with a double field `bidAmount` and getter/setter pair. So called POJO. – Mar-Z Aug 22 '23 at 17:16
  • Put the Bid Amount inside class, e.g public class bidRequest. And then add that class as payload in controller – Deepak Negi Aug 22 '23 at 17:16
  • 1
    @Mar-Z That's wrong, payload like this - `"33"` will be successfully deserialized directly into any numeric type, `BigDecimal`, `double`, `int`, their wrapper classes, etc. – Chaosfire Aug 23 '23 at 06:34
  • @Chaosfire Cool. I have learned something new. – Mar-Z Aug 23 '23 at 06:47

2 Answers2

0

This error: Cannot deserialize value of type `double` from Object value (token `JsonToken.START_OBJECT`)

tells you that you cannot deserialize the Object {"bidAmount": 500.26} into a double primitive bidAmount

In this case, I can suggest that you create a separate class for mapping @RequestBody to an object:

// Getter, setter and etc. if you need
public class YourNameModel {
    private double bidAmount;
}

or accept bidAmount as a request parameter @RequestParam: Spring @RequestParam

I also want to draw your attention to the danger of using float/double in cases where precision is required. For example with money:

double total = 0;
total += 5.6;
total += 5.8;
// 11.399999999999

retain precision with double in java

One way to solve this problem is to use BigDecimal

Alexey Bril
  • 479
  • 4
  • 14
  • 1
    `// Getter, setter and etc. if you need public class YourNameModel { private double bidAmount; }` is working thank you very much. – Destello Aug 23 '23 at 12:09
0

The error is caused by mismatch between request payload and the object you deserialize into. To match data and class you need to either:

  1. Change the deserialization class to match payload. This is already covered in other answer, so i won't go into details.
  2. Change payload to match double - for example "123.43", "11", is correct body to deserialize into double, float, BigDecimal.

Additionally, since you are working with money, consider using BigDecimal, instead of double. Floating point types are not precise enough for financial operations - Why not use Double or Float to represent currency?

Chaosfire
  • 4,818
  • 4
  • 8
  • 23