3

EXTENDED_PERSISTENT_CONTEXT を使用して、複数の呼び出しにわたって状態を維持しようとしています。私の理解では、管理されたエンティティは呼び出し間で切り離されませんが、以前に検証エラーをスローした後、呼び出しで切り離されたエンティティに関連するエラーが発生し続けます。状態はステートフル セッション Bean で維持されています。

@Named(SessionFacadeBean.SEAM_NAME)
@SessionScoped
@Stateful
@LocalBean
@AccessTimeout(value = 10, unit = TimeUnit.SECONDS)
public class SessionFacadeBean implements Serializable
{
    public static final String  SEAM_NAME        = "sessionCacheBean";

    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME, type = PersistenceContextType.EXTENDED)
    private EntityManager       em;

    private ParentOne sessionData;

    public synchronized ParentOne getSessionData() {
        if(sessionData == null) {
            sessionData = new ChildTwo();
        }
        return sessionData;
    }

    public boolean getLock() {
        return true;
    }

    public void clearLock() {
    }

    // Other stuff I don’t ‘think’ is relevant.
}

(簡略化された)状態は、休止状態を使用して保存されています。これは 3 つのクラス (親と 2 つの子で、そのうちの 1 つには子のリストが含まれます) で構成されます。

@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "Class", length = 50)
@Entity
public class ParentOne 
{   
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @XmlElement(name = "ID")
    private Long              iD;

    @XmlElement(name = "name")
    protected String              friendlyName          = "";
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildOne extends ParentOne
{
    public ChildOne(String name, ParentOne child) {
        super(name);
        myChild = child;
    }

    @ManyToOne(cascade = CascadeType.ALL)
    protected ParentOne myChild;   
}


@XmlRootElement(name = XMLConstants.COMPONENT_ELEMENT_NAME_IN_XML)
@XmlAccessorType(XmlAccessType.NONE)
@Entity
public class ChildTwo extends ParentOne
{
    public ChildTwo() {
            super(“common”);
    }
}

次のように、ステートレス Bean からステートフル Bean にアクセスしています。

@Stateless
@LocalBean
@Path("/")
public class MyService
{
    @PersistenceContext(unitName = GlobalParameters.BACKEND_CODE_PERSISTENCE_CONTEXT_NAME)
    private EntityManager       em;

    @Inject
    private SessionFacadeBean   sessionBean;

    @POST
    @Path("/create/item")
    @ValidateRequest
    public ComponentShortSummary addItem(@Form NewItemForm itemForm)
    {       
        if(sessionBean.getLock()) {
            try {
                if(itemForm.getName().equals("INVALID") == true) {
                    throw new ConstraintViolationException("Failed", new HashSet<ConstraintViolation<?>>());
                }

                ChildOne child = new ChildOne(itemForm.getName(), sessionBean.getSessionData());
                em.persist(child);
                return null;
            }
            finally {
                sessionBean.clearLock();
            }
        } else {
            return null;
        }
    }
}

問題を再現するために、次のシーケンスを実行します。

  • 有効な名前で addItem を呼び出します (これにより、項目がデータベースに永続化されます)。
  • 「INVALID」という名前で addItem を呼び出すと、制約例外がスローされます。
  • 有効な名前を指定して addItem を呼び出します (これにより、ラインem.persist(child).

私が理解していないのは、分離されたエンティティで終わる方法/理由です。実際のコードでは、状態を変更する前に、要求/状態の検証を実行します (そのため、状態が分離されていることがわかる理由はありません)。

への呼び出しを削除するとsessionBean.getLock()、問題は解決します (オブジェクトは正しく保持されます)。ロック メソッドの目的は基本的にセッション状態へのアクセスをシリアル化することですが、現在getLock()メソッドは空です。問題は、例外をスローする前にステートフル Bean を呼び出しているという事実に関連しているように感じます。

私のエンティティが分離される結果となる何が起こっているのか、誰かが説明できますか/それを回避する方法がある場合(理想的には、説明をサポートするドキュメントを教えてください)?

ステートフル Bean にアクセスする前に検証を実行して、現在の問題を回避する方法はおそらくあると思いますが、一般的なケース (ステートフル Bean が呼び出しでアクセスされた後に例外がスローされる場合) について心配しています。拡張された永続コンテキストからエンティティを切り離したくない場合に、例外を処理するための受け入れられた戦略はありますか?

4

1 に答える 1

2

これは予想される動作のようです。Scott Marlowによる JPA 仕様のセクション 3.3.2 への参照に感謝します。

トランザクション ロールバック トランザクション スコープと拡張持続性コンテキストの両方で、トランザクション ロールバックにより、すべての既存のマネージド インスタンスと削除されたインスタンス [31] が切り離されます。インスタンスの状態は、トランザクションがロールバックされた時点でのインスタンスの状態になります。通常、トランザクションのロールバックにより、永続化コンテキストがロールバックの時点で一貫性のない状態になります。特に、バージョン属性の状態と生成された状態 (たとえば、生成された主キー) は矛盾している可能性があります。したがって、以前は永続化コンテキストによって管理されていたインスタンス (そのトランザクションで永続化された新しいインスタンスを含む) は、他の切り離されたオブジェクトと同じ方法で再利用できない場合があります。たとえば、それらはマージ操作に渡されると失敗する可能性があります.[32 ]

そのため、アクティブなトランザクションに関与しているエンティティは、トランザクションがロールバックされると切り離され、sessionBean を呼び出すことでトランザクションに関与させます。

これを回避する1つの方法は、許容可能な例外を@AppicationException注釈で装飾することです。これにより、例外が非致命的であるとマークされ、トランザクションがロールバックされなくなります。このアプローチは、 David Blevinによって詳細に説明されています。

于 2012-10-09T19:54:07.247 に答える