spring-data-jpaを使用してエンティティを更新するにはどうすればよいですか?


194

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

JPARepositoryには保存メソッドしかないため、実際に作成または更新されたかどうかはわかりません。たとえば、データベースUserに単純なオブジェクトを挿入します。これには、3つのフィールド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のいずれかであるQueryを使用できます。しかし、spring-data-jpaとJPARepositoryを使用したいだけの場合、挿入の代わりに更新したいことをどのように伝えますか?

具体的には、同じユーザー名と名を持つユーザーが実際にはEQUALであり、既存のエンティティが更新されることになっていることをspring-data-jpaにどのように通知しますか?イコールをオーバーライドしてもこの問題は解決しませんでした。


1
データベースに既存のオブジェクトを保存すると、IDが書き換えられますか?私のプロジェクトtbhでこれがあったことはありません
Byron Voorbach

@ByronVoorbachうん、そうだ、これをテストしただけだ。質問も更新してください。thx–
Eugene

2
こんにちは友人、あなたは、このリンクを見ることができstackoverflow.com/questions/24420572/...をあなたがするsaveOrUpdate()のようなアプローチすることができ
ibrahimKiraz


ここに美しい解決策があると思います。ここにリンクの説明を入力してください
Clebio Vieira

回答:


206

エンティティのIDは、主キーによって定義されます。以来firstnamelastname主キーの一部ではありません、あなたは治療のためにJPAを伝えることはできませんUser同じで、S firstnamesおよびlastnameそれらが異なる持っている場合は同等であるS userId秒。

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

編集:

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

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

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

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


ええ、私はこれを知っていると思います。私は厳密にspring-data-jpaを参照していました。私はこの答えで2つの問題を抱えています:1)ビジネスバリューは主キーの一部ではないはずです-それは既知のことですよね?したがって、名と姓を主キーとして持つのはよくありません。そして2)このメソッドがcreateと呼ばれず、代わりにspring-data-jpaに保存されるのはなぜですか?
ユージーン

1
「Spring Data JPAのsave()は、プレーンなJPAのmerge()によってサポートされています」実際にコードを見ましたか?私はやっただけで、両方とも永続化またはマージのいずれかによってバックアップされました。ID(主キー)の存在に基づいて永続化または更新されます。これは、saveメソッドで文書化する必要があると思います。したがって、保存は実際にはマージまたは永続化のどちらかです。
ユージーン

オブジェクトの状態に関係なくオブジェクトを保存することになっているため、保存とも呼ばれます。保存状態に等しい更新または挿入を実行します。
ユージーン

1
これは私にはうまくいきません。有効な主キーを持つエンティティを保存しようとしました。「order / edit /:id」でページにアクセスすると、実際にはIDによって正しいオブジェクトが表示されます。神の愛のために私が試みることは、実体を更新しません。それは常に新しいエンティティを投稿します。カスタムサービスを作成し、EntityManagerで "merge"を使用してみましたが、それでも機能しません。常に新しいエンティティを投稿します。
DtechNet 2015

1
@DTechNet DtechNetと同様の問題が発生していましたが、Spring Dataリポジトリインターフェースで間違った主キータイプを指定していたことが問題であることがわかりました。本来持っているべきものではextends CrudRepository<MyEntity, Integer>なく、そう言ったextends CrudRepository<MyEntity, String>。それは役に立ちますか?これはほぼ1年後のことです。それが他の誰かを助けることを願っています。
ケントブル

139

@axtavtによる答えJPAspring-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-jpa、このクエリは更新操作であり、必須ではexecuteUpdate()ないことを通知するためのものexecuteQuery()です。

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


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


10
トランザクションで実行してください
hussachai '20 / 10/20

1
おい!春のデータBeanを使用していただきありがとうございます。そのため、自動的に更新が行われます。<SはTを拡張します> S save(Sエンティティ); 自動的に更新を処理します。私はあなたの方法を使う必要はありませんでした!とにかく、ありがとう!
bks4line 2015年

3
いつでも:)エンティティを保存する場合、saveメソッドは機能します(これは、em.persist()またはem.merge()への呼び出しをバックグラウンドで委任します)。とにかく、カスタムクエリは、データベース内の一部のフィールドのみを更新する場合に役立ちます。
hussachai、2015年

パラメータの1つがサブエンティティ(manyToOne)のIDである場合はどうすれば更新できますか (本には著者がいて、本の著者を更新するために本のIDと著者のIDを渡しました)
Mahdi

