Search code examples
javaspringcrudcrud-repository

Cannot invoke "...." because the return value of "..." is null


I am working on small CRUD parking thing.

My tables Car and Parking are created with liquibase, please see xml.

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
                      http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.9.xsd">
    <changeSet id="202010211812" author="Victor">
        <createTable tableName="parking">
            <column name="id" type="bigint" autoIncrement="true" defaultOnNull="true">
                <constraints primaryKey="true" primaryKeyName="parking_id_pk"/>
            </column>
            <column name="name" type="varchar(250)"/>
            <column name="number_of_places" type="int"/>
            <column name="number_of_chargers" type="int"/>
            <column name="parking_type" type="varchar(250)"/>
            <column name="length_parking_spot" type="double"/>
            <column name="width_parking_spot" type="double"/>
        </createTable>

        <createTable tableName="car">
            <column name="id" type="bigint" autoIncrement="true" defaultOnNull="true">
                <constraints primaryKey="true" primaryKeyName="car_id_pk"/>
            </column>
            <column name="make" type="varchar(250)"/>
            <column name="model" type="varchar(250)"/>
            <column name="price" type="double"/>
            <column name="width" type="double"/>
            <column name="length" type="double"/>
            <column name="car_combustible_type" type="varchar(250)"/>
            <column name="year" type="date"/>
            <column name="parking_id" type="bigint">
            </column>
        </createTable>
    </changeSet>
</databaseChangeLog>

Here are my classes

Parking :

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;


@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Parking {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    protected int numberOfPlaces;
    protected int numberOfChargers;
    private ParkingType parkingType;
    private double lengthParkingSpot;
    private double widthParkingSpot;

    @OneToMany
    private Set<Car> cars = new HashSet<>();
}

ParkingService :

import com.example.lesson31.domain.Car;
import com.example.lesson31.domain.Parking;
import com.example.lesson31.domain.ParkingType;
import com.example.lesson31.exception.CarBiggerThenParkingSpotException;
import com.example.lesson31.repository.ParkingRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class ParkingService extends Parking {

    private final ParkingRepository parkingRepository;

    public Parking save(Parking parking) {
        parkingRepository.save(parking);
        return parking;
    }

    public List<Parking> getAll() {
        return parkingRepository.findAll();
    }

    public Parking parkCarIntoParking(Long parkingId, Car car) throws Exception {
        Parking parking = parkingRepository.findById(parkingId)
                .orElseThrow(() -> new Exception("Id car not found"));
        if (parking.getNumberOfPlaces() == 0) {
            throw new Exception("no more empty parking spaces :(");
        }
        isLpgOrNo(car, parking);
        isElectricOrNo(car, parking);
        isCarBiggerThenTheWidthSpot(car, parking);

        parking.getCars()
                .add(car);
        return parking;
    }

    public Parking getParkingById(Long id) throws Exception {
        return parkingRepository.findById(id)
                .orElseThrow(() -> new Exception("Car Id not found"));
    }
    public Parking updateParking(Long id, Parking parking) {
        Parking parking1 = parkingRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Custom error message"));
        parking1.setName(parking.getName());
        parking1.setNumberOfPlaces(parking.getNumberOfPlaces());
        parking1.setNumberOfChargers(parking.getNumberOfChargers());
        parking1.setWidthParkingSpot(parking.getWidthParkingSpot());
        parking1.setLengthParkingSpot(parking.getLengthParkingSpot());
        return parkingRepository.save(parking);

    }

    public void deleteParking(Long id) throws Exception {
        Parking parkingForDelete = parkingRepository.findById(id)
                .orElseThrow(() -> new Exception("grgreg"));
        parkingRepository.deleteById(parkingForDelete.getId());
    }


    public void isLpgOrNo(Car car, Parking parking) throws Exception {
        if (car.getCarCombustibleType()
                .equalsIgnoreCase("lpg")
                && parking.getParkingType()
                .equals(ParkingType.UNDERGROUND)) {
            throw new Exception("Parking is not for cars with LPG :(");
        }
    }

    public void isElectricOrNo(Car car, Parking parking) throws Exception {
        if (car.getCarCombustibleType()
                .equalsIgnoreCase("electric") && parking.getNumberOfChargers() == 0) {
            throw new Exception("No chargers available");
        }
    }

    public void isCarBiggerThenTheWidthSpot(Car car, Parking parking) throws CarBiggerThenParkingSpotException {
        if (car.getWidth() > parking.getWidthParkingSpot()) {
            throw new CarBiggerThenParkingSpotException("Vehicle width is bigger then the parking spot width!");
        }
    }

}

