Evan Gertis
Evan Gertis

Reputation: 2062

variable id might not have been initialized Spring Boot Controller with lombok

I am trying to add a simple controller method, but I am running into the following Exercise.java:[13,1] variable id might not have been initialized

Here is the code that I am working with

@RequestMapping(value = "/exercises/{id}")
    public ResponseEntity<Optional<Exercise>> getExerciseById(Model model, @PathVariable Integer id) {
        Optional<Exercise> exercise = exerciseRepository.findById(id);
        if(id!=null)
            return new ResponseEntity<Optional<Exercise>>(exercise, HttpStatus.OK);
        else
            return new ResponseEntity<Optional<Exercise>>(exercise, HttpStatus.NOT_FOUND);
    }

I am using an Optional<Exercise> here because I am taking advantage of the build in method findById from the JpaRepository package. I haven't found any good posts on how to handle this is. This is probably something simple. I've found some documentation around this:https://www.java67.com/2016/07/how-to-fix-variable-might-not-have-been-initialized-error-in-java.html, but I could use a little help understanding the best way to fix this. This is the exercise classe

@Entity
@Table(name = "exercise")
@Value
@NoArgsConstructor
public class Exercise {
    
    @Id
    @NonNull
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    private int chapterId;

    private String exercise;

    private String answer;

    private String question;

    private String a;
    
    private String b;

    private String c;
}

Upvotes: 1

Views: 1955

Answers (1)

xdhmoore
xdhmoore

Reputation: 9925

tldr;

I don't think JPA plays well with the immutable entity created by @Value. Use @Data instead.

ihnbtdttrt (i have nothing better to do than to read this);

This partially guesswork, but since it has seemed to help, this is what I think is happening:

When you call findById(), JPA creates a new entity object using a no-argument constructor, and then sets the fields individually afterwards. (I'm not sure if it uses setters or sets the fields directly using reflection).

The @Value annotation, as documented here, makes a class immutable, with all the fields private and final and with no "setters" created. This means that the only way to set the fields is by passing the field values into a constructor with appropriate arguments. After that the fields are not changeable.

Since JPA initializes entities using the no-args constructor and tries to set the fields afterwards, with your setup, it uses the no-args constructor and ends up with an entity object where none of the fields have been initialized but none of them are modifiable after the constructor. All private, final fields, with no setters. Then it tries to call entity.getId(), and the id field hasn't been initialized, leading to the above error.

To fix this, you can use the @Data annotation instead of @Value. This is similar, but doesn't create an immutable object. In particular, it generates "setter" functions and the fields are not set to final. This is the type of Java bean that JPA expects, one that can be initialized with a no-argument constructor and then have the fields set afterwards.

There may be ways to configure JPA to create objects differently, so that it passes all the data into a constructor so that you can have immutable entities. I know that some Spring DI stuff is configurable to initialize using rich constructors like this, but Idk about JPA.

For what it's worth, I appreciate the value of immutable objects for clean code, but it's not uncommon to find the above pattern of no-arg construction + post-construction setting when using the popular Java frameworks like JPA/Hibernate, Spring, etc. They don't always play well with immutability.

Upvotes: 1

Related Questions