web-dev-qa-db-ja.com

spring-data-jpaを使用してエンティティを更新する方法

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

JPARepositoryにはsaveメソッドしかありませんが、実際に作成したのか更新したのかはわかりません。たとえば、データベースのUserに単純なオブジェクトを挿入します。このオブジェクトには、firstnameとlastname、ageの3つのフィールドがあります。

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

それから私は単に "保存"します。これはこの時点では実際には挿入です。

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

[OK]をすべて良いです。今、私はこのユーザーを更新したいと思います、彼の年齢を変えると言います。それでは、QueryDSLまたはNamedQueryのどちらでもQueryを使用できます。しかし、spring-data-jpaとJPARepositoryを使用したいだけなのですが、insertの代わりにupdateを実行したいというのはどうすればいいのでしょうか。

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

ありがとうございました!

145
Eugene

エンティティのIDは、プライマリキーによって定義されます。 firstnamelastnameは主キーの一部ではないため、同じUsersとfirstnamesを持つlastnamesが等しい場合、JPAに処理するように指示することはできません。異なるuserIds。

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

編集:

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

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

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

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

173
axtavt

@axtavtによる答えはspring-data-jpaではなくJPAに焦点を当てているので

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

Spring-data-jpaは更新操作をサポートします。
Repository interface.でメソッドを定義し、それに@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はカスタムクエリを定義するためのもので、@Modifyingspring-data-jpaにこのクエリが更新操作であり、executeUpdate()ではなくexecuteQuery()が必要であることを伝えるためのものです。

93
hussachai

この関数は、JPA関数のsave()で使用することができますが、パラメータとして送信されるオブジェクトにはデータベース内の既存のIDが含まれている必要があります。ただし、既存の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);
}
9
Kalifornium

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

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

まず、CrudRepository<T,ID>の拡張/実装階層を見てみましょう、 enter image description here

さて、SimpleJpaRepository<T, ID>save()の実装をチェックしましょう。

@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)メソッドによって新しいレコードが挿入されます。

8
Gearon

Spring-data-jpa save()を使用して、@ DtechNetと同じ問題を抱えていました。すべてのsave()が更新ではなく新しいオブジェクトを作成していたということです。これを解決するために、エンティティと関連テーブルにファイルされた "version"を追加しなければなりませんでした。

7
smile

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

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

spring dataのsave()メソッドは、新しいアイテムの追加と既存のアイテムの更新の両方を実行するのに役立ちます。

save()を呼び出して人生を楽しんでください。

1
Amir Mhp
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));
}
0
BinLee

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

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

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>

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

0
Andreas Gelever