1
To update an entity by querying then saving is not efficientこれらは2つの選択肢だけではありません。idを指定して、クエリせずに行オブジェクトを取得する方法があります。を実行してrow = repo.getOne(id)からrow.attr = 42; repo.save(row);ログを監視すると、更新クエリのみが表示されます。
nurettin 2018

21

save()JPAfunctionでこの関数を単純に使用できますが、パラメーターとして送信されるオブジェクトにはデータベース内の既存のIDが含まれている必要があります。そうでない場合は機能しません。IDなしでオブジェクトを送信すると、save()が直接行を追加するためです。データベースですが、既存のIDを持つオブジェクトを送信すると、データベースですでに見つかった列が変更されます。

public void updateUser(Userinfos u) {
    User userFromDb = userRepository.findById(u.getid());
    // crush the variables of the object found
    userFromDb.setFirstname("john"); 
    userFromDb.setLastname("dew");
    userFromDb.setAge(16);
    userRepository.save(userFromDb);
}

4
更新する前にデータベースからオブジェクトをロードする必要がある場合、パフォーマンスに問題はありませんか?(私の英語で申し訳ありません)
august0490

3
1つではなく2つのクエリがあり、
優先度

私が知っていることは、別の方法を示したことです!しかし、なぜidが同じであるときにjpaがupdate関数を実装したのですか?
カリフォルニア州

17

他の人がすでに述べたように、save()それ自体には作成および更新操作の両方が含まれています。

save()メソッドの背後にあるものについて補足を追加したいだけです。

まず、のはの実装/拡張階層を見てみましょうCrudRepository<T,ID>ここに画像の説明を入力してください

では、save()実装をで確認しましょうSimpleJpaRepository<T, ID>

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

ご覧のとおり、最初にIDが存在するかどうかを確認します。エンティティが既に存在する場合は、merge(entity)メソッドによって更新のみが行われ、それ以外の場合は、persist(entity)メソッドによって新しいレコードが挿入されます。


7

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


エンティティと関連テーブルにバージョンフィールドを追加するという意味
jcrshankar


5

これは私が問題を解決した方法です:

User inbound = ...
User existing = userRepository.findByFirstname(inbound.getFirstname());
if(existing != null) inbound.setId(existing.getId());
userRepository.save(inbound);

使用@Transaction数dBの要求については、上記の方法を。この場合、の必要はありませんuserRepository.save(inbound);。変更は自動的にフラッシュされます。
Grigory Kislin

5

スプリングデータsave()メソッドは、新しい項目の追加と既存の項目の更新の両方を実行するのに役立ちます。

ただ電話してsave()、人生を楽しんでください:))


別の方法で送信した場合、このようにしてId保存しますが、新しいレコードを保存しないようにするにはどうすればよいですか。
Abd Abughazaleh

1
@AbdAbughazalehは、受信IDがリポジトリに存在するかどうかを確認します。'repository.findById(id).map(entity-> {//何かを返すrepository.save(entity)}).orElseGet(()-> {//何かを返す;}); '
アミールMhp

1
public void updateLaserDataByHumanId(String replacement, String humanId) {
    List<LaserData> laserDataByHumanId = laserDataRepository.findByHumanId(humanId);
    laserDataByHumanId.stream()
            .map(en -> en.setHumanId(replacement))
            .collect(Collectors.toList())
            .forEach(en -> laserDataRepository.save(en));
}

@JoshuaTaylor確かに、それを完全に逃した:)コメントは削除されます...
Eugene

1

具体的には、同じユーザー名と名を持つユーザーが実際にはEQUALであり、エンティティを更新することになっていることをspring-data-jpaにどのように伝えるのですか?等号のオーバーライドは機能しませんでした。

この特定の目的のために、次のような複合キーを導入できます。

CREATE TABLE IF NOT EXISTS `test`.`user` (
  `username` VARCHAR(45) NOT NULL,
  `firstname` VARCHAR(45) NOT NULL,
  `description` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`username`, `firstname`))

マッピング:

@Embeddable
public class UserKey implements Serializable {
    protected String username;
    protected String firstname;

    public UserKey() {}

    public UserKey(String username, String firstname) {
        this.username = username;
        this.firstname = firstname;
    }
    // equals, hashCode
}

使用方法は次のとおりです。

@Entity
public class UserEntity implements Serializable {
    @EmbeddedId
    private UserKey primaryKey;

    private String description;

    //...
}

JpaRepositoryは次のようになります。

public interface UserEntityRepository extends JpaRepository<UserEntity, UserKey>

次に、次のイディオムを使用できます。ユーザー情報でDTOを受け入れ、名前と名を抽出してUserKeyを作成し、この複合キーでUserEntityを作成してから、Spring Data save()を呼び出して、すべてをソートします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.