Я использую Spring Boot 2.x, Spring Data REST, Spring HATEOAS, Spring JPA, Hibernate.Я знаю о этом вопросе , но он довольно старый, и я хочу лучше детализировать мой случай.
У меня есть две простые бобы, подобные этой:
@Entity
@Data
public class Contact extends AbstractEntity {
private String sid;
private String firstName;
private String lastName;
private String companyName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id", updatable = false)
private Store store;
//more custom fields
}
и:
@Entity
@Data
public class Store extends AbstractEntity {
private String code;
private String name;
private String sid;
//more custom fields
}
Я раскрываю репозиторий через REST:
@Transactional
@PreAuthorize("isAuthenticated()")
public interface ContactRepository extends JpaRepository<Contact, Long> {
}
и
@Transactional
@PreAuthorize("isAuthenticated()")
public interface StoreRepository extends JpaRepository<Store, Long>, StoreCustomRepository<Store, Long> {
}
Если я позвоню http://localhost:8082/api/v1/contacts/1, я получу:
{
"sid": "962732c2-68a8-413b-9762-f676d42046b4",
"createdBy": "1ccf2329-4aa3-4d55-8878-25517edf1522",
"createdDate": "2019-05-28T14:06:07.011Z",
"lastModifiedDate": "2019-06-04T08:46:02.591Z",
"lastModifiedBy": "system",
"createdByName": "Agent",
"lastModifiedByName": null,
"type": "CUSTOMER",
"personType": "NATURAL_PERSON",
"firstName": "Daniele",
"lastName": "LastName",
"companyName": null,
"fullName": "Full name",
"gender": "MALE",
"birthDate": "2019-05-21T00:00:00Z",
"birthCity": null,
"job": null,
"billingAddress": "Via 123",
"billingZipCode": "14018",
"billingCity": "Roatto",
"billingDistrict": "AT",
"billingCountry": "IT",
"shippingAddress": "Via 123",
"shippingZipCode": "14018",
"shippingCity": "Roatto",
"shippingDistrict": "AT",
"shippingCountry": "IT",
"taxCode": "",
"vatNumber": null,
"landlinePhone": null,
"mobilePhone": null,
"fax": null,
"email": "aaa@sdfg.it",
"certifiedEmail": null,
"survey": null,
"iban": null,
"swift": null,
"publicAdministration": false,
"sdiAccountId": "0000000",
"preset": false,
"_links": {
"self": {
"href": "http://localhost:8082/api/v1/contacts/1"
},
"contact": {
"href": "http://localhost:8082/api/v1/contacts/1{?projection}",
"templated": true
},
"notes": {
"href": "http://localhost:8082/api/v1/contacts/1/notes"
},
"auditLogs": {
"href": "http://localhost:8082/api/v1/contacts/1/auditLogs"
},
"media": {
"href": "http://localhost:8082/api/v1/contacts/1/media"
},
"privacyAgreements": {
"href": "http://localhost:8082/api/v1/contacts/1/privacyAgreements"
},
"eyeExams": {
"href": "http://localhost:8082/api/v1/contacts/1/eyeExams"
},
"contactLensEyeTests": {
"href": "http://localhost:8082/api/v1/contacts/1/contactLensEyeTests"
},
"eyeExamsCount": {
"href": "http://localhost:8082/api/v1/contacts/1/eyeExams/count"
},
"documents": {
"href": "http://localhost:8082/api/v1/contacts/1/documents"
},
"pendingSalesOrders": {
"href": "http://localhost:8082/api/v1/contacts/1/pendingSalesOrders"
},
"latestPurchasedFrames": {
"href": "http://localhost:8082/api/v1/contacts/1/latestPurchasedFrames"
},
"latestPurchasedItems": {
"href": "http://localhost:8082/api/v1/contacts/1/latestPurchasedItems"
},
"latestSoldItems": {
"href": "http://localhost:8082/api/v1/contacts/1/latestSoldItems"
},
"latestDocuments": {
"href": "http://localhost:8082/api/v1/contacts/1/latestDocuments"
},
"store": {
"href": "http://localhost:8082/api/v1/contacts/1/store{?projection}",
"templated": true
}
}
}
Как описано в документе Spring Data REST, если есть хранилище для соответствующего ресурса (Store), оно отображается в виде ссылки и не встроено.
Я также использую https://github.com/bohnman/squiggly-java для динамического выбора полей.Конечная цель - вызвать конечную точку контакта, выбрав только несколько полей контакта и имея вложенное хранилище, содержащее только код и собственную ссылку.
Поэтому я хотел бы получить ответ, подобный этому:
{
"store": {
"code": "RO",
"_links": {
"self": {
"href": "http://localhost:8082/api/v1/stores/1{?projection}",
"templated": true
}
}
},
"lastName": "LastName",
"_links": {
"self": {
"href": "http://localhost:8082/api/v1/contacts/1"
},
"contact": {
"href": "http://localhost:8082/api/v1/contacts/1{?projection}",
"templated": true
},
"store": {
"href": "http://localhost:8082/api/v1/contacts/1/store{?projection}",
"templated": true
}
}
}
Чтобы иметь вложенный вложенный ресурс, мне нужно было создать проекцию:
@ Projection (name = "inline", types = {Contact.class}) открытый интерфейс ContactInlineProjection {
String getFirstName();
String getLastName();
Store getStore();
//All other fields
}
но в результате отсутствуют ссылки на вложенный ресурс:
{
"store": {
"code": "RO"
},
"lastName": "LastName",
"_links": {
"self": {
"href": "http://localhost:8082/api/v1/contacts/1"
},
"contact": {
"href": "http://localhost:8082/api/v1/contacts/1{?projection}",
"templated": true
},
"store": {
"href": "http://localhost:8082/api/v1/contacts/1/store{?projection}",
"templated": true
}
}
}
Чтобы заставить его работать, мне также пришлось создать проекцию для Store:
@Projection(name = "inline", types = {Store.class})
public interface StoreLightProjection {
String getName();
String getCode();
//other fields
}
и измените проекцию контакта следующим образом:
@Projection(name = "inline", types = {Contact.class})
public interface ContactInlineProjection {
String getFirstName();
String getLastName();
StoreLightProjection getStore();
}
При этом ответ выглядит так, как я хотел.Это нормально или потенциальная ошибка?Есть ли другой способ достичь цели с меньшим количеством кода?