Home > database >  Why this Spring Data JPA query by name method gives me this error and Eclipse forces me to return an
Why this Spring Data JPA query by name method gives me this error and Eclipse forces me to return an

Time:01-10

I am working on a Spring Boot project using Spring Data JPA and Hibernate mapping. In my repository classes I am using a query by method name approach and I have the following question. I have this User entity class:

@Entity
@Table(name = "portal_user")
@Getter
@Setter
public class User implements Serializable {
     
    private static final long serialVersionUID = 5062673109048808267L;
    
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    
    @Column(name = "first_name")
    @NotNull(message = "{NotNull.User.firstName.Validation}")
    private String firstName;
    
    @Column(name = "middle_name")
    private String middleName;
    
    @Column(name = "surname")
    @NotNull(message = "{NotNull.User.surname.Validation}")
    private String surname;
    
    @Column(name = "sex")
    @NotNull(message = "{NotNull.User.sex.Validation}")
    private char sex;
    
    @Column(name = "birthdate")
    @NotNull(message = "{NotNull.User.birthdate.Validation}")
    private Date birthdate;
    
    @Column(name = "tax_code")
    @NotNull(message = "{NotNull.User.taxCode.Validation}")
    private String taxCode;
    
    @Column(name = "e_mail")
    @NotNull(message = "{NotNull.User.email.Validation}")
    private String email;
    
    @Column(name = "pswd")
    @NotNull(message = "{NotNull.User.pswd.Validation}")
    private String pswd;
    
    @Column(name = "contact_number")
    @NotNull(message = "{NotNull.User.contactNumber.Validation}")
    private String contactNumber;
    
    @Temporal(TemporalType.DATE)
    @Column(name = "created_at")
    private Date createdAt;
    
    @Column(name = "is_active")
    private boolean is_active;
    
    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
    @JsonManagedReference
    private Set<Address> addressesList = new HashSet<>();
    
    @ManyToMany(cascade = { CascadeType.MERGE })
    @JoinTable(
        name = "portal_user_user_type", 
        joinColumns = { @JoinColumn(name = "portal_user_id_fk") }, 
        inverseJoinColumns = { @JoinColumn(name = "user_type_id_fk") }
    )
    Set<UserType> userTypes;


    @ManyToOne(fetch = FetchType.EAGER)
    @JsonProperty("subagent")
    private User parent;

    public User() {
        super();
        // TODO Auto-generated constructor stub
    }


    public User(String firstName, String middleName, String surname, char sex, Date birthdate, String taxCode,
            String email, String pswd, String contactNumber, Date createdAt, boolean is_active) {
        super();
        this.firstName = firstName;
        this.middleName = middleName;
        this.surname = surname;
        this.sex = sex;
        this.birthdate = birthdate;
        this.taxCode = taxCode;
        this.email = email;
        this.pswd = pswd;
        this.contactNumber = contactNumber;
        this.createdAt = createdAt;
        this.is_active = is_active;
    }
    

}

Then I have this repository interface:

public interface UsersRepository extends JpaRepository<User, Integer> {
    
    /**
     *  Retrieve an user by its e-mail address:
     * @param email is the e-mail of the user
     * @return the user related to the specified e-mail
     */
    User findByemail(String email);
    
    /**
     * Retrieve the list of users belonging to a specific user type
     * @param typeName is the name of the user type
     * @return the list of users belonging to a specific user type
     */
    List<User> findByUserTypes_TypeName(String typeName);
    
    /**
     *  Retrieve the list of users children of an user. 
     *  Generally used to retrieve the client users of a specific sub-agent user
     * @param id is the id of the parent user
     * @return the list of children of a specific user (generally used to retrieve the client users of a specific sub-agent user)
     */
    List<User> findByParent_Id(Integer id);
    
    
    /**
     * Retrieve an user starting from is id
     * @param id of the user which you want to retrieve
     * @return the retrieved object
     */
    Optional<User> findById(Integer id);

}

Om my doubt is on the last method (intended to retrieve an User object by its own id field value, this one:

Optional<User> findById(Integer id);

Originally I tried to define it simply as:

User findById(Integer id);

But doing in this way Eclipse\STS give me the following error:

Multiple markers at this line
    - implements org.springframework.data.repository.CrudRepository<com.easydefi.users.entity.User,java.lang.Integer>.
     findById
    - The return type is incompatible with CrudRepository<User,Integer>.findById(Integer)
    - The return type is incompatible with CrudRepository<User,Integer>.findById(Integer)

Why this error? and why Eclipse force me to return Optional<User> as returned type? What is this Optional? And why in the findByemail() allow me to return a simple User type?

CodePudding user response:

As you can see in the JavaDoc of CrudRepository interface which JpaRepository extends, there is already a findById method with the same signature.

Java overriding rules do not allow you to have a method with the same name and parameters and a return type that is not consistent with the parent class definition (in this case, Optional<User>).

If you somehow change the method name, Spring Data JPA will be able to use a plain reference. In this case, null will be returned if the entity hasn't been found. For you case however, you should just drop your findById method.

CodePudding user response:

Your repository extends JpaRepository. JpaRepository extends PagingAndSortingRepository that extends CrudRepository. CrudRepository has the method: Optional<T> findById(ID var1);. https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html

When you define a method with this signature you are overriding the CrudRepository method. But if you define a different return type for this method created in your repository, your code won't compile.

Optional was created in Java 8, in resume, is used to indicate that the return data of method may be null. Is a good practice to use Optional for these scenarios. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

About another methods that you mentioned, they are just methods of your UsersRepository that you can create as you want.

CodePudding user response:

In short, when you try to read a repository by PKID defined in that entity, you need to use Optional<YourEntity>. JPA returns you an optional object. You have to check if that object really has any value by checking like this:

Optional<User>userOptional= usersRepository.findById(id);
if(userOptional.isPresent())
{
  User user= userOptional.get();
}
else{
   //no  such object found for that id
}

findById(...) method returns Optional by the choice of the jpa specification.

  •  Tags:  
  • Related