Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why to use @AllArgsConstructor and @NoArgsConstructor together over an Entity?

Tags:

I have seen multiple codes online on applications of Spring Boot in IntelliJ, and many use both @AllArgsConstructor and @NoArgsConstructor together and both are constructors however the purpose of each is different -

  • @AllArgsConstructor generates a constructor requiring argument for every field in the annotated class
  • @NoArgsConstructor generates a constructor with no parameter

Then why are we using both together over the same entity and how do they function in such case?

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Entity
public class Product {
    @Id
    private int id;
    private String name;
    private String type;
}
like image 455
NIKITA RATH Avatar asked Jul 09 '21 09:07

NIKITA RATH


2 Answers

These are the annotations from Lombok. To understand why it's is needed you have to understand how things work internally.

JPA says
It's specification says "The JPA specification requires that all persistent classes have a no-arg constructor. This constructor may be public or protected. Because the compiler automatically creates a default no-arg constructor when no other constructor is defined, only classes that define constructors must also include a no-arg constructor."

To understand further, when it creates and entity using reflection it uses Class.newInstance() method which requires a no-argument constructor to create an instance.

The most common type of dependency injection used by Spring is

  1. Constructor based Injection
  2. Setter based Injection

Constructor based Injection : When you create object by passing all parameters, you basically use a constructor injection. It should be done when we have all parameter values and we want to create an object with all values initialized.(@AllArgsConstructor)

Setter based Injection: We create an object first (uses no arg-constructor) and then update the dependencies or values later by using setters.(@NoArgsConstructor)

There are many key differences between constructor injection and setter injection.

  • Partial dependency: can be injected using setter injection but it is not possible by constructor. Suppose there are 3 properties in a class, having 3 arg constructor and setters methods. In such case, if you want to pass information for only one property, it is possible by setter method only.

  • Overriding: Setter injection overrides the constructor injection. If we use both constructor and setter injection, IOC container will use the setter injection.

  • Changes: We can easily change the value by setter injection. It doesn't create a new bean instance always like constructor. So setter injection is flexible than constructor injection.

like image 114
Tris Avatar answered Oct 01 '22 22:10

Tris


The JPA specification requires that all persistent classes (@Entity) have a no-arg constructor, public or protected. (note that this is not necessarily true when dealing with some implementation like Hibernate, see this answer).

This is needed because JPA uses the default constructor method to create a bean class using the reflection API. Indeed if your class would contain many constructors then JPA wouldn't know which one to call, this is why it instantiates the class through its no-arg constructor using reflections :

Product.class.newInstance();

which is equivalent to new Product() (Product.class is a class literal, it can fail at runtime if the class is not found in the classpath), then, once instantiated, uses fields setters to deal with it.

Then, in Java, a default constructor (no-argument constructor) is automatically generated for a class unless you define other constructors (it only does when you don't provide any other constructor).

So because the compiler automatically creates a default no-arg constructor when no other constructor is defined, only classes that define constructors must also include a no-arg constructor if required by a framework (here JPA). This is why you need to add @NoArgsConstructor annotation if you add the @AllArgsConstructor annotation.

Also note that you are using @Data, which bundles the features of @RequiredArgsConstructor, which will generate a constructor for all final or @NonNull annotated fields (see Lombok documentation). So as you are using only non final nullable fields, it may generate an empty constructor even if you don't add the @NoArgsConstructor annotation. I have not tested that last case though, I know that it generates an empty constructor when using @RequiredArgsConstructor directly with non final nullable fields, but I don't know if it works the same when using @Data.

@Data also bundles @ToString, so you don't need to add it again.

I'm personnaly not a fan of using @Data if I don't need all the bundled annotations, so I usually simply use :

@Entity
@Getter
@Setter
@EqualsAndHashCode
public class Product {

    @Id
    private int id;

    private String name;

    private String type;

}

as I often don't use toString() nor parameterized constructor. It may be more verbose but more meaningful to me.

like image 31
Yann39 Avatar answered Oct 01 '22 21:10

Yann39