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

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;
}

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.

Related

Java Annotations public/private scope

In the following document, detailing a study from 2009 regarding annotation validations, the following is stated:
http://www.ii.uib.no/~federico/latex/annotationlimitations2.pdf
Let us point out that in practice it can be possible to annotate
directly a field of an object as they do in [3]:
#IntRange(min=1,max=100000)
private int Amount;
However we decided not to offer this possibility for a simple reason:
if the field is private, the framework must first change its
visibility to public by means of reflection, before to be able to
retrieve its value. We consider a very bad practice to allow an
external framework to tamper with the visibility of object properties.
So instead of putting the annotation on the private variable declaration as in the above example, they put the annotation on the public getter() method instead.
public class WebForm {
private int Amount;
...
#IntRange(min=1,max=100000)
public int getAmount {
return this.Amount;
}
}
This paper is dated 2009, so I'm wondering does this still apply? If I am using the Hibernate Validator, following JSR-380 & the Bean Validation 2.0 specification, do I need to declare validation annotations at the getter level to avoid the private variable being made public by reflection by the Hibernate framework?
Most/All of the examples I see online do not do this - they are happy to put the annotation above the private variable declaration.
Using reflection to change the visibility of a field doesn't actually change the visibility of that field. It returns a copy of the field that is public. No this does still not apply.
This paper never applied to Hibernate Validator and Bean Validation. We support annotations on private fields and we use reflection to access the content of the field.
Indeed we do that by making the field accessible to our code. This does not change the visibility of the field itself though.

Is is possible to create a combo-annotation which combines multiple field annotations?

I have some boolean fields in my JPA entities which are annotated in the following way:
#Column(length = 1)
#Type(type = "yes_no")
private final boolean myField;
Is it possible to create a combo-annotation (e. g. MyAnnotation) which combines both of this annotations?
#MyAnnotation
private final boolean myField;
How?
Obviously you could create an annotation that provides the equivalent of multiple other annotations.
Equally obviously no JPA provider will support it, since they will check for the javax.persistence annotations only (not that #Type is javax.persistence).
What you want is similar to CDI stereotypes - unfortunately, JPA does not support such a concept, therefore you must copy recurring annotations all over.
If you can afford to wrap your field into an object, you may mark it as #Embeddable and put your field into it - the annotations on that field will be copied wherever you embed that object into an entity. You may extend the annotations using #AnnotationOverrides. Obviously, the drawback of such solution is additional complexity when accessing the field in your entity.

Entity class must have non argumented constructor

I am studying JPA, so while studying on a JPA BLOG Vogella 1.2 Entity Heading, i came across a line which says:
All entity classes must define a primary key, must have a non-arg
constructor and or not allowed to be final
I am not able to understand this line. Do we need to specifically write a non-argumented constructor, because a default non argumented constructor is always inherited by classes.
what do they mean by not allowing to be final, do this mean we can always extend an Entity class? If so why is this compulsion, why it can not be final
By default, you get non arg constructor when there is no constructor defined in your class which has nothing to do with JPA. Yes if you are defining a constructor, then you have to define no arg constructor like:
public class Student {
public Student() {}//default ctor which is needed by JPA to convert row from DB to java object
public Student(int id) {..}//ctor which accepts one arg
}
Making class final meaning you can't subclass. JPA vendor's like Hibernate creates proxy to lazily fetch rows which will ultimately limit your options for performance tuning. Hence you see that your class may/may not be final depending on your use case.

How does updating work with Ebean and Play framework

I'm new to play framework, Ebean, and ORMs in general and I have a question regarding updating fields of a persisted object.
I know that in hibernate when you call the set method of an object, it will automatically call the update method to update in the db. Does Ebean work similarly? With play framework, from what i've read, the getters and setters are generated automatically when the fields are made public. Say I have the following class in my play project:
#Entity
public class Foo extends Model{
public String bar;
}
public static void main(String a[]){
Foo f = new Foo();
f.bar = "foobar";
}
My question has 2 parts:
1) does the assignment f.bar="foobar"; recompile into calling f.setBar("foobar"); within play?
2) And if so, will this assignment automatically call the model's upadte method or do i need to explicitly make update methods for updating each field?
Thanks for the assistance :)
Yes, play creates setter and getters automatically unless you define your custom setters or getters.
In order to persist your data, you have to call model.save() or model.update(), assignments does not automatically update the database.
By the way, I advice you to always write setter and getter for #ID fields, because sometimes it causes very strange errors.

Injecting fields into Java JPA fields

I'm no pro with Java, so I need a little help. I'm using the Play Framework.
I have an Entity class which extends GenericModel with fields like the following:
#Column(name = "description")
private String description;
I want to add an additional field using a getter, let's call it getToString, which basically contains a read only string with the string representation of the entity.
I need this because the object is getting sent as a JSON response, and my JavaScript will read this field, and display it where for example the entity needs to be represented as a string.
How do I go about doing this?
I'm no expert on the Play framework, but probably you should have a look at the #Transient annotation.
Fields (and getters/setters if you are using JPA property access) marked with #Transient will be ignored by JPA, but usually be considered by other frameworks.
The problem I'm having was a side effect of using GsonBuilder. The builder doesn't appear to be parsing getters and setters, unless the source of the library is modified, which I'm not willing to do.
For what I understand (please correct me if I'm wrong) you want a read-only method that will return a string representation (JSon format) of the entity.
You could just override the default toString method:
#Override
public String toString() {
return "your_json_string";
}
and call it when needed

Categories

Resources