Jakarta ee 什么是具有容器管理事务优势的JTA

Jakarta ee 什么是具有容器管理事务优势的JTA,jakarta-ee,jta,bean-managed-transactions,Jakarta Ee,Jta,Bean Managed Transactions,我将JavaEE8与OpenLiberty应用服务器一起使用。 在我的项目中,我尝试在CRUD层中使用JTA over container managed transaction(BMT)。 这是我的示例代码: @Stateless @TransactionManagement(TransactionManagementType.CONTAINER) public class SampleCRUD { @Inject private Logger logger; @P

我将JavaEE8与OpenLiberty应用服务器一起使用。
在我的项目中,我尝试在CRUD层中使用JTA over container managed transaction(BMT)。
这是我的示例代码:

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

    @Inject
    private Logger logger;

    @PersistenceContext
    private EntityManager em;

    public void insertFood(Food food) {
        em.persist(food);
    }

    public void updateFood(Food food) {
        em.merge(food);
    }

    public Food selectFood(long id) {
        return em.find(Food.class, id);
    }

    public void deleteFood(long id) {
        em.remove(select(id));
    }
}      
食品实体:

@Table
@Entity
@SequenceGenerator(name = "default_seq", sequenceName = "food_seq", allocationSize = 1)
public class Food extends BaseEntity {

    @Column(unique = true)
    private String name;
    
我想了解:
在sql插入/删除/更新操作之前,是否建议在数据库上进行选择?
我问这个问题是因为在CMT模式下无法捕获应用程序上的约束或SQL异常

在我的示例代码中:

  • 在持久化之前需要选择,因为重复密钥异常会导致应用程序服务失败
  • 删除前需要选择,因为application server中出现“找不到实体”异常结果

什么是具有容器管理事务(CMT)优势的JTA?

区别在于谁管理事务划分

如果您自己管理它(BEAN),那么您可以控制好的和错误的情况(并且您必须执行提交/回滚)

如果让它对容器执行操作,任何未处理的异常都会隐式地导致回滚,并且提交也会得到保证。 因此,您只需实现逻辑,其余部分由JTA完成


因此,如果不需要对事务进行任何特殊处理,最简单的方法是将事务处理留给容器,区别在于谁管理事务划分

如果您自己管理它(BEAN),那么您可以控制好的和错误的情况(并且您必须执行提交/回滚)

如果让它对容器执行操作,任何未处理的异常都会隐式地导致回滚,并且提交也会得到保证。 因此,您只需实现逻辑,其余部分由JTA完成


因此,如果您不需要对事务进行任何特殊处理,最简单的方法是将事务处理留给容器处理,这里提出的问题不是由于容器管理的事务,而是由于JPA(Java持久性体系结构)在乐观插入方面的限制。当您尝试插入已存在的实体时,JPA EntityManager将引发错误(EntityExistsException或PersistenceException with chained exception,如SQLIntegrityConstraintViolationException)和。这会阻止您编写应用程序逻辑,将插入失败的异常捕获为要提交的有效事务的一部分。编程模型不允许您这样做。无论您使用的是容器管理的事务还是应用程序管理的事务,都不是重点

根据您的业务逻辑,可以通过强制在新事务中运行某些操作来部分解决该问题,在这种情况下,如果回滚,则调用方的事务不会受到影响。这看起来像下面这样

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean insertFood(Food food) {
    try {
        em.persist(food);
        em.flush();
        return true;
    } catch (PersistenceException x) {
        if (x instanceof EntityExistsException
         || x.getCause() instanceof SQLIntegrityConstraintViolationException)
            return false;
        throw x;
    }
}
明显的缺点是操作不再是调用方事务的一部分

您提到了在尝试插入之前运行select/find的可能性。这里的问题是当多个线程重叠时,两个线程都看不到数据库中的任何内容,然后都尝试插入。错误仍然会发生,尽管发生的频率较低。一个悲观的锁在这里可能会有所帮助


另一种选择是,如果数据库/JDBC驱动程序允许(不强制回滚不成功的插入),可以放弃使用JPA,直接通过JDBC向数据库运行SQL命令。

这里提出的问题不是由于容器管理的事务,而是由于JPA的限制(Java持久性体系结构)关于乐观插入。当您尝试插入已存在的实体时,JPA EntityManager将引发错误(EntityExistsException或PersistenceException,带有链接异常,如SQLIntegrityConstraintViolationException)并且。这会阻止您编写应用程序逻辑,将插入失败的异常捕获为要提交的有效事务的一部分。编程模型不允许您这样做。无论您使用的是容器管理的事务还是应用程序管理的事务都无关紧要

根据您的业务逻辑,可以通过强制在新事务中运行某些操作来部分解决该问题,在这种情况下,如果回滚,则调用方的事务不会受到影响

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public boolean insertFood(Food food) {
    try {
        em.persist(food);
        em.flush();
        return true;
    } catch (PersistenceException x) {
        if (x instanceof EntityExistsException
         || x.getCause() instanceof SQLIntegrityConstraintViolationException)
            return false;
        throw x;
    }
}
明显的缺点是操作不再是调用方事务的一部分

您提到了在尝试插入之前运行select/find的可能性。这里的问题是当多个线程重叠时,两个线程都没有看到数据库中的任何内容,然后都尝试插入。错误仍然会发生,尽管发生的频率较低。悲观锁定可能会有所帮助

另一种选择是,如果数据库/JDBC驱动程序允许(在插入失败时不强制回滚),可以放弃使用JPA,直接通过JDBC向数据库运行SQL命令