ParkingController :

import com.example.lesson31.command.CarCommand;
import com.example.lesson31.command.ParkingCommand;
import com.example.lesson31.domain.Car;
import com.example.lesson31.domain.Parking;
import com.example.lesson31.dto.ParkingDto;
import com.example.lesson31.service.ParkingService;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/parking")
@RequiredArgsConstructor
public class ParkingController {


    private final ParkingService parkingService;
    public final ModelMapper modelMapper;

    @PostMapping("/{id}")
    public ResponseEntity<ParkingDto> parkCar(@PathVariable Long id, @RequestBody CarCommand carCommand) throws Exception {
        return new ResponseEntity<>(modelMapper.map(parkingService.parkCarIntoParking(id, modelMapper.map(carCommand, Car.class)), ParkingDto.class), HttpStatus.OK);
    }


    @PostMapping
    public ResponseEntity<ParkingDto> saveParking(@RequestBody ParkingCommand parkingCommand) {
        return new ResponseEntity<>(modelMapper.map(parkingService.save(modelMapper.map(parkingCommand, Parking.class)), ParkingDto.class), HttpStatus.CREATED);
    }

    @GetMapping
    public ResponseEntity<List<ParkingDto>> getAllParkings() {
        return new ResponseEntity<>(parkingService.getAll().stream()
                .map(parkings -> modelMapper.map(parkings, ParkingDto.class))
                .collect(Collectors.toList()), HttpStatus.OK);
    }

    @PutMapping("/{id}")
    public ResponseEntity<ParkingDto> updateParking(@PathVariable Long id, @RequestBody ParkingCommand parkingCommand)  {
        return new ResponseEntity<>(modelMapper.map(parkingService.updateParking(id, modelMapper.map(parkingCommand, Parking.class)), ParkingDto.class), HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public HttpStatus deleteParking(@PathVariable Long id) throws Exception {
        parkingService.deleteParking(id);
        return HttpStatus.OK;
    }
}

ParkingCommand :

import com.example.lesson31.domain.ParkingType;
import lombok.Data;

@Data
public class ParkingCommand {
    private String name;
    private int numberOfPlaces;
    private int numberOfChargers;
    private ParkingType parkingType;
    private int lengthParkingSpot;
    private int widthParkingSpot;
}

ParkingDto :

import com.example.lesson31.domain.ParkingType;
import lombok.Data;

@Data
public class ParkingDto {
    private String name;
    private int numberOfPlaces;
    private ParkingType parkingType;

}

Car :

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDate;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String make;
    private String model;
    private double price;
    private double length;
    private double width;
private CarCombustibleType carCombustibleType;
    private LocalDate year;

    @ManyToOne
    @JoinColumn(name = "parking_id")
    private Parking parking;
}

CarService:

import com.example.lesson31.domain.Car;
import com.example.lesson31.repository.CarRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class CarService {

    private final CarRepository carRepository;

    public Car save(Car car) {
        return carRepository.save(car);
    }

    public List<Car> getAll() {
        return carRepository.findAll();
    }

    public Car updateCar(Long id, Car car) throws Exception {
        Car carForUpdate = carRepository.findById(id)
                .orElseThrow(() -> new Exception("Car Id not found"));
        carForUpdate.setMake(car.getMake());
        carForUpdate.setModel(car.getModel());
        carForUpdate.setPrice(car.getPrice());
        carForUpdate.setLength(car.getLength());
        carForUpdate.setWidth(car.getWidth());
        carForUpdate.setCarCombustibleType(car.getCarCombustibleType());
        return carRepository.save(car);

    }

    public void deleteCar(Long id) throws Exception {
        Car carForDelete = carRepository.findById(id)
                .orElseThrow(() -> new Exception("Car id not found"));
        carRepository.deleteById(carForDelete.getId());
    }
}

CarController:

import com.example.lesson31.command.CarCommand;
import com.example.lesson31.command.ParkingCommand;
import com.example.lesson31.domain.Car;
import com.example.lesson31.dto.CarDto;
import com.example.lesson31.repository.CarRepository;
import com.example.lesson31.service.CarService;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/car")
@RequiredArgsConstructor
public class CarController {

