1

休止状態で JSR-303 検証を使用しています。データベースにクエリを実行してデータの整合性をチェックするカスタム バリデータ アノテーションを作成しました。

例えば:

public boolean isValid(Object value, ConstraintValidatorContext context) {
    if(value == null){
        return true;
    }
    //This method uses entityManager to fetch a list from database
    Map<String, String> pickList = pickListProvider.getPickList(picklistName);
    return pickList.contains(value);
}

使用法:

public class UserProfile extends Persistent {
    //Member fields come here

    @PicklistConstraint(name="languages")
    private String prefLanguage;
}

ここで UserProfile は、Hibernate を使用して永続化されたエンティティです。Hibernate は、挿入前または更新前にこの検証を呼び出します。ただし、バリデーターがデータベースからレコードをフェッチしようとすると、休止状態がセッションをフラッシュします。検証が実行されるドメイン オブジェクトが完全に焼き付けられていない (Id フィールドがない) ため、次の例外が発生します。

org.hibernate.AssertionFailure: null id in <domain object here> entry (don't flush the Session after an exception occurs)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:79)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:194)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:58)
    at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1186)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1241)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:257)

調査の結果、DefaultAutoFlushEventListener の flushMightBeNeeded が true を返すことがわかりました。

private boolean flushMightBeNeeded(final EventSource source) {
    return !source.getFlushMode().lessThan(FlushMode.AUTO) &&
            source.getDontFlushFromFind() == 0 &&
            ( source.getPersistenceContext().getEntityEntries().size() > 0 ||
                    source.getPersistenceContext().getCollectionEntries().size() > 0 );
}

問題は、FlushMode が AUTO であり、source.getPersistenceContext().getEntityEntries().size() にいくつかのエントリがあることです。そのようなシナリオでの私のアプローチはどうあるべきですか? FlushMode を AUTO から MANUAL に変更する必要がありますか? 実際のエンティティとは異なる、新しいセッションまたは永続的なコンテキストを取得することは可能ですか? 休止状態とともに春の mvc を使用しています。

編集

source.getPersistenceContext().getEntityEntries() には、現在検証中のエンティティがあることがわかりました。挿入する前にここに存在する必要がありますか?

4

3 に答える 3

2

JPA仕様は次のように述べています。

一般に、移植可能なアプリケーションのライフサイクル メソッドは、EntityManager または Query 操作を呼び出したり、他のエンティティ インスタンスにアクセスしたり、同じ永続化コンテキスト内の関係を変更したりしてはなりません。

また、次のようにも述べています。

これらの制約を使用した自動検証は、セクション 3.5.2 で説明されている、永続化前、更新前、および削除前のエンティティ ライフサイクル イベントで、Java Persistence が検証を Bean Validation 実装に委譲するように指定することによって実現されます。

そのため、仕様では、ライフサイクル イベントでエンティティ マネージャーを使用すべきではなく、ライフサイクル イベントを使用して自動検証が実行されることが明示的に示されています。そのため、バリデーターでエンティティーマネージャーを使用することはできません。

于 2012-12-08T13:18:52.187 に答える
0

前の回答で指摘したように、ライフサイクル イベント中に EntityManager 操作を実行する必要はありません。本当にやりたい場合は、tmp セッションを開く必要があります。https://community.jboss.org/wiki/AccessingTheHibernateSessionWithinAConstraintValidatorも参照してください。

于 2012-12-09T15:39:39.020 に答える
0

バリデーターに次のコードを追加しましたが、正常に動作しているようです。

public boolean isValid(Object value, ConstraintValidatorContext context) {
    if(value == null){
        return true;
    }
    //This method uses entityManager to fetch a list from database
    TransactionTemplate txTemplate = new TransactionTemplate(txManager);                
        txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        Map<String, String> pickList = (Map<String, String>) txTemplate.execute(new TransactionCallback<Object>() {
            public Object doInTransaction(TransactionStatus status) {
                 return pickListProvider.getPickList(picklistName);
            }
    });
    return pickList.contains(value);
}

これが JPA 仕様に反しているように見えることは理解していますが、このバリデータによって取得されるデータはマスター データの一部であり、ファントム リードのような分離の問題につながることはないと確信しています。

于 2012-12-30T14:06:56.930 に答える