Spring MVC 3: zwraca stronę Spring-Data jako JSON
Mam warstwę dostępu do danych wykonaną za pomocą Spring-Data. Obecnie tworzę na nim aplikację webową. Ta jedna metoda kontrolera powinna zwracać Stronę Spring-Data sformatowaną jako JSON.
Taka strona jest listą z dodatkowymi informacjami stronicowania, takimi jak całkowita ilość rekordów i tak dalej.
Czy to możliwe i jeśli tak to jak?
I bezpośrednio z tym związane Czy Mogę zdefiniować odwzorowanie nazw właściwości? Np. oznacza to, że musiałbym zdefiniować, jak stronicowanie właściwości informacji są nazwane w JSON (inaczej niż w page). Czy to możliwe i jak?
3 answers
Jest wsparcie dla takiego scenariusza nadchodzącego na wiosnę HATEOAS i Spring Data Commons. Spring HATEOAS jest wyposażony w obiekt PageMetadata
, który zasadniczo zawiera te same dane, co Page
, ale w mniej wymuszony sposób, dzięki czemu można go łatwiej skalować i nie skalować.
Innym aspektem, dla którego implementujemy to w połączeniu z Spring HATEOAS i Spring Data commons jest to, że nie ma wartości w prostym rozbudowywaniu strony, jej treści i metadanych, ale chcesz również wygenerować linki do być może istniejących następnych lub poprzednich stron, aby Klient nie musiał konstruować Uri, aby przejść przez te strony.
Przykład
Przyjmij klasę domeny Person
:
class Person {
Long id;
String firstname, lastname;
}
Jak również odpowiednie repozytorium:
interface PersonRepository extends PagingAndSortingRepository<Person, Long> { }
Możesz teraz wyświetlić Kontroler Spring MVC w następujący sposób:
@Controller
class PersonController {
@Autowired PersonRepository repository;
@RequestMapping(value = "/persons", method = RequestMethod.GET)
HttpEntity<PagedResources<Person>> persons(Pageable pageable,
PagedResourcesAssembler assembler) {
Page<Person> persons = repository.findAll(pageable);
return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
}
}
Prawdopodobnie jest tu sporo do wyjaśnienia. Zróbmy to krok po kroku:
- mamy Spring MVC kontroler podłącza do niego repozytorium. Wymaga to skonfigurowania danych Spring (poprzez
@Enable(Jpa|Mongo|Neo4j|Gemfire)Repositories
lub odpowiedniki XML). Metoda kontrolera jest mapowana do/persons
, co oznacza, że będzie akceptować wszystkie żądaniaGET
do tej metody. - Typ rdzenia zwracany z metody to
PagedResources
- Typ od Spring HATEOAS, który reprezentuje pewne treści wzbogacone oLinks
plusPageMetadata
. -
Gdy metoda jest wywoływana, Spring MVC będzie musiał utworzyć instancje dla
Pageable
iPagedResourcesAssembler
. Aby to zadziałało, musisz włączyć obsługę Spring Data web poprzez adnotację@EnableSpringDataWebSupport
, która ma zostać wprowadzona w nadchodzącym milowym etapie Spring Data Commons lub poprzez samodzielne definicje bean (udokumentowane tutaj ).Pageable
zostaną wypełnione informacje z żądania. Domyślna konfiguracja zmieni?page=0&size=10
wPageable
żądanie pierwszej strony o rozmiarze strony 10.PageableResourcesAssembler
pozwala na łatwe obracaniePage
wPagedResources
instancje. Spowoduje to nie tylko dodanie metadanych strony do odpowiedzi, ale także dodanie odpowiednich linków do reprezentacji w zależności od tego, do której strony uzyskujesz dostęp i jak skonfigurowana jest rozdzielczośćPageable
.
@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@EnableJpaRepositories
class ApplicationConfig {
// declare infrastructure components like EntityManagerFactory etc. here
}
Przykładowe zapytanie i odpowiedź
Załóżmy, że mamy 30 Persons
w bazie danych. Możesz teraz uruchomić żądanie GET http://localhost:8080/persons
, a zobaczysz coś podobnego do tego:
{ "links" : [
{ "rel" : "next", "href" : "http://localhost:8080/persons?page=1&size=20 }
],
"content" : [
… // 20 Person instances rendered here
],
"pageMetadata" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
}
Zauważ, że asembler wytworzył poprawny URI, a także pobiera domyślną konfigurację, aby przekształcić parametry w Pageable
dla nadchodzącego żądania. Oznacza to, że jeśli zmienisz tę konfigurację, linki automatycznie dostosują się do zmiany. Domyślnie asembler wskazuje na metodę kontrolera, w której został wywołany, ale można ją dostosować, przekazując niestandardową Link
, która ma być używana jako baza do budowania linków do stron do przeciążeń PagedResourcesAssembler.toResource(…)
metoda.
Outlook
Bity PagedResourcesAssembler
będą dostępne w nadchodzącym przełomowym wydaniu Spring Data Babbage release train. Jest już dostępny w aktualnych migawkach. Możesz zobaczyć działający przykład tego w mojej aplikacji Spring RESTBucks przykładowej aplikacji . Po prostu Sklonuj go, uruchom mvn jetty:run
i zwiń http://localhost:8080/pages
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-26 11:35:45
Oliver, Twoja odpowiedź jest świetna i zaznaczam ją jako odpowiedź. Tutaj tylko dla kompletności, co wymyśliłem na średni czas, który może być przydatny dla kogoś innego.
Używam jQuery Datatables jako widżetu siatki / tabeli. Wysyła bardzo konkretny parametr do serwera i wyłącza bardzo konkretną odpowiedź: zobacz http://datatables.net/usage/server-side .
Aby to osiągnąć, tworzony jest obiekt custom helper odzwierciedlający to, czego oczekuje datatables. Zauważ, że getter i setter musi być nazwany tak, jak są one inaczej produkowane json jest źle (wielkość liter nazwy właściwości i datatables używa tej "pseudo Węgierskiej notacji"...).
public class JQueryDatatablesPage<T> implements java.io.Serializable {
private final int iTotalRecords;
private final int iTotalDisplayRecords;
private final String sEcho;
private final List<T> aaData;
public JQueryDatatablesPage(final List<T> pageContent,
final int iTotalRecords,
final int iTotalDisplayRecords,
final String sEcho){
this.aaData = pageContent;
this.iTotalRecords = iTotalRecords;
this.iTotalDisplayRecords = iTotalDisplayRecords;
this.sEcho = sEcho;
}
public int getiTotalRecords(){
return this.iTotalRecords;
}
public int getiTotalDisplayRecords(){
return this.iTotalDisplayRecords;
}
public String getsEcho(){
return this.sEcho;
}
public List<T> getaaData(){
return this.aaData;
}
}
Druga część to metoda w kontrolerze:
@RequestMapping(value = "/search", method = RequestMethod.GET, produces = "application/json")
public @ResponseBody String search (
@RequestParam int iDisplayStart,
@RequestParam int iDisplayLength,
@RequestParam int sEcho, // for datatables draw count
@RequestParam String search) throws IOException {
int pageNumber = (iDisplayStart + 1) / iDisplayLength;
PageRequest pageable = new PageRequest(pageNumber, iDisplayLength);
Page<SimpleCompound> page = compoundService.myCustomSearchMethod(search, pageable);
int iTotalRecords = (int) (int) page.getTotalElements();
int iTotalDisplayRecords = page.getTotalPages() * iDisplayLength;
JQueryDatatablesPage<SimpleCompound> dtPage = new JQueryDatatablesPage<>(
page.getContent(), iTotalRecords, iTotalDisplayRecords,
Integer.toString(sEcho));
String result = toJson(dtPage);
return result;
}
private String toJson(JQueryDatatablesPage<?> dt) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate4Module());
return mapper.writeValueAsString(dt);
}
compoundService
jest wspierany przez repozytorium Spring-Data. Zarządza transakcjami i zabezpieczeniami na poziomie metody. toJSON()
metoda wykorzystuje Jackson 2.0 i musisz zarejestrować odpowiedni moduł do mapera, w moim przypadku dla hibernate 4.
W przypadku, gdy masz relacje dwukierunkowe, musisz przypisać wszystkie klasy encji za pomocą
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="jsonId")
To pozwala Jacksonowi 2.0 serializować zależności kołowe (nie było to możliwe we wcześniejszej wersji i wymaga, aby Twoje encje były adnotowane).
Będziesz musiał dodać następujące zależności:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.1</version>
<type>jar</type>
</dependency>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-29 08:12:44
Używając Spring Boot (i dla Mongo DB) udało mi się z powodzeniem wykonać następujące czynności:
@RestController
@RequestMapping("/product")
public class ProductController {
//...
@RequestMapping(value = "/all", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE })
HttpEntity<PagedResources<Product>> get(@PageableDefault Pageable p, PagedResourcesAssembler assembler) {
Page<Product> product = productRepository.findAll(p);
return new ResponseEntity<>(assembler.toResource(product), HttpStatus.OK);
}
}
A Klasa modelu jest taka:
@Document(collection = "my_product")
@Data
@ToString(callSuper = true)
public class Product extends BaseProduct {
private String itemCode;
private String brand;
private String sku;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-11 14:03:56