Repository method only works returning the whole e

2019-07-16 12:13发布

I cannot return specific properties of an entity class from a repository method.

Exception stacktrace

Resolving exception from handler 
[public org.springframework.http.ResponseEntity<java.lang.Object>            
org.springframework.data.rest.webmvc.RepositorySearchController.executeSearch
(
org.springframework.data.rest.webmvc.RootResourceInformation,
org.springframework.util.MultiValueMap<java.lang.String, java.lang.Object>, 
java.lang.String,

org.springframework.data.rest.webmvc.support.DefaultedPageable,
org.springframework.data.domain.Sort,org.springframework.data.rest.webmvc.Persis
tentEntityResourceAssembler
)
]
 : java.lang.IllegalArgumentException: PersistentEntity must not be null!

Entity class

@Entity
@Table(name="reference_data")
@NamedQueries({
  @NamedQuery(name="MyTable.findX", query = "SELECT t.name FROM MyTable t WHERE t.catId = 330 ")
})
public class MyTable implements Serializable { 
  @Id
  private Long id;

  @Column(name="cat_id")
  private Long catId;

  @Column(name="name")
  private String name;
}

Repository interface

@RepositoryRestResource(collectionResourceRel = "myTables", path = "myTables")
//public interface MyTableDataRepository extends PagingAndSortingRepository<MyTable, Long> {  // This didn't work.
public interface MyTableDataRepository extends JpaRepository<MyTable, Long> {
  //@Query("SELECT a.name FROM MyTable a WHERE a.catId = 330 ") -- This didn't work.
  //@Query(value= "SELECT r.name FROM my_table r ", nativeQuery = true) -- This didn't work either.
  @Query("SELECT DISTINCT a.name FROM MyTable a WHERE a.catId = 330 ") // This didn't work either.
  List<String> findX();  // This didn't work.

  //List<MyDTO> findX(); // DTO with one property: String name.. didn't work either.
}

As you can see, none of the repository methods above works even though they should according to thousands of examples.

The only way I have got it working is as follows:

@Query("SELECT a FROM MyTable a WHERE a.catId = 330") // This WORKS
List<MyTable> findX();  // This WORKS

Technical stack

+-------------------------------+-----------------+
| Dependency                    | Version         |
+-------------------------------+-----------------+
| spring-boot-starter-web       | 1.3.5.RELEASE   |
| spring-boot-starter-data-jpa  | 1.3.5.RELEASE   |
| spring-boot-starter-data-rest | 1.3.5.RELEASE   |
| HikariCP                      | 2.4.6           |
| postgresql                    | 9.3-1102-jdbc41 |
| JDK                           | 8               |
+-------------------------------+-----------------+

I'd really appreciate any help in getting the query to return only selected properties to work.

3条回答
Deceive 欺骗
2楼-- · 2019-07-16 12:44

This stems from a misunderstanding of what Spring Data REST can do. Whilst you can do what you are describing through direct repository access, you are accessing this method through the automatically exposed REST endpoint, and these only work on mapped classes. This means that any repository methods you expose with Spring Data REST need to return entity types, i.e. those which are annotated with @Entity which java.lang.String is not.

Whilst you could create a String class wrapper that is also a database entity, as is suggested here this is a terrible approach, and totally unnecessary.

The proper way to achieve what you want is to use projections. You would define a projection interface exposing the fields you want like so:

@Projection(name = "myTableNameProjection", types = MyTable.class)
public interface MyTableNameProjection {
    String getName();
}

And then, when you make the request, specify that it is the projection you are interested in.

curl 'http://localhost:8080/mytables?projection=myTableNameProjection'

The results returned will be projections only including the names of the entities. This works equally on individual requests (e.g. /mytables/1) or repository search methods.

Here is a related question explaining projections, here is the relevant section of the documentation, and here is an example project.

查看更多
闹够了就滚
3楼-- · 2019-07-16 12:44

I think the return value should not be List<String> but List<Object[]>.

查看更多
男人必须洒脱
4楼-- · 2019-07-16 12:56

folks, I have tried with a lot of possible work-arounds (tkxs for your inputs), but under spring-data-rest just the whole entity works otherwise I get: 'cause": null message: PersistentEntity must not be null!' .. this needs to be written in some place.. just to avoid a headache you know.
Also, according to spring-data-* documentation we can use @RestResource(exported = false) to hide query methods and entities' fields in HAL.. cuNextChallenge.

查看更多
登录 后发表回答