I'm new to spring but I'm trying to make a web application from which I can add different entries to a database with multiple tables - I have two tables, for now, batch and type - My code for type works and I've tested it using a small python script. But when I try to make a batch (And here I need to access the type-table(The repository in Java)) I get a nullPointerExecption at by repository.
I've searched multiple places to find a solution but none have worked for me.
Here are the different classes that I use to create a new batch:
BatchController:
package org.example.BeerMachine.web;
import org.example.BeerMachine.data.models.Batch;
import org.example.BeerMachine.data.payloads.request.BatchRequest;
import org.example.BeerMachine.data.payloads.response.MessageResponse;
import org.example.BeerMachine.service.BatchService;
import org.example.BeerMachine.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/batch")
public class BatchController {
@Autowired
BatchService batchService;
@PostMapping("/add")
public ResponseEntity<MessageResponse> addBatch(@RequestBody BatchRequest batch){
MessageResponse newBatch = batchService.createBatch(batch);
return new ResponseEntity<>(newBatch, HttpStatus.CREATED);
}
}
BatchService:
package org.example.BeerMachine.service;
import org.example.BeerMachine.data.models.Batch;
import org.example.BeerMachine.data.payloads.request.BatchRequest;
import org.example.BeerMachine.data.payloads.response.MessageResponse;
import org.example.BeerMachine.data.repository.BatchRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
@Service
public class BatchServiceImpl implements BatchService {
@Autowired
BatchRepository batchRepository;
@Override
public MessageResponse createBatch(BatchRequest batchRequest) {
Batch newBatch = new Batch();
newBatch.setAmount(batchRequest.getAmount());
newBatch.setType(batchRequest.getType());
batchRepository.save(newBatch);
return new MessageResponse("New Batch created successfully");
}
}
BatchRequest NOTE: It is here I get my nullPointerException:
package org.example.BeerMachine.data.payloads.request;
import org.example.BeerMachine.data.models.Type;
import org.example.BeerMachine.data.repository.TypeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import javax.validation.constraints.NotNull;
import java.util.List;
@Component
public class BatchRequest {
@Autowired
private TypeRepository typeRepository; //This is null
@NotNull
private Integer amount;
@NotNull
private Integer type_id;
public Integer getAmount() {
return amount;
}
public Integer getType_id() {
return type_id;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
public Type getType() {
//Here is the nullPointerExecption
List<Type> typeList = typeRepository.findAll();
for (Type type : typeList) {
if (type.getId().equals(getType_id()))
return type;
}
return null;
}
public void setType(int t) {
this.type_id = t;
}
}
My TypeRepository:
package org.example.BeerMachine.data.repository;
import org.example.BeerMachine.data.models.Type;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface TypeRepository extends JpaRepository<Type, Integer> {
//This was only added to test it - Saw it in another response. Didn't work
List<Type> findAll();
}
Note: I have the same structure for my BatchRepository
If It helps, here is my model class for batch:
package org.example.BeerMachine.data.models;
import javax.persistence.*;
import java.util.Objects;
@Entity
@Table(name="batch")
public class Batch {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private Integer amount;
@ManyToOne(optional = false)
@JoinColumn(name = "type_id", nullable = false)
private Type type;
public Batch() {}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getAmount() {
return amount;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
@Override
public String toString() {
return "Batch{"
"id=" id
", amount=" amount
", type=" type
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Batch batch = (Batch) o;
return id.equals(batch.id) &&
amount.equals(batch.amount) &&
type == batch.type;
}
@Override
public int hashCode() {
return Objects.hash(id, amount, type);
}
}
If you want to see how I use the above code, see the python-script below:
import requests
url1 = 'http://localhost:8081/batch/add'
batch = {'amount': 19,
'type_id': 6}
x = requests.post(url1, json = batch)
Above, A user enters the type_id and my code needs to fine the Type based on the id(The id from the database)
One final note: I have the same classes for type and almost the same methods there also - Not sure if that is to any use in this case
CodePudding user response:
The instance of BatchRequest that is passed to your controller was only the result of deserialization from the request. Meaning spring did not create an instance of it in the application context, therefore the dependency (TypeRepository) is also not injected.
What you can do is autowire the TypeRepository
in your BatchServiceImpl
and pass it as an argument in the batchService.createBatch(batch,typeRepository)
method.
@Service
public class BatchServiceImpl implements BatchService {
@Autowired
private BatchRepository batchRepository; <----- Autowire here
@Autowired
private TypeRepository typeRepository;
@Override
public MessageResponse createBatch(BatchRequest batchRequest) {
Batch newBatch = new Batch();
newBatch.setAmount(batchRequest.getAmount());
newBatch.setType(batchRequest.getType(typeRepository)); <---- pass here
batchRepository.save(newBatch);
return new MessageResponse("New Batch created successfully");
}
}
CodePudding user response:
BatchRequest
should not be a @Component
, it should be a POJO and not a Spring-managed Bean. As @Ruelos Joel wrote, BatchRequest batchRequest
in your createBatch()
method is the result of the deserialization of the POST request body you performed to test your code.
Having written this, your BatchRequest
should look as follows:
public class BatchRequest {
@NotNull
private Integer amount;
@NotNull
private Integer type_id;
public Integer getAmount() {
return amount;
}
public Integer getType_id() {
return type_id;
}
public void setAmount(Integer amount) {
this.amount = amount;
}
public void setType_id(int t) {
this.type_id = t;
}
}
This means that the logic you have in public Type getType()
method should be placed elsewhere, e.g. in a TypeService
:
@Service
public class TypeService {
@Autowired
private TypeRepository typeRepository;
public Type getType(int typeId) {
return typeRepository.findById(typeId);
}
}
You actually don't need the logic you had, simply because JpaRepository
already has a method available for you to get an entity given its ID.
Finally, you need to adjust your BatchServiceImpl
to use the new TypeService
as follows:
@Service
public class BatchServiceImpl implements BatchService {
@Autowired
TypeService typeService;
@Autowired
BatchRepository batchRepository;
@Override
public MessageResponse createBatch(BatchRequest batchRequest) {
Batch newBatch = new Batch();
newBatch.setAmount(batchRequest.getAmount());
newBatch.setType(typeService.getType(batchRequest.getType_id()));
batchRepository.save(newBatch);
return new MessageResponse("New Batch created successfully");
}
}
As a side note, if you want @NotNull
to have any effect on validation you must use @Valid
in your Controller
as follows:
@RestController
@RequestMapping("/batch")
public class BatchController {
@Autowired
BatchService batchService;
@PostMapping("/add")
public ResponseEntity<MessageResponse> addBatch(@Valid @RequestBody BatchRequest batch){
MessageResponse newBatch = batchService.createBatch(batch);
return new ResponseEntity<>(newBatch, HttpStatus.CREATED);
}
}