267

まあ、質問はほとんどすべてを言っています。JPARepository を使用してエンティティを更新するにはどうすればよいですか?

JPARepository にはsaveメソッドしかなく、実際に create なのか update なのかわかりません。たとえば、次の 3 つのフィールドを持つ単純なオブジェクトをデータベース User に挿入firstnamelastnameますage

 @Entity
 public class User {

  private String firstname;
  private String lastname;
  //Setters and getters for age omitted, but they are the same as with firstname and lastname.
  private int age;

  @Column
  public String getFirstname() {
    return firstname;
  }
  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }

  @Column
  public String getLastname() {
    return lastname;
  }
  public void setLastname(String lastname) {
    this.lastname = lastname;
  }

  private long userId;

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  public long getUserId(){
    return this.userId;
  }

  public void setUserId(long userId){
    this.userId = userId;
  }
}

次に、単純に を呼び出しますsave()。この時点では、実際にはデータベースへの挿入です。

 User user1 = new User();
 user1.setFirstname("john"); user1.setLastname("dew");
 user1.setAge(16);

 userService.saveUser(user1);// This call is actually using the JPARepository: userRepository.save(user);

ここまでは順調ですね。ここで、このユーザーを更新したいと思います。たとえば、年齢を変更します。この目的のために、QueryDSL や NamedQuery などのクエリを使用できます。しかし、単に spring-data-jpa と JPARepository を使用したいだけであることを考えると、挿入の代わりに更新を行いたいことをどのように伝えればよいでしょうか?

具体的には、同じユーザー名とファーストネームを持つユーザーが実際には EQUAL であり、既存のエンティティが更新されるはずであることを spring-data-jpa に伝えるにはどうすればよいですか? equals をオーバーライドしても、この問題は解決しませんでした。

4

12 に答える 12

259

エンティティのIDは、主キーによって定義されます。firstnameとは主キーの一部ではないため、同じsとsが異なるsがある場合、それらを等しいものとしてlastname扱うようにJPAに指示することはできません。UserfirstnamelastnameuserId

したがって、andでUser識別されるものを更新する場合は、クエリでそれを見つけてから、見つけたオブジェクトの適切なフィールドを変更する必要があります。これらの変更は、トランザクションの終了時に自動的にデータベースにフラッシュされるため、これらの変更を明示的に保存するために何もする必要はありません。firstnamelastnameUser

編集:

おそらく、JPAの全体的なセマンティクスについて詳しく説明する必要があります。永続性APIの設計には、主に2つのアプローチがあります。

  • 挿入/更新アプローチ。データベースを変更する必要がある場合は、永続APIのメソッドを明示的に呼び出す必要があります。insertオブジェクトを挿入するか、オブジェクトupdateの新しい状態をデータベースに保存するために呼び出します。

  • 作業単位アプローチ。この場合、永続ライブラリによって管理されるオブジェクトのセットがあります。これらのオブジェクトに加えたすべての変更は、作業単位の終了時(つまり、通常は現在のトランザクションの終了時)にデータベースに自動的にフラッシュされます。データベースに新しいレコードを挿入する必要がある場合は、対応するオブジェクトを管理対象にします。管理対象オブジェクトは主キーによって識別されるため、事前定義された主キーを管理対象とするオブジェクトを作成すると、同じIDのデータベースレコードに関連付けられ、このオブジェクトの状態がそのレコードに自動的に伝播されます。

JPAは後者のアプローチに従います。Spring Dataでは、JPAはプレーンJPAにsave()支えられているため、エンティティは上記のように管理されます。これは、事前定義されたIDを持つオブジェクトを呼び出すと、新しいレコードを挿入するのではなく、対応するデータベースレコードが更新されることを意味し、が呼び出されない理由も説明します。merge()save()save()create()

于 2012-08-09T10:43:42.270 に答える
192

@axtavtによる回答はそうではないことに焦点を当てているJPAのでspring-data-jpa

クエリを実行してエンティティを更新してから保存するのは効率的ではありません。これには 2 つのクエリが必要であり、他のテーブルを結合してコレクションをロードする可能性があるため、クエリのコストが非常に高くなる可能性があるためです。fetchType=FetchType.EAGER

Spring-data-jpa更新操作をサポートします。
リポジトリ インターフェイスでメソッドを定義し、 および で注釈を付ける必要が@Queryあり@Modifyingます。

@Modifying
@Query("update User u set u.firstname = ?1, u.lastname = ?2 where u.id = ?3")
void setUserInfoById(String firstname, String lastname, Integer userId);

@Queryはカスタム クエリを定義するためのものであり、このクエリが更新操作であり、 を必要としないこと@Modifyingを伝えるためのものです。spring-data-jpaexecuteUpdate()executeQuery()

他の戻り値の型を指定できます:
int- 更新されるレコードの数。
boolean- 更新中のレコードがある場合は true。それ以外の場合は false。


: このコードはトランザクションで実行します。

于 2015-06-30T22:28:59.903 に答える
9

spring-data-jpa を使用すると、@ save()DtechNet と同じ問題が発生しました。つまり、すべてsave()が更新ではなく新しいオブジェクトを作成していたということです。versionこれを解決するには、エンティティと関連テーブルにフィールドを追加する必要がありました。

于 2015-12-06T18:54:40.890 に答える
0

主キーが自動インクリメントの場合、主キーの値を設定する必要があります。save(); の場合 update().else として機能するメソッドは、db に新しいレコードを作成します。

jsp フォームを使用している場合は、隠しファイルを使用して主キーを設定します。

Jsp:

<form:input type="hidden" path="id" value="${user.id}"/>

ジャワ:

@PostMapping("/update")
public String updateUser(@ModelAttribute User user) {
    repo.save(user);
    return "redirect:userlist";
}

これも見てください:

@Override
  @Transactional
  public Customer save(Customer customer) {

    // Is new?
    if (customer.getId() == null) {
      em.persist(customer);
      return customer;
    } else {
      return em.merge(customer);
    }
  }
于 2020-10-18T09:37:56.103 に答える