В 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]
Таким образом, требование «все или ничего» удовлетворено, но я бы хотел, чтобы транзакция завершилась ошибкой с исключением в любом случае, чтобы я мог вызвать ошибку.
Источник данных 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)
). Но я все же хотел бы понять, почему в журнале регистрируется только предупреждение, если фиксация локальной транзакции не удалась.
Есть ли какое-то свойство, которое я могу установить, чтобы получить исключение даже в том случае, когда в настоящее время я получаю только предупреждение?
RollbackException: ARJUNA016053: Could not commit transaction.
выбрасывается, как и ожидалось. Так что оптимизация 1PC неправильно проглатывает исключение. В коде файла-передачи вcommit()
простоIOException
переопределяется какResourceException
. - person Torsten Römer   schedule 13.12.2015