web-dev-qa-db-ja.com

Spring MVC 3:Spring-Data PageをJSONとして返す

Spring-Dataで作成されたデータアクセスレイヤーがあります。その上にWebアプリケーションを作成しています。この1つのコントローラーメソッドは、JSON形式の Spring-Data Page を返します。

そのようなページは、レコードの総量などの追加のページング情報を含むリストです。

それは可能ですか?はいの場合はどうですか?

そしてそれに直接関連して、プロパティ名のマッピングを定義できますか?例えば。つまり、ページング情報のプロパティの名前をJSONで(ページではなく)命名する方法を定義する必要があります。これは可能ですか?

25
beginner_

Spring HATEOASとSpring Data Commonsでは、このような今後のシナリオのサポートがあります。 Spring HATEOASには、基本的にPageMetadataと同じデータが含まれているPageオブジェクトが付属していますが、それほど強制的ではないため、マーシャリングとマーシャリング解除をより簡単に行うことができます。

これをSpring HATEOASおよびSpring Dataコモンズと組み合わせて実装する理由のもう1つの側面は、単にページをマーシャリングするだけの価値はほとんどなく、コンテンツとメタデータだけでなく、既存または次のページへのリンクを生成したいためです。クライアントは、これらのページ自体をトラバースするためにURIを構築する必要はありません。

ドメインクラスPersonを想定します。

_class Person {

  Long id;
  String firstname, lastname;
}
_

それに対応するリポジトリです:

_interface PersonRepository extends PagingAndSortingRepository<Person, Long> { }
_

これで、Spring MVCコントローラーを次のように公開できます。

_@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);
  }
}
_

ここで説明することはおそらくかなりあります。それを一歩ずつ見ていきましょう:

  1. リポジトリをそれに接続するSpring MVCコントローラーがあります。これには、Spring Dataを(@Enable(Jpa|Mongo|Neo4j|Gemfire)Repositoriesまたは同等のXMLを介して)設定する必要があります。コントローラメソッドは_/persons_にマップされます。つまり、そのメソッドへのすべてのGETリクエストを受け入れます。
  2. メソッドから返されるコアタイプは PagedResources -LinksPageMetadataで強化されたコンテンツを表すSpring HATEOASのタイプです。
  3. メソッドが呼び出されると、Spring MVCはPageableおよびPagedResourcesAssemblerのインスタンスを作成する必要があります。これを機能させるには、Spring Data Commonsの次のマイルストーンに導入される_@EnableSpringDataWebSupport_アノテーションを介して、またはスタンドアロンのBean定義を介して、Spring Data Webサポートを有効にする必要があります(ドキュメント here ) 。

    Pageableには、リクエストからの情報が入力されます。デフォルトの構成では、_?page=0&size=10_をPageableに変更し、ページサイズ10で最初のページを要求します。

    PageableResourcesAssemblerを使用すると、PagePagedResourcesインスタンスに簡単に変換できます。ページのメタデータを応答に追加するだけでなく、アクセスするページとPageable解像度の構成方法に基づいて、適切なリンクを表現に追加します。

JPAでこれを有効にするサンプルJavaConfig構成は次のようになります。

_@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@EnableJpaRepositories
class ApplicationConfig {

  // declare infrastructure components like EntityManagerFactory etc. here
}
_

リクエストとレスポンスの例

データベースに30のPersonsがあるとします。これで、_GET http://localhost:8080/persons_リクエストをトリガーできるようになり、次のようなものが表示されます。

_{ "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
  }
}
_

アセンブラは正しいURIを生成し、また、存在するデフォルトの構成を取得して、次のリクエストのパラメータをPageableに解決することに注意してください。つまり、その構成を変更すると、リンクは自動的に変更に準拠します。デフォルトでは、アセンブラはそれが呼び出されたコントローラメソッドをポイントしますが、カスタムLinkを渡すことでカスタマイズでき、PagedResourcesAssembler.toResource(…)のオーバーロードへのページ付けリンクを構築するためのベースとして使用できます方法。

見通し

PagedResourcesAssemblerビットは、Spring Data Babbageの次のマイルストーンリリース release train で利用可能になります。現在のスナップショットではすでに利用可能です。この動作例は、Spring RESTBucks sample application で確認できます。単にそれを複製し、_mvn jetty:run_およびcurl _http://localhost:8080/pages_を実行します。

56
Oliver Drotbohm

オリバー、あなたの答えは素晴らしいです、そして私はそれを答えとしてマークします。ここで私が他の誰かに役立つかもしれない、当面の間思いついたものを完全にするために。

グリッド/テーブルウィジェットとして JQuery Datatables を使用しています。それは非常に特定のパラメータをサーバーに送信し、非常に特定の応答を除きます: http://datatables.net/usage/server-side を参照してください。

これを実現するために、データテーブルが期待するものを反映するカスタムヘルパーオブジェクトが作成されます。 getterとsetterは、他の場合と同じように名前を付ける必要があることに注意してください。生成されたjsonは間違っています(大文字と小文字を区別するプロパティ名とデータテーブルは、この「疑似ハンガリー表記」を使用します...)。

_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;
    }
}
_

2番目の部分は、対応するコントローラーのメソッドです。

_@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は、Spring-Dataリポジトリによってサポートされています。トランザクションとメソッドレベルのセキュリティを管理します。 toJSON()メソッドはJackson 2.0を使用しており、適切なモジュールをマッパーに登録する必要があります(私の場合はhibernate 4の場合)。

双方向の関係がある場合は、すべてのエンティティクラスに注釈を付ける必要があります

_@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="jsonId")
_

これにより、Jackson 2.0は循環依存関係をシリアル化できます(以前のバージョンでは不可能であり、エンティティに注釈を付ける必要があります)。

次の依存関係を追加する必要があります。

_<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>
_
5
beginner_

Spring Boot(およびMongo DB)を使用して、次のことを実行でき、成功しました。

@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);
    }
}

そしてモデルクラスはこのようなものです:

@Document(collection = "my_product")
@Data
@ToString(callSuper = true)
public class Product extends BaseProduct {
    private String itemCode;
    private String brand;
    private String sku;    
}
0