    public final CarService carService;
    public final CarRepository carRepository;
    public final ModelMapper modelMapper;

    @PostMapping
    public ResponseEntity<CarDto> saveCar(@RequestBody CarCommand carCommand) {
        return new ResponseEntity<>(modelMapper.map(carService.save(modelMapper.map(carCommand, Car.class)), CarDto.class), HttpStatus.CREATED);
    }

    @GetMapping
    public ResponseEntity<List<CarDto>> getAllCars() {
        return new ResponseEntity<>(carService.getAll()
                .stream()
                .map(car -> modelMapper.map(car, CarDto.class))
                .collect(Collectors.toList()), HttpStatus.OK);
    }

    @PutMapping("/{id}")
    public ResponseEntity<CarDto> updateCar(@PathVariable Long id, @RequestBody ParkingCommand parkingCommand) throws Exception {
        return new ResponseEntity<>(modelMapper.map(carService.updateCar(id, modelMapper.map(parkingCommand, Car.class)), CarDto.class), HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public HttpStatus deleteCar(@PathVariable Long id) throws Exception {
        carService.deleteCar(id);
        return HttpStatus.OK;
    }

}

CarCommand:

import lombok.Data;

import java.time.LocalDate;

@Data
public class CarCommand {

    private String make;
    private String model;
    private double price;
    private double length;
    private double width;
    private String carCombustibleType;
    private LocalDate year;
}

CarDto:

import lombok.Data;

import java.time.LocalDate;

@Data
public class CarDto {

    private String make;
    private String model;
    private double price;
    private LocalDate year;
    private CarCombustibleType carCombustibleType;
}

I add car and parking by POSTMAN POST request into database.

See example of car and parking :

Car :

{
"make" :  "Fiat",
"model" :  "Abarth",
"price" :  "21500",
"width" :  "3.5",
"length" :  "3.5",
"carCombustibleType" :"0",(0= BENZINE, 1 = LPG, 2 =ELECTRIC, 3 = DIESEL)
"year" :  "2020-08-15"
}

Parking:

}
    "name": "WestParking",
    "numberOfPlaces": 25,
    "numberOfChargers": 5,
    "parkingType": "1",  //(1= OUTSIDE / 2= UNDERGROUND)
    "length": 3.5,
    "width": 3.0
}

When I want to park a car inside the parking by POSTMAN I get the error:

Cannot invoke "com.example.lesson31.domain.CarCombustibleType.equals(Object)" because the return value of "com.example.lesson31.domain.Car.getCarCombustibleType()" is null

This is my JSON postman POST request:

http://localhost:8080/parking/1

With the follozing body

{
"id" : "1"
}

Can anyoane advice what I do wrong ?


Solution

  • Your http://localhost:8080/parking/1 endpoint expects a CarCommand object as a request body, like the one you've shown:

    {
    "make" :  "Fiat",
    "model" :  "Abarth",
    "price" :  "21500",
    "width" :  "3.5",
    "length" :  "3.5",
    "carCombustibleType" :"0",(0= BENZINE, 1 = LPG, 2 =ELECTRIC, 3 = DIESEL)
    "year" :  "2020-08-15"
    }
    

    But when you POST

    {
    "id" : "1"
    }
    

    the fields of the carCommand used in

    @PostMapping("/{id}")
        public ResponseEntity<ParkingDto> parkCar(@PathVariable Long id, @RequestBody CarCommand carCommand) throws Exception {
            return new ResponseEntity<>(modelMapper.map(parkingService.parkCarIntoParking(id, modelMapper.map(carCommand, Car.class)), ParkingDto.class), HttpStatus.OK);
        }
    

    are set to null, thus the modelMapper-mapped Car instance also has null fields and thus your error the return value of "com.example.lesson31.domain.Car.getCarCombustibleType()" is null

    The proper way to make sure the request data you receive is valid is by using bean validation. See https://www.baeldung.com/spring-boot-bean-validation for more info.