私としても知らUPSERT、実行するいくつかのコード持ってマージ。私は特に、私は例外処理から離れて移動し、このような簡単な操作のためのコードの全体的な冗長性と複雑性を軽減したい、クリーンアップこのコードにしたいです。要件は、それが既に存在しない限り、各項目を挿入することです。
public void batchInsert(IncomingItem[] items) {
try(Session session = sessionFactory.openSession()) {
batchInsert(session, items);
}
catch(PersistenceException e) {
if(e.getCause() instanceof ConstraintViolationException) {
logger.warn("attempting to recover from constraint violation");
DateTimeFormatter dbFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
items = Arrays.stream(items).filter(item -> {
int n = db.queryForObject("select count(*) from rets where source = ? and systemid = ? and updtdate = ?::timestamp",
Integer.class,
item.getSource().name(), item.getSystemID(),
dbFormat.format(item.getUpdtDateObj()));
if(n != 0) {
logger.warn("REMOVED DUPLICATE: " +
item.getSource() + " " + item.getSystemID() + " " + item.getUpdtDate());
return false;
}
else {
return true; // keep
}
}).toArray(IncomingItem[]::new);
try(Session session = sessionFactory.openSession()) {
batchInsert(session, items);
}
}
}
}
SOの最初の検索が不十分です:
- 休止べき等の更新 -マルチスレッドやマルチプロセッシングのために関わらず、概念的に似ていますが、はるかに簡単なシナリオ。
- MySQLの「ON DUPLICATE KEY UPDATE」構文で作業を休止することができますか?はるかに優れた、使用してデータベースへのアトミックを押すことによって、競合状態を取り除き
@SQLInsert
注釈を。残念ながら、このソリューションは、あまりにもエラーが発生しやすい広いテーブルで使用すると、メンテナンスを多用するアプリケーションの進化です。 - どのようにHibernateを使用して模倣アップサートの行動に?同様の答えと、上記の質問に非常によく似て
- 休止+「ON DUPLICATE KEY」論理上記と同様、答えは言及
merge()
シングルスレッドの場合にOKであります - Hibernateで一括挿入または更新?同様の質問が、選ばれた回答は、ストアドプロシージャを使用して、オフレールです
- JPAとユニーク制約違反を防ぐための最善の方法再び非常にナイーブ、シングルスレッド指向の質問と回答を
質問ではどのように春データJPAでON DUPLICATE KEY UPDATEを行うには?これは重複としてマークされていた、私は、この魅力的なコメントに気づきました:
私は本当にコメントを理解していないとして、それはそれは巧妙な解決策のように聞こえる、そして「実際に同じSQL文」の言及にもかかわらず、行き止まりでした。
もう一つの有望なアプローチはこれです:Hibernateと春がDBに送信する前にクエリを変更します
CONFLICT DO NOTHING ON / ON DUPLICATE KEY UPDATE
主要なオープンソースデータベースの両方がデータベースに冪等を押し下げするメカニズムをサポートしています。以下の例は、PostgreSQLの構文を使用しますが、簡単にMySQLのために適合させることができます。
でアイデアに従うことでHibernateと春のDBに送信する前にクエリを変更、Hibernateのクエリー生成にフッキング、そしてどのように私は、HibernateでStatementInspectorを設定することができますか?、私が実装さ:
import org.hibernate.resource.jdbc.spi.StatementInspector;
@SuppressWarnings("serial")
public class IdempotentInspector implements StatementInspector {
@Override
public String inspect(String sql) {
if(sql.startsWith("insert into rets")) {
sql += " ON CONFLICT DO NOTHING";
}
return sql;
}
}
プロパティを持ちます
<prop key="hibernate.session_factory.statement_inspector">com.myapp.IdempotentInspector</prop>
重複が発生した次のようなエラーに残念なことに、このリード線:
org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException:によって引き起こさバッチ更新は、更新[0]から予期しない行数を返しました。実際の行数:0。予想:1; ネストされた例外はorg.hibernate.StaleStateExceptionある:バッチ更新は、更新[0]から予期しない行数を返しました。実際の行数:0。予想:1
あなたはどちらのカバーの下に何が起こっているのか考えてみれば、理にかなって:ON CONFLICT DO NOTHING
ゼロ行が挿入されます、しかし、1つのインサートが期待されています。
スレッドセーフな例外なしの同時冪等の挿入が可能になり、Hibernateが実行される全体のSQL INSERT文を定義し、手動で必要としない解決策はありますか?
何それの価値のために、私は、データベースへのdupcheckのダウンを押しアプローチは適切な解決策へのパスであることを感じています。
明確化IncomingItem
によって消費されるオブジェクトbatchInsert
メソッドは、レコードが不変であるシステムに由来します。この特殊な条件下でON CONFLICT DO NOTHING
可能にもかかわらず、UPSERTと同じように動作N番目の更新の損失。
短い答え- Hibernateは箱のそれをサポートしていない(でHibernateの第一人者によって確認されるよう、このブログの記事)。おそらく、あなたはそれはあなたがすでに説明したメカニズムで、いくつかのシナリオである程度動作させることができますが、単にネイティブクエリを使用すると、直接、この目的のために私には最も簡単な方法を探します。
長い答えは例えば、休止状態Iの推測のすべての側面を考慮すると、それをサポートするのは難しいだろうと次のようになります。
- それらが持続した後、管理しまうことになっているとして、重複が発見されるインスタンスで何をしますか?永続コンテキストにそれらをマージ?
- 何すでにされている団体で行うには、(;またはそれはその決定を下すためにその時点で手遅れになるsomething_new /持続/マージ)の操作がそれらに適用するためにどのカスケード、持続しましたか?
- データベースはすべてのユースケースをカバーするためにアップサート事業から十分な情報を返します(行をスキップ;など、モードのインサートバッチで、スキップされていないために生成されたキー)。
- 何について
@Audit
-edエンティティ、彼らは作成または更新され、更新された場合に何が変わったのか? - またはバージョニングと楽観的ロック(定義により、あなたが実際にそのような場合には例外をしたいですか)?
Hibernateはいくつかの方法でそれをサポートしていても気を付けると考慮すべきあまりにも多くの注意点があった場合、私は確かに、私はその機能を使用すると思いませんよ。
だから、私は従う親指のルールは次のとおりです。
- (ほとんどの時間です)シンプルなシナリオの場合:+再試行を持続。特定のエラー(例外タイプまたは類似の別)の場合の再試行は、グローバルプロジェクトで使用しているフレームワークに依存しAOPのようなアプローチ(注釈、カスタムインターセプタと同様に)で構成することができ、特に分散環境でとにかくお勧めします。
- 特定のデータベース機能の利用率を最大化するために、ネイティブクエリ:複雑なシナリオとパフォーマンスの集中的な操作については(特にそれがバッチ処理、非常に複雑なクエリと同様に来るとき)。