Home > Mobile >  Spring Repository in component is null even with @Autowire
Spring Repository in component is null even with @Autowire

Time:11-01

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);
    }
}
  • Related