ORA-02051 global transaction when INSERT after prepare on other branch
The following code uses the Oracle JDBC thin driver version 10.2.0.2.0. The code receives a SQLException "ORA-02051: another session in same transaction failed" in writeToDB2() while trying to execute an INSERT. The same code works fine when run against a PostgreSQL database.
For what I can tell, the root of the problem is that Oracle doesn't allow an application to do an INSERT on a branch of a global transaction after prepare() has been called on the other branch. (Notice: This is a simplified example used to reproduce the problem with straight JDBC and XA calls. The real-life application uses TopLink in a clustered WebSphere environment. The problem is definitely not TopLink-related).
Any comments/suggestions would be appreciated.
Sample code:
public class XATest {
private static class MyXid implements Xid
{
protected int formatId;
protected byte gtrid[];
protected byte bqual[];
public MyXid()
{
}
public MyXid(int formatId, byte gtrid[], byte bqual[])
{
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
public int getFormatId()
{
return formatId;
}
public byte[] getBranchQualifier()
{
return bqual;
}
public byte[] getGlobalTransactionId()
{
return gtrid;
}
}
private static void writeToDB1(Connection conn)
throws Exception {
String sqlString;
sqlString = "SELECT * FROM dual";
System.out.println(
"Executing on conn " + conn.hashCode() + " [" + sqlString
+ "]");
PreparedStatement ps2 = conn.prepareStatement(sqlString);
ps2.execute();
System.out.println("Success");
}
private static void writeToDB2(Connection conn) throws Exception {
String sqlString;
sqlString = "INSERT INTO dcd_test2 (info) VALUES ('test')";
System.out.println(
"Executing on conn " + conn.hashCode() + " [" + sqlString + "]");
PreparedStatement ps2 = conn.prepareStatement(sqlString);
ps2.execute();
System.out.println("Success");
}
private void writeToDB(Connection conn) throws Exception {
String sqlString;
sqlString = "INSERT INTO dcd_test1 (info) VALUES ('test')";
System.out.println("Executing on conn " + conn.hashCode() +
" [" + sqlString + "]");
PreparedStatement ps1 = conn.prepareStatement(sqlString);
ps1.execute();
System.out.println("Success");
}
private XADataSource getDataSource() throws Exception {
oracle.jdbc.xa.client.OracleXADataSource ret =
new oracle.jdbc.xa.client.OracleXADataSource();
ret.setURL("jdbc:oracle:thin:@host:1521:SID");
ret.setUser(...);
ret.setPassword(...);
return ret;
}
private void main() throws Exception {
String txid = Long.toHexString(System.currentTimeMillis());
Xid xid1 = new MyXid(100, txid.getBytes(), new byte[] { 0x01 });
Xid xid2 = new MyXid(100, txid.getBytes(), new byte[] { 0x02 });
int ret1, ret2;
XADataSource xaDS = getDataSource();
// CMT
XAConnection xaCon2 = xaDS.getXAConnection();
XAResource xaRes2 = xaCon2.getXAResource();
Connection con2 = xaCon2.getConnection();
con2.setAutoCommit(false);
System.out.println("Starting XA on 2nd branch");
xaRes2.start(xid2, XAResource.TMNOFLAGS);
writeToDB1(con2);
// BMT
XAConnection xaCon1 = xaDS.getXAConnection();
XAResource xaRes1 = xaCon1.getXAResource();
Connection con1 = xaCon1.getConnection();
con1.setAutoCommit(false);
System.out.println("Starting XA on 1st branch");
xaRes1.start(xid1, XAResource.TMNOFLAGS);
writeToDB(con1);
// BMT - start commit
xaRes1.end(xid1, XAResource.TMSUCCESS);
System.out.println("Preparing XA on 1st branch");
ret1 = xaRes1.prepare(xid1);
if (ret1 == XAResource.XA_RDONLY) {
System.out.println("Readonly on 1st branch, commit not needed");
}
// CMT - beforeCompletion()
writeToDB2(con2);
xaRes2.end(xid2, XAResource.TMSUCCESS);
System.out.println("Preparing XA on 2nd branch");
ret2 = xaRes2.prepare(xid2);
if (ret2 == XAResource.XA_RDONLY) {
System.out.println("Readonly on 2nd branch, commit not needed");
}
// JTS
if (ret1 == XAResource.XA_OK) {
System.out.println("Committing on 1st branch");
xaRes1.commit(xid1, false);
}
if (ret2 == XAResource.XA_OK) {
System.out.println("Committing on 2nd branch");
xaRes2.commit(xid2, false);
}
}
}