3

EJB3 Bean とJPA2アノテーションを使用して、 JaveEE6でプロジェクトを開発しています。

データベースエンティティを前面に表示するために拡張永続コンテキストを使用するステートフル EJB3 Bean がいくつかあります (いくつかのインターフェースを介して、DTO の必要性を取り除きます)。

典型的な使用法は次のようなものです。

  • ユーザーの変更をすぐにコミットしたくないため、すべてのメソッドはトランザクションなしです。
  • 非トランザクション メソッドを介して、拡張コンテキストにアタッチされたエンティティをロードしています
  • save メソッドのみがトランザクションです。ユーザー データをチェックした後、エンティティはコミットされ、データベースに永続化されます。

MySQL データベースでは、すべてがうまく機能します。

残念ながら、Postgres@Lobでは、非トランザクション メソッドでロードされたフィールドで問題が発生します。JDBC は、トランザクション外の Lob アクセスを禁止しているようで、以下をスローします。 org.hibernate.exception.GenericJDBCException: Large Objects may not be used in auto-commit mode


stackoverflower が指摘したように、Lob は複数のレコードに存在する可能性があるため、一貫性を維持するにはトランザクションが必要です。

autocommittrue に設定しpersistence.xmlてもまったく機能せず、行うべきではありません

呼び出しの最後に何もコミットしたくないので、メソッドをトランザクション対応にすることはできません。では、ロブに簡単にアクセスする方法を知っている人はいますか?

私たちが想像するハック ソリューションは、Lob を別のエンティティに移動し、Lob コンテンツを読み取って使用できるようにするトランザクション メソッドを追加することです。かなり汚いと思います...

4

3 に答える 3

2

JPAコンテキストにロードされたエンティティへの変更は、エンティティがデタッチされない限り自動的にコミットされるという印象を受けているようです。これは実際にどのように機能するかということではありません。ただし、アタッチされたエンティティを変更してフラッシュしたり、デタッチされたエンティティをマージしたりしてもrollback、変更1が他のトランザクションに表示されないようにします。

読み取り専用操作を実行するときにトランザクションを開いたままにしておくことは無害であり、多くの場合、一貫性を保つために良い考えです2。データが書き込まれないことを保証したい場合で、JTAを使用している場合は、を使用setRollbackOnly()してデータSessionContextを確認してください。手動のJPAトランザクション管理の場合は、コミットするのではなく、完了時に必ず呼び出しrollback()てください。EntityTransaction

個人的には、「getLob」メソッドで新しいトランザクションを使用し、メソッドの最後でロールバックすることをお勧めします。DBがネストされたトランザクションをサポートしていない場合(ほとんどサポートしていません)、これにより通常、この作業を実行するためにプールから新しい接続がフェッチされます。

JTAおよびコンテナー管理トランザクションを使用している場合は、以下を試してください。

@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
public class LobTest {

    @PersistenceContext
    private EntityManager em;

    @Resource 
    private SessionContext sctx;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public byte[] getLob() {
        // Get your LOB content by fetching a new copy of the entity from the DB
        // by ID, avoiding the need to split the LOB out. Note that you lose
        // tx consistency guarantees between the LOB and the rest of the entity by
        // doing this.
        // then after loading the LOB:
        sctx.setRollbackOnly();
    }

}

または、周囲のトランザクションを中止するLOBの読み取りエラーを気にしない場合は、のTransactionAttributeType.REQUIRES代わりにを使用REQUIRES_NEWしてくださいsetRollbackOnly()。何も変更できないので、何もコミットされません。まだ開いていない場合は新しいトランザクションを開き、それ以外の場合は既存のトランザクションに参加するため、LOBの一貫した読み取りが可能になります。唯一の欠点は、一部のデータベースエラーがJTAトランザクション全体を中止することです。

非JTA環境でユーザー管理トランザクションを使用している場合は、新しいEntityManagerを取得し、EntityTransactionを取得し、em.find(...)エンティティを含むLOBの新しいコピーをロードするために使用します。


1SEQUENCEさて、ほとんどのデータベースには、PostgreSQLや関連するSERIAL疑似タイプ、アドバイザリロックなど、ロールバックするトランザクションの影響を受けるトランザクション免除オブジェクトタイプがいくつかあります。トランザクションは、他の操作を妨げる可能性のあるリソースのロックを保持するという意味で、データベースに「書き込む」こともできます。実際のデータについては、安全です。

2。実行時間の長いトランザクションは一部のデータベースでパフォーマンスの問題を引き起こし、接続プールを拘束するため、可能であれば、txを数秒以上開いたままにしないでください。「ユーザーの思考時間」(ユーザーが何かをするのを待っている時間)にトランザクションを開いたままにしないでください。空想にふけったり、昼食をとったり、休日に、月に行ったりする可能性があります。データベースと接続プールが返されるのを待っています。

于 2012-07-25T00:41:53.000 に答える
0

いくつかのアーキテクチャ上の理由から、Lobフィールドを別のフィールドに設定し、 BeanEntityを介して読み書きすることを選択しました。@Stateless

エンティティ:

@Entity
@Access(AccessType.FIELD)
public class LobEntity
{
    [...]

    @Lob
    private String content;

    public String getContent()
    {
        return content;
    }

    public void setContent(String content)
    {
        this.content = content;
    }
}

サービス:

@Stateless
@LocalBean
public class LobService 
{
    @PersistenceContext
    private EntityManager em;

    public String readLob(Long lobId)
    {
        LobEntity lobEntity = em.find(LobEntity.class, lobId);
        return lobEntity.getContent();
    }

    public LobEntity writeNewLob(String content)
    {
        LobEntity lob = new LobEntity(content);
        em.persist(lob);
        return lob;
    }
}

そして、ロブを直接含むがそれ以上は含まないクラス:

@Entity
@Access(value = AccessType.FIELD)
public class MyEntity
{
    [...]

    protected Long contentLobId;
    @Transient
    protected String editableContent;

    public Long getContentLobId()
    {
        return contentLobId;
    }

    public void setContentLobId(Long contentLobId)
    {
        this.contentLobId = contentLobId;
    }

    public String getEditableContent()
    {
        return editableContent;
    }

    public void setEditableContent(String editableContent)
    {
        this.editableContent = editableContent;
    }
}

LobEntity開発者が愚か者のようにアクセスしようとするのを避けるために、エンティティにはそれ自体がありません。非トランザクション コンテキストからコンテンツが必要な場合は、 LobService. また、編集したコンテンツを保存する場合も、同じ Bean を使用しています。

于 2012-07-27T09:40:50.393 に答える
0

試すgetEntityManager().flush();

これはデータベースに書き込みますが、現在のトランザクションはコミットしません。分離レベルが「読み取りコミット」であると仮定すると、実際にトランザクションをコミットするまで、他のクエリで更新は表示されません。触れた行にロックを保持することに注意してください...

于 2012-07-24T19:29:08.687 に答える