web-dev-qa-db-ja.com

Spring Data JPAでHibernate @DynamicUpdateを使用する方法は?

Spring data-jpaを使用しています。 1列のみ更新したい。

私のリポジトリは;

public interface UserRepository extends JpaRepository<User,Long> {
}

私のサービスは

public User save(User user) {
    return userRepository.save(user);
}

私のエンティティ;

@Entity
@DynamicUpdate(true)
public class User implements Serializable {
    // column definitions, etc.
}

Userの1つの列のみを更新するにはどうすればよいですか?

6
Ravi Developer

問題は、まったく新しいUserエンティティを渡すためです。したがって、Hibernateは、データベースから既にフェッチされたキャッシュバージョンを使用できず、動的に更新する列を決定できません。

そのため、以下を実行して @DynamicUpdate ;の正しい動作を確認してください。

稼働中;

@Transactional
public User save(User newUser) {
    User currentUser = userRepository.get(newUser.getId());
    // handle merging of updated columns onto currentUser manually or via some mapping tool
    currentUser.setName(newUser.getName());
    return userRepository.save(currentUser);
}

上記のロジックと動的更新アノテーションを使用すると、監査が有効になっていない場合、または@Version 'ed列を使用している場合を除き、name列のみの更新を確認できます。

更新された列が常に同じ場合、updatable = falseの使用は非常に非効率的であるため、@Column定義の更新の対象ではない列に@DynamicUpdateを使用することをお勧めしますキャッシュされたSQLを利用せずに、各SQLを新たに生成します。ただし、これらの列をまったく更新できないため、この機能の使用には注意してください。

ネイティブJPA/Hibernateが不十分な場合を除いて、@Queryの使用はお勧めしませんが、列のターゲットセットのみを更新するユースケースがある場合は、それが最良の選択であり、効率的。

更新された列が多く、大きく異なる場合は、newUserからcurrentUserへの手動マッピングロジックを定義するか、 Orikaなどのマッピングツールを使用します。 、私は同様の自動化された構造を持ち、これらのマッパーを介して数百のエンティティにパッチが適用され、非常に汎用的な方法で多くのCRUD操作を処理できます。

3
buræquete

まず、_@DynamicUpdate_が機能しない理由を説明します。したがって、_@DynamicUpdate_がどのように機能するかに気付くはずです。

@DynamicUpdateアノテーションは、エンティティが変更されるたびにUPDATE SQLステートメントが生成されるように指定するために使用されます。デフォルトでは、Hibernateはすべてのテーブル列を設定するキャッシュされたUPDATEステートメントを使用します。エンティティに@DynamicUpdateアノテーションが付けられている場合、PreparedStatementには値が変更された列のみが含まれます。( 詳細

たとえば、Usernameというプロパティがあると仮定します。

定義された名前で初めてユーザーを保存した場合、値_@DynamicUpdate_としてnullを渡すと、変更と見なされ、名前をnullに更新しようとします。

最初の解決策:

最初の解決策として、_@DynamicUpdate_が機能するようにしたい場合は、最初に他のすべてのUserプロパティに古い値を入力してから保存します。

長所:生成されたSQLクエリは、必要なプロパティを更新するだけです。

短所:Hibernateは対応するSQL文字列を毎回生成する必要があるため、Hibernate側のパフォーマンスコストが発生します。

第二の解決策:

カスタム変数でUserを更新できます:

_@Modifying
@Query("UPDATE User SET name=(:name) WHERE id=(:id)")
public void updateName(@Param("name")String name, @Param("id")Long id);
_

第三の解決策:

最後の解決策として、_updatable = false_を使用することをお勧めします。これにより、エンティティが挿入された最初の瞬間にプロパティが埋められます。

_  @Column(name = "create_date", nullable = false, updatable = false)
    private Instant createDate;
_
8
Spara