@Unique
Bean Validationに制約を付けたいのですが、それは標準では提供されていません。JPAを使用する場合@UniqueConstraint
、独自の検証およびエラー報告メカニズムはありません。
Bean Validation制約として定義@Unique
し、それをJPAと組み合わせて、JPAが一意の制約を持つ列を作成し、値が一意であるかどうかをチェックする方法はありますか?
@Unique
Bean Validationに制約を付けたいのですが、それは標準では提供されていません。JPAを使用する場合@UniqueConstraint
、独自の検証およびエラー報告メカニズムはありません。
Bean Validation制約として定義@Unique
し、それをJPAと組み合わせて、JPAが一意の制約を持つ列を作成し、値が一意であるかどうかをチェックする方法はありますか?
テーブル全体のロックを取得しない限り、SQLクエリを使用して単一性をチェックすることは基本的に不可能です(同時トランザクションは、手動チェックの後、進行中のトランザクションのコミット前にデータを変更できます)。つまり、Javaレベルで有効な一意の検証を実装して、検証の実装を提供することはできません。ユニシティをチェックする唯一の信頼できる方法は、トランザクションをコミットしているときです。
BV仕様は、次のように要約しています。
付録D.JavaPersistence2.0の統合
質問:@Column(unique = true)にマップされる@Uniqueを追加する必要がありますか?
@UniqueはJavaレベルで確実にテストすることはできませんが、データベース固有の制約を生成する可能性があります。@Uniqueは今日のBV仕様の一部ではありません。
したがって、Bean Validation例外で一意の(およびnull以外の)制約違反をラップするのがよいと私は同意しますが、現在はそうではありません。
@Uniqueの実装方法とその周辺の問題の詳細については、こちらをご覧ください-http: //community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator
あなたはそれを行うことができますが、それは些細なことではありません。問題は次のとおりです。バリデーターは、挿入する値がすでに存在するかどうかを確認するために、いくつかのクエリを実行するためにデータベースアクセスを必要とします。そして、これは、sessionFactory / sessionにアクセスできないため、バリデーターから実際に行うことはできません。もちろん、バリデーター内でそれ(session / sessionFactory)をインスタンス化することはできますが、それは良いコーディング方法ではありません。
バリデーターにJPAアノテーションを読み取らせて適用させることができます。これは、拡張するためのアイデアとして使用できるスプリングバリデーターを使用した例の一部です。
@Inject
カスタムバリデーターにセッションBeanを挿入(またはSpring )することもでき@Autowired
、コンテナーはそれを接続する方法を知っている必要があります。私はこれを春の例としてのみ知っています:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
@Autowired //@Inject
private Foo aDependency;
...
}
試して(挿入または更新)、例外をキャッチして、何らかのアクションを実行する必要があります。たとえば、JSFバッキングBeanの場合:
try {
dao.create(record);//or dao.modify(record)
//add message success
} catch(EJBException e) {
//look for origin of error (duplicate label, duplicate code, ...)
var err = dao.isUnique(record);
if(err == null) throw e;//other error
String clientId = null;
String message = null;
switch(err) {
case CODE:
clientId = "client_id_of_input_code";
message = "duplicate code";
break;
case LABEL:
clientId = "client_id_of_input_label";
message = "duplicate label";
break;
default:
throw new AssertionError();//or something else
}
facesContext.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_ERROR, message));
facesContext.validationFailed();
}
別のオプションは、挿入/変更の前にチェックすることです。これには時間がかかる可能性があり、最終的にエラーが発生するのを防ぐことはできません。