如何制作可能抛出 PSQLException 等系统异常的弹性 EJB

How to make resilient EJB's which may throw System Exceptions like PSQLException

我在 GlassFish 4.1 中有一个 EJB 运行,它有时可能会抛出系统异常,例如 PSQLException。在一种特殊情况下,当尝试使用现有的唯一键值将记录写入数据库时​​,会抛出重复键异常。我已经尝试捕获所有异常以及添加 org.postgresql.util.PSQLException (和其他)作为异常-class 在 ejb-jar.xml 在应用程序异常中,但调用总是回滚事务.我希望我的 EJB 能够适应这些类型的异常。如果我能捕捉到异常甚至忽略它们,我的应用程序就可以从这些问题中恢复过来。我怎样才能做到这一点?

这是一个例子。我有一个 Servlet,当我用网络浏览器点击它时,它会调用这个 EJB 方法:

public void initialize() {  
    try {  
        User user = new User(0, "John", "Doe");  
        em.persist(user);  
        em.flush();  
    } catch(Exception e) {  
        e.printStackTrace();  
    }

我的 ejb-jar.xml 包含这个:

<application-exception>
   <exception-class>org.postgresql.util.PSQLException</exception-class>
    <rollback>false</rollback>
    <inherited>true</inherited>
</application-exception>

对我有用的解决方案是由 highstakes 提供的。 "The solution is to simply mark 'initialize' as non-transactional and put this stuff into a separate method that starts a new transaction for each try."

更新:这个解决方案并不像我想象的那样有效。我的应用程序中有一个错误,使它看起来可以正常工作。修复错误后,我又回到了同样的问题。似乎 PSQLException 是一个 SystemException,因此无法捕获。我尝试了容器管理和 bean 管理的方法。我不同意第二次尝试保留记录会导致 SystemException。在分布式环境中,这有可能发生。这应该是可以恢复的情况。

更新:我已经使用下面发布的解决方案解决了我的问题。

这绝对与 JPA 无关,它与 PostgreSQL 如何处理事务中的错误状态有关。

在您的情况下,它似乎要么在数据库级别自动回滚,要么将事务标记为处于不一致状态。您 cannot/should 无法从业务层内部恢复的内容。

解决方案是简单地将 'initialize' 标记为非事务性的,并将这些东西放入一个单独的方法中,每次尝试都会启动一个新的事务。

编辑:我的回答仅适用于您实际上设法在 catch 块中捕获异常的情况,如果您不这样做,则意味着您只是在提交期间获得异常,在这种情况下,上述解决方案也适用。

这是我选择的适合我的解决方案。而不是注入 PersistenceContext:

@PersistenceContext
private EntityManager em;

我正在注入数据源资源:

@Resource(lookup = "jdbc/sloocefw")
private DataSource dataSource;

这使我能够创建自己的 SQL 语句(这很重要)让我在尝试插入具有重复键的行时捕获 SQLException。

public void initialize() {  
    try (Connection connection = dataSource.getConnection()) {
        PreparedStatement ps = connection.prepareStatement("INSERT INTO user (id, firstName, lastName) VALUES (" + id + ", 'John', 'Doe')");
        ps.executeUpdate();
    } catch(SQLException e) {
        e.printStackTrace();
    }
}

我现有的用户实体 bean 管理 id(自动递增)。我需要更改为手动管理(如上例所示)或使用数据库自​​动增量。

我更愿意使用实体 bean,但是当我使用实体 bean 时尝试插入重复键会导致无法捕获的 SystemException。