JPA/Hibernate 在 EJB/Seam 环境中提交时不发出更新

发布于 2025-01-02 11:19:57 字数 2286 浏览 0 评论 0原文

我有一个 Seam 3 沙箱应用程序,使用 JBoss 7、Hibernate 作为默认 JPA 实现,并使用 JSF 作为 Web 前端。

我有一个问题,默认情况下 SQL UPDATE 被吞没。

我的状态 EJB 在会话范围内维护一个扩展范围的 EntityManager 和一个实体,容器管理事务(需要新的)

  1. EntityManager 被注入
  2. EJB 使用 EM 加载实体并将其保存在一个字段中
  3. JSF 应用程序访问 EJB 及其实体,更改字符串字段
  4. JSF 应用程序在 EJB 中调用“Save”方法
  5. 在 save() 中,我检查实体字段是否已更改 ->它已正确更改
  6. 我什么也不做,容器在 save() 完成后提交事务。
  7. 问题:未对数据库执行 SQL 更新。

如果我通过以下方式扩展 save():

a)entityManager.contains(entity),则 UPDATE 按预期执行(结果为“true”)

b)entityManager.persist(entity),则 UPDATE 按预期执行

问:据我所知了解规范 a) 或 b) 都不需要,因为实体在整个过程中保持受管理。 我不明白,为什么a)对储蓄有影响。 我可以想象 b) 对保存有影响,但它不应该是必需的,不是吗?

欢迎任何解释。

这是我的 EJB:

@Named
@ConversationScoped
@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class LanguageBean {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;
    @Inject
    private UserTransaction transaction;

    private Language value;

    @Inject
    Conversation conversation;

    public LanguageBean() {
        super();
    }

    @Begin
    public void selectLanguage(Long anId) {
        conversation.setTimeout(10 * 60 * 1000);
        if (anId != null) {
            value = em.find(Language.class, anId);
        }
    }

    @BeforeCompletion
    public void transactionComplete(){
        System.out.println("transactionComplete");
    }

    public Language getValue() {
        return value;
    }

    @Produces
    @Named
    @ConversationScoped
    public Language getLanguage() {
        return getValue();
    }

    public void setValue(Language aValue) {
        value = aValue;
    }

    @End
    public String save() {
//      displays the changed attribute:
        System.out.println("save code: "+value.getCode());

//      why is either this required:
//      boolean tempContains = em.contains(value);
//      System.out.println("managed: "+tempContains);

//      or: why is persist required:
        em.persist(value);
        return "languages?faces-redirect=true";
    }

    @End
    public String cancel() throws SystemException {
        transaction.setRollbackOnly();
        return "languages?faces-redirect=true";
    }

}

I have a Seam 3 sandbox application using JBoss 7, Hibernate as default JPA implementation and as JSF as web front end.

I have the problem, that the SQL UPDATE is swallowed by default.

My stateful EJB in conversation scope maintains an extended scoped EntityManager and one Entity, Container Managed Transactions (Requires new)

  1. The EntityManager gets injected
  2. The EJB uses the EM to load the Entity and keeps it in a field
  3. JSF application accesses the EJB and its entity, changes a String field
  4. JSF application calles "Save" method in EJB
  5. In save() I check, if the Entities field was changed -> it was changed properly
  6. I do nothing more, the container commits the transaction after save() is finished.
  7. Problem: No SQL update is performed against the DB.

If i extend save() by:

a) entityManager.contains(entity) the UPDATE is executed as expected (result is "true")

OR

b) entityManager.persist(entity) the UPDATE is executed as expected

Q: As far as I understand the specs neither of a) or b) should be required, because the Entity remains managed during the entire process.
I dont understand, why a) has an effect on saving.
I can imaging the b) has an effect on saving, but it should not be required, should it?

Any explanation is welcome.

Here is my EJB:

@Named
@ConversationScoped
@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class LanguageBean {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;
    @Inject
    private UserTransaction transaction;

    private Language value;

    @Inject
    Conversation conversation;

    public LanguageBean() {
        super();
    }

    @Begin
    public void selectLanguage(Long anId) {
        conversation.setTimeout(10 * 60 * 1000);
        if (anId != null) {
            value = em.find(Language.class, anId);
        }
    }

    @BeforeCompletion
    public void transactionComplete(){
        System.out.println("transactionComplete");
    }

    public Language getValue() {
        return value;
    }

    @Produces
    @Named
    @ConversationScoped
    public Language getLanguage() {
        return getValue();
    }

    public void setValue(Language aValue) {
        value = aValue;
    }

    @End
    public String save() {
//      displays the changed attribute:
        System.out.println("save code: "+value.getCode());

//      why is either this required:
//      boolean tempContains = em.contains(value);
//      System.out.println("managed: "+tempContains);

//      or: why is persist required:
        em.persist(value);
        return "languages?faces-redirect=true";
    }

    @End
    public String cancel() throws SystemException {
        transaction.setRollbackOnly();
        return "languages?faces-redirect=true";
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眼趣 2025-01-09 11:19:57

我的经验主要是关于seam-2,但应该同样适用于这里。

会话和 JPA 会话是解耦的,原因很简单,会话结束可能不会导致实体被保存。

例如,对长时间运行的对话执行取消操作将结束对话(因为没有理由再维持对话)

考虑到您在示例中对取消进行回滚,您需要按照@user1187037的建议调用刷新(理论上是一个提交,但我认为这是不允许的)

我认为您可能可以设置一个配置,以便它在对话结束时刷新,但我可能是错的。

无论如何, http://javalangblog.blogspot.co.uk /2010/04/flush-mode-conversation.html 似乎提出了一个解决方案

希望有帮助。

来配置每个会话的刷新模式,

<begin-conversation join="true" flush-mode="COMMIT" />

编辑:您可以使用 xml和使用注释

@Begin(flushMode=COMMIT)

但请记住,会话可以 @End 而无需显式定义。如果用户在对话中途对实体进行了更改,然后放弃对话,则对话将在超时后自动关闭。如果我没记错的话,这将导致在上述情况下提交任何更改。

参考文献:

http:// docs.jboss.org/seam/3/persistence/3.0.0.Alpha1/reference/en-US/html_single/#d0e249
http://docs.jboss.org/seam/3/latest/api/org/jboss/seam/persistence/FlushModeType.html

My experience is largely with seam-2 but should be equally applicable here.

The conversation and JPA session is decoupled in seam for the simple reason that a conversation ending may not result in the entity being saved.

For example, a cancel action on a long running conversation would end the conversation (since there is no reason to maintain the conversation anymore)

Considering that you are doing a rollback on the cancel in your example, it would also seem logical that you would need to call a flush as suggested by @user1187037 (theoretically a commit but I don't think that's allowed)

I think there might have been a configuration you could set so that it did flush on conversation end but I may be mistaken.

In any case, http://javalangblog.blogspot.co.uk/2010/04/flush-mode-conversation.html seems to suggest a solution

Hope that helps.

EDIT: You can configure the flush mode per conversation using xml

<begin-conversation join="true" flush-mode="COMMIT" />

and using annotations

@Begin(flushMode=COMMIT)

Bear in mind though that a conversation can @End without it being explicitly defined. If a user is partway through the conversation, made changes to entities and then abandons the conversation, it will get automatically closed after a timeout. If I remember correctly, this will cause any changes to get committed in the above case.

References:

http://docs.jboss.org/seam/3/persistence/3.0.0.Alpha1/reference/en-US/html_single/#d0e249
http://docs.jboss.org/seam/3/latest/api/org/jboss/seam/persistence/FlushModeType.html

蓦然回首 2025-01-09 11:19:57

尝试在@End注释的方法上添加@Remove注释。

在我看来,@End 注释不会导致 bean 破坏。因此,即使在 save() 执行之后,持久性上下文仍然处于活动状态,并且其内容无法刷新到数据库。

Try to add @Remove annotation on methods annotated by @End.

In my opinion @End annotation does not result in bean destruction. So the persistence context is active even after save() execution and its content cannot be flushed to database.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文