Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring data REST findBy nested entity

I have a Spring Boot project using Spring-data-rest for generating my REST interface and I am trying to allow pagination for nested resources. I followed this workaround and got stuck with implementing the findBy query.

I have the following Device entity:

@Entity
@Table(name = "devices")
public class Device extends ResourceSupport {

  ...

  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "user_id")
  private User user;
}

I need to query it using userId:

@RequestMapping(path = "/users/{id}/devices", method = RequestMethod.GET)
public ModelAndView getUserDevices(@PathVariable Integer id) {
    return new ModelAndView("forward:/devices/search/findByUserId?userId=" + id);
}

So I created the following method in my DeviceRepository:

@Repository
public interface DeviceRepository extends JpaRepository<Device, Long> {

  @PreAuthorize("hasRole('ROLE_ADMIN') OR @securityCheck.check(#user, authentication)")
  Page<Device> findByUserId(@Param("user") User user, Pageable pageable);

}

But I am getting the following exception when trying to send request to this endpoint:

Unable to locate Attribute  with the the given name [id] on this ManagedType [com.example.User]

Does anyone have some suggestion what I am doing wrong?

EDIT: Thank you for your responses, I changed the method to

Page<Device> findByUser_UserId(@Param("user") User user, Pageable pageable);

My user entity contains a userId field.

However, now I am getting the following exception:

{
    "cause": {
        "cause": {
            "cause": null,
            "message": "Cannot resolve URI 1. Is it local or remote? Only local URIs are resolvable."
        },
        "message": "Failed to convert from type [java.net.URI] to type [@org.springframework.data.repository.query.Param com.example.User] for value '1'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 1. Is it local or remote? Only local URIs are resolvable."
    },
    "message": "Failed to convert 1 into me.originalsin.user.model.User!"
}

So it seems I am using the query wrongly?

like image 800
Smajl Avatar asked Dec 14 '17 09:12

Smajl


2 Answers

Please have a look at the docs.

Property expressions can refer only to a direct property of the managed entity, as shown in the preceding example. At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties. Assume a Person has an Address with a ZipCode. In that case a method name of

List<Person> findByAddressZipCode(ZipCode zipCode);

[...] Although this should work for most cases, it is possible for the algorithm to select the wrong property. Suppose the Person class has an addressZip property as well. The algorithm would match in the first split round already and essentially choose the wrong property and finally fail (as the type of addressZip probably has no code property). To resolve this ambiguity you can use _ inside your method name to manually define traversal points. So our method name would end up like so:

List<Person> findByAddress_ZipCode(ZipCode zipCode);

That means: what you did should work, except the case your Device class has a field userId.

The exception you showed to us was the following in the original question.

Unable to locate Attribute with the the given name [id] on this ManagedType [com.example.User]

This looks like your class User does not have a field called id. Since you did not show us the code of this class i cant tell if that's the case or not. (Make sure that you imported the correct User class in your repository)

Question has been updated and therefore ANOTHER error exists with ANOTHER answer

The problem now is that you pass a User object to your repository method but filter by an id. Just change the first parameter to the datatype of the user id. I think that should work.

Assuming the type of id is int the method could look like this

Page<Device> findByUser_UserId(@Param("userId") int userId, Pageable pageable);
like image 174
Yannic Klem Avatar answered Nov 14 '22 21:11

Yannic Klem


Try

 Page<Device> findByUser_Id(@Param("user") User user, Pageable pageable);

Since you are creating a method in DeviceRepository, and you want to search on a nested filed(user.id), You need to reference it using "_"

like image 2
pvpkiran Avatar answered Nov 14 '22 22:11

pvpkiran