为什么LocalXAResourceImpl.commit()中的异常只是发出警告?

时间:2015-12-10 16:41:10

标签: jpa java-ee transactions wildfly jca

在WildFly 8中,我使用的是包含Oracle XA数据源和自定义JCA LocalTransaction资源(与文件的连接)的分布式事务。

基本上它按预期工作 - 如果其中一个资源未能提交,则回滚整个事务,数据库和文件都不会更新/写入。

然而,有一个特例。使用JPA,如果我正在执行entityManager.merge(entity);并且实体的值与数据库中的值相同,并且写入文件失败(即因为它不存在),我得到的只是一个警告,我的EJB中没有抛出任何异常:

@Stateless
public class JCABean {

    @PersistenceContext(unitName = "file-tx") 
    private EntityManager entityManager;

    @Resource(name = "java:/FileDataSource")
    private IDataSource fileDataSource;

    public void insert(
            final long id, 
            final String value, 
            final boolean update) {

        final FileTxTest entity = new FileTxTest();
        entity.setId(id);
        entity.setValue(value);

        if (update) {
            entityManager.merge(entity);
        } else {
            entityManager.persist(entity);
        }

        final File file = new File(FileHelper.BASE_PATH, 
                String.format("%s.txt", id));
        try (final IConnection connection = fileDataSource.getConnection(
                file.getAbsolutePath())) {
            connection.write(String.format("%s%n", value));
        }
    }
}

fileDataSource.getConnection()会返回FileConnection实施LocalTransaction的实例。

如果file不可写,connection.commit()会抛出ResourceException

如果给entity.setValue()的值等于数据库中的值(和update == true所以合并完成),则只发出此警告:

16:33:58,276 WARN  [com.arjuna.ats.jta] (default task-4) ARJUNA016039: onePhaseCommit on < formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff7f000101:842ac6d:56699b01:22, node_name=1, branch_uid=0:ffff7f000101:842ac6d:56699b01:2a, subordinatenodename=null, eis_name=java:/FileDataSource > (LocalXAResourceImpl@1e22d60[connectionListener=e1ca39 connectionManager=1b9de9e warned=false currentXid=null productName=Generic JCA productVersion=1.0 jndiName=java:/FileDataSource]) failed with exception XAException.XA_RBROLLBACK: org.jboss.jca.core.spi.transaction.local.LocalXAException: IJ001156: Could not commit local transaction
        at org.jboss.jca.core.tx.jbossts.LocalXAResourceImpl.commit(LocalXAResourceImpl.java:180) [ironjacamar-core-impl-1.1.9.Final.jar:1.1.9.Final]
        at com.arjuna.ats.internal.jta.resources.arjunacore.XAOnePhaseResource.commit(XAOnePhaseResource.java:113)
        at com.arjuna.ats.internal.arjuna.abstractrecords.LastResourceRecord.topLevelPrepare(LastResourceRecord.java:152)

如果要合并的实体具有更新值,则整个事务将按预期失败:

Caused by: javax.transaction.RollbackException: ARJUNA016053: Could not commit transaction.
    at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1178)
    at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
    at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.endTransaction(CMTTxInterceptor.java:93) [wildfly-ejb3-8.2.1.Final.jar:8.2.1.Final]

如此有效,&#34;全有或全无&#34;要求得到满足,但我想让交易在任何情况下都失败,因为我可以提出错误。

Oracle XA数据源:

<xa-datasource jndi-name="java:/OracleDS" pool-name="OracleDS" enabled="true">
    <xa-datasource-property name="URL">
        jdbc:oracle:thin:@host.domain.tld:1521:NAME
    </xa-datasource-property>
    <driver>oracle</driver>
    <xa-pool>
        <min-pool-size>1</min-pool-size>
        <max-pool-size>5</max-pool-size>
        <prefill>true</prefill>
    </xa-pool>
    <security>
        <user-name>user</user-name>
        <password>pass</password>
    </security>
</xa-datasource>
<drivers>
    <driver name="oracle" module="com.oracle">
        <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
    </driver>
</drivers>

和资源适配器:

<resource-adapters>
    <resource-adapter id="file-tx-jca.rar">
        <archive>
            file-tx-jca.rar
        </archive>
        <transaction-support>LocalTransaction</transaction-support>
        <config-property name="Server">
            localhost
        </config-property>
        <config-property name="Port">
            19000
        </config-property>
        <connection-definitions>
            <connection-definition class-name="my.package.GenericManagedConnectionFactory" jndi-name="java:/FileDataSource" pool-name="FileConnectionFactory">
                <pool>
                    <min-pool-size>1</min-pool-size>
                    <max-pool-size>5</max-pool-size>
                </pool>
                <security>
                    <application/>
                </security>
            </connection-definition>
        </connection-definitions>
    </resource-adapter>
</resource-adapters>

注意:我知道我最多可以在事务中登记一个LocalTransaction资源(最后一个资源)。由于我可能需要在同一个事务中处理多个文件,因此我将FileConnection<resource-adapter>更改为XAResource / XATransaction。这里,如果提交失败(即通过抛出XAException(XAException.XA_HEURHAZ)),事务总是失败。但我仍然想了解为什么只有在本地事务的提交失败时才会记录警告。

我是否可以设置一些属性来获取异常,即使在我目前只收到警告的情况下?

1 个答案:

答案 0 :(得分:1)

我已从JBOSS 5 EAP中读取this documentation,其中描述了在涉及其他两阶段感知资源的事务中登记单个单阶段感知资源时使用的LRCO算法:

  

虽然XA事务协议旨在提供ACID   通过使用两阶段提交协议的属性,模型可能并不总是如此   合适。有时需要允许非XA感知   资源经理参与交易。这通常是   数据存储不支持分布式事务的情况。

     

在这种情况下,您可以使用称为Last Resource的技术   提交优化(LRCO)。这有时被称为最后资源   开局。在准备中最后处理单阶段感知资源   事务的阶段,此时尝试提交   它。如果尝试成功,则写入事务日志   剩下的资源经过第二阶段提交。如果是最后一个   资源无法提交,事务将回滚。虽然   一些,这个协议允许大多数交易正常完成   错误可能导致不一致的事务结果。为此原因,   使用LRCO作为最后的手段。使用单个时   在交易中,LRCO会自动应用于它。其他   在这种情况下,您可以使用特殊的方法指定最后一个资源   标记界面。请参阅JBoss Transactions Programmer's Guide   了解更多详情。

我的假设如下: 当您不修改合并的实体时,在两阶段感知资源事务的准备阶段,撤消日志中不会写入任何内容。

然后,当在准备阶段结束时发生单阶段资源事务的提交失败(文件写入)时,对于两阶段资源事务不需要撤消任何操作。 因此,您不再获得RollbackException,这与全局事务中涉及的两阶段资源事务的回滚相对应。