25

I made a projection which should expose nested entities:

@Projection(name = "inlineBusiness", types = { UserModel.class })
public interface InlineBusinessUserModelProjection {

    String getUsername();

    String getFirstName();

    String getLastName();

    Date getBirthdate();

    String getEmail();

    BusinessModel getBusiness();
}

And the service repository:

@RepositoryRestResource(collectionResourceRel = "users", path = "users",
       excerptProjection = InlineBusinessUserModelProjection.class)
public interface UserRepository extends BaseDAO<UserModel> {..}

for /users it works fine, the business field is exposed with nested entity, but when I call /users/1- nothing, also all the custom methods.. Seems like projection isn't involved on any methods except of /users Any ideas?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
nKognito
  • 6,297
  • 17
  • 77
  • 138

2 Answers2

42

That works as designed. An excerpt projection is used whenever an instance of the target type (UserModel in your case) is used within in an _embedded clause. Thus the excerpt is some kind of preview used everywhere the resource itself is not rendered but pointed to. This is usually the case from collection resources or for associations.

Using an excerpt projection by default on an item resource doesn't really make sense from another point of view: excerpt projections are a read-only view on some domain object. If you return that view for an item resource by default, how would a client know which data it had to send to update the resource. A JSON document created for an excerpt projection, can't be simply taken, modified and used to send a PUT request to update the resource – by definition.

If you want to apply a projection to the item resource, populate the projection URI template variable with the name of the projection.

EDIT: In case the projections don't get applied if you manually select them make sure InlineBusinessUserModelProjection is actually registered for general use. Be sure the type is located in the very same package or sub-package of UserModel. Alternatively manually register the projection via RepositoryRestConfiguration.projectionConfiguration().addProjection(…). Manual configuration makes the use of @Projection on the projection type obsolete.

Read more on this topic in the Spring Data REST reference documentation.

Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • Thanks, now I understand. But somehow even when I provide the `?projection=inlineBusiness` to `/users/1` - it doesn't work.. Inside `/alps/users/` also there is no any notification of inlineBusiness.. – nKognito May 18 '15 at 08:01
  • `/users/1/projection=inlineBusiness` also gives nothing – nKognito May 18 '15 at 08:19
  • I've edited my answer with a remark on registration of projections for general usage. – Oliver Drotbohm May 18 '15 at 11:08
  • Great! Thank you so much. By the way, it is better to update the docs with "manual registration of projections" section. The other point - there is no there any information that projection must be within the sand package or sub package of entities. – nKognito May 18 '15 at 19:08
  • @nKognito note that the `?projection=...` is case sensitive and matches the name in your `@Projection(name = ...)` annotation. If you get the case wrong, you get a blank page. – toast38coza Jul 05 '16 at 15:25
  • I agree with the reasoning that "excerpt projections are a read-only view on some domain object" from a server/rest/HATEOAS point of view. But when you think from a web-client point of view it is different: When you inline a related entities with a projection, e.g. a line item references the product, then you most likely will always want to get the inlined version. including the data about the referenced product, its size and price. The execptional case is that you want to update the lineitem (eg. chagne the quantity). In normal cases the client will want to "view". – Robert Jan 08 '17 at 20:56
  • Actually there should be two distinct things: (1) Hide some fields: "excerpt" projections. (2) Inline information about related entities: This should be configurable, eg. with an annotation on the @OneToMany field. – Robert Jan 08 '17 at 21:04
  • Related topic: http://stackoverflow.com/questions/29670835/spring-data-rest-include-nested-resource-in-embedded/42293983#42293983. – pdorgambide Feb 17 '17 at 09:21
  • @OliverGierke I have an issue related to this. I have my projection working successfully in a single uri call e.g: **/api/users/1?projection=withAddress.** but if I call to **/api/users?projection=withAddress** it wont work. It is the only way to get this working using an excerpt? – Tirias Mar 06 '17 at 12:28
  • Sorry for double re-post. Finally upgrading Spring Data REST version to 2.6.1 and Spring Data JPA to the last version solved the issue. Sorry! – Tirias Mar 07 '17 at 16:54
  • 1
    @OliverGierke According to the latest Spring Data REST documentation, the @Projection does never get obsolete: `In either situation, the interface with your projection MUST have the @Projection annotation.` – theFriedC Mar 27 '17 at 12:58
2

I'm not going to argue with @Oliver Drotbohm, as this is indeed the correct answer, however, if you want a cheeky workaround, just define a getter for the entity field with a slightly different name (using the OP's example):

BusinessModel getBusinessInline() { return this.businessModel; }

Will result in a JSON payload of:

{
    .
    .
    "businessInline": {"name":"stuff etc"}
    .
    .
}

Presuming your consuming service will accept that, then its an option.

Not big or clever, but it works.

Sparm
  • 529
  • 1
  • 6
  • 14