59

Spring MVC + Jackson を使用して REST サービスを構築するプロジェクトがあります。次のJavaエンティティがあるとしましょう

public class MyEntity {
    private Integer id;
    private boolean aBoolean;
    private String aVeryBigString;
    //getter & setters
}

時々、ブール値を更新したいだけで、単純なブール値を更新するためだけにオブジェクト全体を大きな文字列で送信するのは良い考えではないと思います。そこで、PATCH HTTP メソッドを使用して、更新が必要なフィールドのみを送信することを検討しました。したがって、コントローラーで次のメソッドを宣言します。

@RequestMapping(method = RequestMethod.PATCH)
public void patch(@RequestBody MyVariable myVariable) {
    //calling a service to update the entity
}

問題は、どのフィールドを更新する必要があるかをどうやって知るかです。たとえば、クライアントがブール値を更新したいだけの場合、空の「aVeryBigString」を持つオブジェクトを取得します。ユーザーがブール値を更新したいだけで、文字列を空にしたくないことをどうやって知ることができますか?

カスタム URL を作成することで、この問題を「解決」しました。たとえば、次の URL: POST /myentities/1/aboolean/true は、ブール値の更新のみを許可するメソッドにマップされます。このソリューションの問題点は、REST に準拠していないことです。REST に 100% 準拠したくはありませんが、各フィールドを更新するためにカスタム URL を提供することに不安を感じています (特に、複数のフィールドを更新する場合に問題が発生することを考えると)。

別の解決策は、「MyEntity」を複数のリソースに分割し、これらのリソースを更新することですが、意味がないように感じます.「MyEntity」プレーンなリソースであり、他のリソースで構成されていません.

では、この問題を解決するエレガントな方法はありますか?

4

17 に答える 17

23

これは非常に遅いかもしれませんが、初心者や同じ問題に遭遇した人のために、私自身の解決策を共有させてください.

私の過去のプロジェクトでは、簡単にするために、ネイティブの Java Map を使用していました。クライアントが明示的に null に設定した null 値を含むすべての新しい値をキャプチャします。この時点で、ドメイン モデルとして同じ POJO を使用する場合とは異なり、どの Java プロパティを null に設定する必要があるかを簡単に判断できます。クライアントによってどのフィールドが null に設定されているかを区別できず、これは更新に含まれていませんが、デフォルトでは null になります。

さらに、更新するレコードの ID を送信するように http 要求を要求する必要があり、それをパッチ データ構造に含めないでください。私がしたことは、パス変数として URL に ID を設定し、パッチ データを PATCH 本体として設定することです。次に、ID を使用すると、最初にドメイン モデルを介してレコードを取得し、次に HashMap を使用すると、マッパー サービスまたはユーティリティを使用して、関連するドメイン モデルに変更を適用します。

アップデート

この種の汎用コードを使用して、サービスの抽象スーパークラスを作成できます。Java Generics を使用する必要があります。これは可能な実装のほんの一部です。アイデアが得られることを願っています。また、Orika や Dozer などのマッパー フレームワークを使用することをお勧めします。

public abstract class AbstractService<Entity extends BaseEntity, DTO extends BaseDto> {
    @Autowired
    private MapperService mapper;

    @Autowired
    private BaseRepo<Entity> repo;

    private Class<DTO> dtoClass;

    private Class<Entity> entityCLass;

    public AbstractService(){
       entityCLass = (Class<Entity>) SomeReflectionTool.getGenericParameter()[0];
       dtoClass = (Class<DTO>) SomeReflectionTool.getGenericParameter()[1];
    }

    public DTO patch(Long id, Map<String, Object> patchValues) {
        Entity entity = repo.get(id);
        DTO dto = mapper.map(entity, dtoClass);
        mapper.map(patchValues, dto);
        Entity updatedEntity = toEntity(dto);
        save(updatedEntity);
        return dto;
    }
}
于 2016-02-08T03:57:55.880 に答える
12

これを行う正しい方法は、JSON PATCH RFC 6902で提案されている方法です。

リクエストの例は次のとおりです。

PATCH http://example.com/api/entity/1 HTTP/1.1
Content-Type: application/json-patch+json 

[
  { "op": "replace", "path": "aBoolean", "value": true }
]
于 2013-12-18T10:33:05.260 に答える
5

全体のポイントは、エンティティ表現全体を送信していないPATCHということです。そのため、空の文字列に関するコメントがわかりません。次のような単純な JSON を処理する必要があります。

{ aBoolean: true }

それを指定されたリソースに適用します。受信したものは、目的のリソース状態と現在のリソース状態の差分であるという考え方です。

于 2013-07-25T15:13:28.703 に答える
1

更新されたフィールドからなるオブジェクトを送信できませんでしたか?

スクリプト呼び出し:

var data = JSON.stringify({
                aBoolean: true
            });
$.ajax({
    type: 'patch',
    contentType: 'application/json-patch+json',
    url: '/myentities/' + entity.id,
    data: data
});

スプリング MVC コントローラー:

@PatchMapping(value = "/{id}")
public ResponseEntity<?> patch(@RequestBody Map<String, Object> updates, @PathVariable("id") String id)
{
    // updates now only contains keys for fields that was updated
    return ResponseEntity.ok("resource updated");
}

コントローラーのpathメンバーで、マップ内のキーと値のペアを反復処理しupdatesます。上記の例では、"aBoolean"キーは value を保持しますtrue。次のステップでは、エンティティ セッターを呼び出して実際に値を割り当てます。しかし、それは別の種類の問題です。

于 2017-09-05T07:32:30.460 に答える
0

JpaRepository を実装する場合は、これを使用できます。

@Modifying
@Query("update Customer u set u.phone = :phone where u.id = :id")
void updatePhone(@Param(value = "id") long id, @Param(value = "phone") String phone);
于 2021-02-05T07:49:51.857 に答える
-20

ブール値をブール値に変更し、更新したくないすべてのフィールドに null 値を割り当てることができます。null 以外の値は、クライアントが更新するフィールドを定義します。

于 2013-07-25T14:33:14.713 に答える