はじめに (対象読者・この記事でわかること)

この記事は、Java と Spring MVC を使って Web アプリケーションを開発しているエンジニア(初心者から中級者)を対象としています。特に、フロントエンドで DataGrid(例:jQuery EasyUI、AG Grid、Vaadin Grid) を利用している際に、入力したデータがサーバ側のコントローラで取得できない、という問題に悩んでいる方に向けています。

本記事を読むことで、以下が実現できるようになります。

  • DataGrid の行データが HTTP リクエストとして正しく送信される仕組みを理解する
  • Spring MVC 側で @ModelAttribute@RequestBody を正しく設定し、データを受け取る方法を習得する
  • よくあるバインドエラー(名前付きパラメータの不一致、型変換エラー、リクエストサイズ制限)を防止・対処できる

この問題は、フロントエンドとバックエンドのデータフォーマットが微妙にずれるだけで起きやすく、調査に時間がかかるケースが多いです。実務で即座に解決できるよう、具体的な手順とコード例を交えて解説します。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。

  • Java の基本文法とオブジェクト指向の概念
  • Spring Framework(特に Spring MVC)の基礎知識
  • HTML と JavaScript(jQuery など)で簡単なフロントエンドが作れること

DataGridとSpring MVCのデータ受け取り概要

DataGrid は、テーブル形式で大量のデータを表示・編集できる UI コンポーネントです。ユーザーがセルを編集したとき、通常は JSON 配列Form データ としてサーバへ送信します。一方、Spring MVC はリクエストパラメータやリクエストボディを自動的に Java のオブジェクトに変換(バインド)する仕組みを提供します。

しかし、以下のようなミスマッチが起きると「データが受け取れない」状態になります。

ミスマッチ例 具体例
パラメータ名の不一致 DataGrid が rows[] で送信するが、コントローラ側は items を期待
配列要素の型が合わない JSON の数値が文字列として送られ、int フィールドにバインド失敗
期待するコンテンツタイプが違う application/x-www-form-urlencoded を想定しているが、実際は application/json

このセクションでは、まず DataGrid がどのようにデータを送信するか、次に Spring MVC のバインディング機構 を簡潔に整理し、問題の根本原因を把握します。

  • DataGrid の送信形式
  • FormData: name=value&name=value... 形式。複数行は rows[0].field=... のようにインデックス付きで展開。
  • JSON: [{ "id":1, "name":"A" }, …] 形式で application/json ヘッダーと共に POST。

  • Spring MVC の受け取り方法

  • @ModelAttribute → フォームデータ(URLエンコード)をオブジェクトへバインド。
  • @RequestBody → JSON や XML を HttpMessageConverter が変換。

この基本を理解した上で、次のセクションで「実際に DataGrid のデータを受け取る手順」を示します。

Spring MVCでDataGridの入力データを正しく受け取る手順

以下では、代表的な DataGrid ライブラリ jQuery EasyUI を例に、JSON 形式 でデータを送信し、Spring MVC 側で受け取るまでの全工程を解説します。使用する Java バージョンは Java 17、Spring Boot 3.x、Spring MVC の標準機能だけで完結します。

ステップ1 フロントエンド側の設定:JSON で送信する

EasyUI の DataGrid は toolbar の保存ボタンや onEndEdit イベントで編集済み行を取得できます。取得したデータを JSON.stringify で文字列化し、$.ajaxcontentType: "application/json" と共に POST します。

