My problem: when I do an @Post in Postman I get data back from the prices, but not kitten data.
I enter the following:
{
"name": "Frizzle",
"dateOfBirth": "2019-07-27",
"weight": 7.1,
"breed": "Birman",
"firstVaccination": "yes",
"secondVaccination": "yes",
"breedPrice": 8000,
"firstVaccinationPrice": 80.50,
"secondVaccinationPrice": 80.10
}
And this is what I get back in Postman:
{
"id": 3,
"name": null,
"dateOfBirth": null,
"weight": 0.0,
"breed": null,
"firstVaccination": null,
"secondVaccination": null,
"fileUpload": null,
"price": {
"id": 3,
"broadPrice": 8000.0,
"firstVaccinationPrice": 80.5,
"secondVaccinationPrice": 80.1,
"totalPrice": 8160.6
}
}
I've tried changing the code in several places, but I can't figure out why it outputs the price part and not the kitten data part. I want it to output the kitten data. So basically what I want is for him to reverse it, return data of the kittens and give data of the prices that are null.
My files look like this.
Kitten.java
import com.sun.istack.NotNull;
import javax.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "kittens")
public class Kitten {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@NotNull
private String name;
@NotNull
@Column(name = "date_of_birth")
private LocalDate dateOfBirth;
@NotNull
@Column
private double weight;
@NotNull
@Column(name = "breed")
private String breed;
@Column(name = "first_vaccination")
private String firstVaccination;
@Column(name = "second_vaccination")
private String secondVaccination;
@OneToOne(mappedBy = "kitten")
private FileUpload fileUpload;
@OneToOne(fetch = FetchType.LAZY,
mappedBy = "kitten")
private Price price;
public Kitten(String name, LocalDate dateOfBirth, double weight, String breed, String firstVaccination, String secondVaccination) {
}
public Kitten() {
}
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 LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getFirstVaccination() {
return firstVaccination;
}
public void setFirstVaccination(String firstVaccination) {
this.firstVaccination = firstVaccination;
}
public String getSecondVaccination() {
return secondVaccination;
}
public void setSecondVaccination(String secondVaccination) {
this.secondVaccination = secondVaccination;
}
public FileUpload getFileUpload() {
return fileUpload;
}
public void setFileUpload(FileUpload fileUpload) {
this.fileUpload = fileUpload;
}
public Price getPrice() {
return price;
}
public void setPrice(Price price) {
this.price = price;
}
}
Price.java
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sun.istack.NotNull;
import javax.persistence.*;
@Entity
@Table(name = "prices")
public class Price {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@NotNull
@Column(name = "breed_price")
private double breedPrice;
@Column(name = "first_vaccination_price")
private double firstVaccinationPrice;
@Column(name = "second_vaccination_price")
private double secondVaccinationPrice;
@JsonIgnore
@OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true)
@JoinColumn(name = "kitten_id")
private Kitten kitten;
public Price() {
}
public Price(double breedPrice, double firstVaccinationPrice, double secondVaccinationPrice) {
this.breedPrice = breedPrice;
this.firstVaccinationPrice = firstVaccinationPrice;
this.secondVaccinationPrice = secondVaccinationPrice;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getBreedPrice() {
return breedPrice;
}
public void setBreedPrice(double breedPrice) {
this.breedPrice = breedPrice;
}
public double getFirstVaccinationPrice() {
return firstVaccinationPrice;
}
public void setFirstVaccinationPrice(double firstVaccinationPrice) {
this.firstVaccinationPrice = firstVaccinationPrice;
}
public double getSecondVaccinationPrice() {
return secondVaccinationPrice;
}
public void setSecondVaccinationPrice(double secondVaccinationPrice) {
this.secondVaccinationPrice = secondVaccinationPrice;
}
public Kitten getKitten() {
return kitten;
}
public void setKitten(Kitten kitten) {
this.kitten = kitten;
}
public Double getTotalPrice() {
return this.getBreedPrice() this.getFirstVaccinationPrice() this.getSecondVaccinationPrice();
}
}
KittenBuilder.java
import nl.danielle.cattery.payload.KittenRequest;
import java.time.LocalDate;
public class KittenBuilder {
//Kitten
private String name;
private LocalDate dateOfBirth;
private double weight;
private String breed;
private String firstVaccination;
private String secondVaccination;
//Price
private double breedPrice;
private double firstVaccinationPrice;
private double secondVaccinationPrice;
public KittenBuilder(KittenRequest kittenRequest) {
this.name = kittenRequest.getName();
this.dateOfBirth = kittenRequest.getDateOfBirth();
this.weight = kittenRequest.getWeight();
this.breed = kittenRequest.getBreed();
this.firstVaccination = kittenRequest.getFirstVaccination();
this.secondVaccination = kittenRequest.getSecondVaccination();
this.breedPrice = kittenRequest.getBreedPrice();
this.firstVaccinationPrice = kittenRequest.getFirstVaccinationPrice();
this.secondVaccinationPrice = kittenRequest.getSecondVaccinationPrice();
}
public Kitten buildKitten() {
return new Kitten(name, dateOfBirth, weight, breed, firstVaccination, secondVaccination);
}
public Price buildPrice() {
return new Price(breedPrice, firstVaccinationPrice, secondVaccinationPrice);
}
}
KittenRequest.java
import com.sun.istack.NotNull;
import java.time.LocalDate;
public class KittenRequest {
//Kitten
@NotNull
private String name;
@NotNull
private LocalDate dateOfBirth;
@NotNull
private double weight;
@NotNull
private String breed;
private String firstVaccination;
private String secondVaccination;
//Price
@NotNull
private double breedPrice;
private double firstVaccinationPrice;
private double secondVaccinationPrice;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public String getBreed() {
return breed;
}
public void setBreed(String breed) {
this.breed = breed;
}
public String getFirstVaccination() {
return firstVaccination;
}
public void setFirstVaccination(String firstVaccination) {
this.firstVaccination = firstVaccination;
}
public String getSecondVaccination() {
return secondVaccination;
}
public void setSecondVaccination(String secondVaccination) {
this.secondVaccination = secondVaccination;
}
public double getBreedPrice() {
return breedPrice;
}
public void setBreedPrice(double breedPrice) {
this.breedPrice = breedPrice;
}
public double getFirstVaccinationPrice() {
return firstVaccinationPrice;
}
public void setFirstVaccinationPrice(double firstVaccinationPrice) {
this.firstVaccinationPrice = firstVaccinationPrice;
}
public double getSecondVaccinationPrice() {
return secondVaccinationPrice;
}
public void setSecondVaccinationPrice(double secondVaccinationPrice) {
this.secondVaccinationPrice = secondVaccinationPrice;
}
}
KittenController.java
import nl.danielle.cattery.model.FileUpload;
import nl.danielle.cattery.payload.KittenRequest;
import nl.danielle.cattery.payload.ResponseMessage;
import nl.danielle.cattery.service.FileStorageServiceImpl;
import nl.danielle.cattery.service.KittenService;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
@RestController
@RequestMapping(value = "/kittens")
public class KittenController {
final KittenService kittenService;
final FileStorageServiceImpl storageService;
public KittenController(KittenService kittenService, FileStorageServiceImpl storageService) {
this.kittenService = kittenService;
this.storageService = storageService;
}
@GetMapping(value = "")
public ResponseEntity<Object> getKittens() {
return ResponseEntity.ok().body(kittenService.getKittens());
}
@GetMapping(value = "/{id}")
public ResponseEntity<Object> getKitten(@PathVariable("id") long id) {
return ResponseEntity.ok().body(kittenService.getKittenById(id));
}
@PostMapping(value = "/add")
public ResponseEntity<Object> saveKitten(@RequestBody KittenRequest kitten) {
long newId = kittenService.saveKitten(kitten);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(newId).toUri();
return ResponseEntity.created(location).build();
}
@PutMapping(value = "/{id}")
public ResponseEntity<Object> updateKitten(@PathVariable("id") long id, @RequestBody KittenRequest kitten) {
kittenService.updateKitten(id, kitten);
return ResponseEntity.noContent().build();
}
@PutMapping(value = "/{id}/price")
public ResponseEntity<Object> updatePrice(@PathVariable("id") long id, @RequestBody KittenRequest price) {
kittenService.updatePrice(id, price);
return ResponseEntity.noContent().build();
}
@DeleteMapping(value = "/{id}")
public ResponseEntity<Object> deleteKitten(@PathVariable("id") long id) {
kittenService.deleteKitten(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/upload/kittenid/{id}")
public ResponseEntity<ResponseMessage> uploadFile(@PathVariable long id, @RequestParam("file") MultipartFile file) {
try {
storageService.store(file, id);
String message = "Uploaded the file successfully: " file.getOriginalFilename();
return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
} catch (Exception e) {
String message = "Could not upload the file: " file.getOriginalFilename() "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
}
}
@GetMapping("/download/{id}")
public ResponseEntity<byte[]> getFileById(@PathVariable("id") String id) {
FileUpload fileUpload = storageService.getFileById(id);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" fileUpload.getName() "\"")
.body(fileUpload.getData());
}
}
KittenService.java
package nl.danielle.cattery.service;
import nl.danielle.cattery.exceptions.DatabaseErrorException;
import nl.danielle.cattery.exceptions.RecordNotFoundException;
import nl.danielle.cattery.model.Kitten;
import nl.danielle.cattery.model.KittenBuilder;
import nl.danielle.cattery.model.Price;
import nl.danielle.cattery.payload.KittenRequest;
import nl.danielle.cattery.repository.KittenRepository;
import nl.danielle.cattery.repository.PriceRepository;
import org.springframework.stereotype.Service;
import java.util.Collection;
@Service
public class KittenServiceImpl implements KittenService {
final KittenRepository kittenRepository;
final PriceRepository priceRepository;
public KittenServiceImpl(KittenRepository kittenRepository, PriceRepository priceRepository) {
this.kittenRepository = kittenRepository;
this.priceRepository = priceRepository;
}
@Override
public Collection<Kitten> getKittens() {
return kittenRepository.findAll();
}
@Override
public Kitten getKittenById(long id) {
if (!kittenRepository.existsById(id)) {
throw new RecordNotFoundException();
}
return kittenRepository.findById(id).orElse(null);
}
@Override
public long saveKitten(KittenRequest kittenRequest) {
Kitten newKitten = new KittenBuilder(kittenRequest).buildKitten();
Price newPrice = new KittenBuilder(kittenRequest).buildPrice();
Price savedPrice = priceRepository.save(newPrice);
newKitten.setPrice(savedPrice);
newPrice.setKitten(newKitten);
return kittenRepository.save(newKitten).getId();
}
@Override
public void updateKitten(long id, KittenRequest kitten) {
if (kittenRepository.existsById(id)) {
try {
Kitten existingKitten = kittenRepository.findById(id).orElse(null);
existingKitten.setName(kitten.getName());
existingKitten.setDateOfBirth(kitten.getDateOfBirth());
existingKitten.setWeight(kitten.getWeight());
existingKitten.setBreed(kitten.getBreed());
existingKitten.setFirstVaccination(kitten.getFirstVaccination());
existingKitten.setSecondVaccination(kitten.getSecondVaccination());
kittenRepository.save(existingKitten);
} catch (Exception ex) {
throw new DatabaseErrorException();
}
} else {
throw new RecordNotFoundException();
}
}
@Override
public void updatePrice(long id, KittenRequest price) {
if (priceRepository.existsById(id)) {
try {
Price existingPrice = priceRepository.findById(id).orElse(null);
existingPrice.setBreedPrice(price.getBreedPrice());
existingPrice.setFirstVaccinationPrice(price.getFirstVaccinationPrice());
existingPrice.setSecondVaccinationPrice(price.getSecondVaccinationPrice());
priceRepository.save(existingPrice);
} catch (Exception ex) {
throw new DatabaseErrorException();
}
} else {
throw new RecordNotFoundException();
}
}
@Override
public void deleteKitten(long id) {
kittenRepository.deleteById(id);
}
}
CodePudding user response:
I have a lot to say about your code here, but let's see what's the main problem here. The problem is simple if you look at your KittenBuilder
you create a Kitten using this builder but your Kitten constructor is empty.
public Kitten(String name, LocalDate dateOfBirth, double weight, String breed, String firstVaccination, String secondVaccination) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.weight = weight;
this.breed = breed;
this.firstVaccination = firstVaccination;
this.secondVaccination = secondVaccination;
}
This will populate the Kitten object and you're ready to go.
Some advices about your code
If you wonder why it's saving the null
values when you specified the null
constraints is that recent version of spring boot
don't include the validation starter in the web starter, you have to include it yourself.
In you pom.xml
add the following dependecy:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And you have to include the annotation @Transactional
to your services to make your operations atomic, for example in your create kitten method you want to persist the price and the kitten atomically.
@Service
@Transactional
class KittenServiceImpl implementes ...
And one the best practices also, is to not use your entites directly in your controllers, use DTO
(Data Transfer Object), so you map the properties you want from your entity to your DTO (this way you can include only your kitten properties and leave the price entity).
And last bonus:
public Kitten getKittenById(long id) {
if (!kittenRepository.existsById(id)) {
throw new RecordNotFoundException("");
}
return kittenRepository.findById(id).orElse(null);
}
This can be simplified to:
public Kitten getKittenById(long id) {
return kittenRepository.findById(id).orElseThrow(() -> new RecordNotFoundException());
}