Html
<table id="dg" class="easyui-datagrid" style="width:700px;height:250px" data-options="singleSelect:true,toolbar:'#tb'"> <thead> <tr> <th data-options="field:'id',width:50">ID</th> <th data-options="field:'name',width:150,editor:'text'">名前</th> <th data-options="field:'price',width:80,editor:'numberbox'">価格</th> </tr> </thead> </table> <div id="tb"> <a href="javascript:void(0)" class="easyui-linkbutton" onclick="save()">保存</a> </div> <script> function save(){ const rows = $('#dg').datagrid('getChanges'); // 編集済み行だけ取得 $.ajax({ url: '/products/save', method: 'POST', data: JSON.stringify(rows), contentType: 'application/json', success: function(){ $.messager.alert('Info','保存しました','info'); } }); } </script>

ポイント:

  • getChanges() により 変更された行だけ を送信でき、通信量を削減。
  • contentTypeapplication/json に設定し、サーバ側が JSON と認識できるようにする。

ステップ2 コントローラ側の受け取り設定:@RequestBody と List

サーバ側では、受け取った JSON 配列を List<Product> に変換します。そのために、エンティティクラス Product と、コントローラメソッドを用意します。

Java
// src/main/java/com/example/demo/model/Product.java package com.example.demo.model; public class Product { private Long id; private String name; private Integer price; // EasyUI の numberbox は数値として送られる // getter & setter (省略可) public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } }
Java
// src/main/java/com/example/demo/controller/ProductController.java package com.example.demo.controller; import com.example.demo.model.Product; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/products") public class ProductController { @PostMapping("/save") public ResponseEntity<String> saveProducts(@RequestBody List<Product> products) { // ここでビジネスロジック(バリデーション・DB保存等)を実行 products.forEach(p -> System.out.println("受信: " + p.getId() + ", " + p.getName() + ", " + p.getPrice())); return ResponseEntity.ok("保存完了"); } }

ポイント:

  • @RestController@RequestBody により、JSON → Java オブジェクト の変換が自動で行われる。
  • List<Product> と宣言することで、配列全体を受け取れる。
  • Jackson がデフォルトの HttpMessageConverter として機能し、型変換エラーが起きにくい。

ステップ3 カスタムバインディングが必要になるケース

(a) 日付や独自フォーマットのプロパティ

DataGrid が yyyy/MM/dd 形式の文字列を送るが、Java 側は LocalDate にしたい場合は @JsonFormatObjectMapper のカスタム設定が必要です。

Java
public class Order { @JsonFormat(pattern = "yyyy/MM/dd") private LocalDate orderDate; // getter / setter }

(b) フィールド名がスネークケース(snake_case)とキャメルケース(camelCase)の不一致

Jackson の PropertyNamingStrategies.SNAKE_CASE を全体に設定すれば、order_dateorderDate が自動変換されます。

Java
@Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); return mapper; }

ハマった点やエラー解決

発生したエラー 原因 解決策
JSON parse error: Unexpected character ('[') at position 0 @ModelAttribute で受け取ろうとしていたが、リクエストは JSON だった メソッドシグネチャを @RequestBody List<Product> に変更
Failed to convert property value of type java.lang.String to required type java.lang.Integer フロント側が数値を文字列 "10" で送信した Jackson が自動的に変換できるが、ObjectMapperDeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT を有効化、またはフロントで数値型に変換
org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing contentTypeapplication/json でない、もしくは data が空文字列 Ajax 呼び出しの contentType を必ず設定し、JSON.stringify([]) のように空配列でも送信する

実際にハマったケース:リクエストサイズ制限

Spring Boot のデフォルト設定では、spring.mvc.dispatcherServlet.maxHttpHeaderSize が 8KB で、巨大な JSON 配列を送信すると Request Entity Too Large (413) が返ってきました。

対策

Yaml
# src/main/resources/application.yml server: max-http-header-size: 16KB tomcat: max-swallow-size: -1 # 無制限に設定(サイズが大きい場合のみ) spring: servlet: multipart: max-request-size: 10MB max-file-size: 10MB

完全な動作確認手順

  1. フロント側で DataGrid に数行データを入力し、保存ボタンをクリック。
  2. ブラウザの開発者ツールでリクエストペイロードが application/json の配列であることを確認。
  3. Spring Boot アプリが起動していることを確認し、コンソールに 受信: と表示されるかチェック。
  4. Postman等で同様の JSON 配列を手動送信し、ステータス 200 とレスポンス 保存完了 が返ることを確認。

これらの手順が全て成功すれば、DataGrid の入力データが Spring MVC に正しくバインドされ、ビジネスロジックで利用できる状態です。

まとめ

本記事では、DataGrid の入力データが Spring MVC で受け取れない 典型的な原因と、その解決手順を体系的に解説しました。

  • フロント側は JSON 形式で送信し、contentType を正しく設定することが基本
  • サーバ側は @RequestBody List<T> を使い、Jackson の自動変換を活用
  • 型不一致や命名規則の違いは カスタムシリアライズ設定で対応し、エラーはログと例外メッセージで素早く切り分ける

これらを実践すれば、開発者は データ受け取りに関するトラブルシューティング時間を大幅に削減 でき、機能実装に集中できるようになります。次回は、WebSocket と DataGrid のリアルタイム同期について詳しく書く予定です。

参考資料