Forum Stats

  • 3,783,343 Users
  • 2,254,760 Discussions
  • 7,880,369 Comments

Discussions

Why Distributed Transaction Error In Message Bean But Not Backing Bean?

wlovett
wlovett Member Posts: 87
edited Apr 30, 2014 1:47PM in JDeveloper and ADF

Hi Guys,

** Update ** I'd read the second post first.

Using JDeveloper 12.1.2.0.0 and integrated WebLogic server of the same version.

I've got an ADF program where one project of the application uses JDBC in a message bean.  If I create an Application Module within the same project and then connect using its connection (java:comp/env/jdbc/MyAdfConnDS), then the code below works just fine.  However, if I lookup the JDBC connection (jdbc/MyJdbcDS) I configured in WebLogic myself, then I get distributed transaction errors.  How can I setup my JDBC connection to emulate the one that the AppModule creates?  That way I can execute prepared statements and call commit explicitly?

Here is my JDBC code

        conn.setAutoCommit(false);
        PreparedStatement statement = conn.prepareStatement(sql);
        statement.setLong(1, companyAccountId);
// errors here
        statement.execute();
        conn.commit();
        statement.close();


Resulting in the error

// prepared statement.
DELETE BUSINESS_PARTNERS WHERE MASTER_TYPE_KEY = 'C' AND COMPANY_ACCOUNT_ID = ?
java.sql.SQLException: Cannot call commit when using distributed transactions
            at weblogic.jdbc.wrapper.JTAConnection.commit(JTAConnection.java:373)
            at com.sample.myProduct.jms.clients.MyClient.deleteEntities(MyClient.java:211)
            at com.sample.myProduct.jms.clients.MyClient.importData(MyClient.java:155)
            at com.sample.myProduct.jms.beans.ImportMessageBean.onMessage(ImportMessageBean.java:91)
            at weblogic.ejb.container.internal.MDListener.execute(MDListener.java:575)
            at weblogic.ejb.container.internal.MDListener.transactionalOnMessage(MDListener.java:477)
            at weblogic.ejb.container.internal.MDListener.onMessage(MDListener.java:375)
            at weblogic.jms.client.JMSSession.onMessage(JMSSession.java:4855)
            at weblogic.jms.client.JMSSession.execute(JMSSession.java:4529)
            at weblogic.jms.client.JMSSession.executeMessage(JMSSession.java:3976)
            at weblogic.jms.client.JMSSession.access$000(JMSSession.java:120)
            at weblogic.jms.client.JMSSession$UseForRunnable.run(JMSSession.java:5375)
            at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:550)
            at weblogic.work.ExecuteThread.execute(ExecuteThread.java:295)
            at weblogic.work.ExecuteThread.run(ExecuteThread.java:254)


Here's a screenshot of all the drivers available: www.williverstravels.com/JDev/Forums/Threads/3544909/drivers.png as well as the transaction configuration screen www.williverstravels.com/JDev/Forums/Threads/3544909/config.png

I've tinkered enough to feel pretty strongly it's not a coding error, but rather something in Weblogic.  I've read what I can, but I don't know how the AppModule creates connections.  I'd hate to add an AppModule just to grab its data source / connection information if I don't need to.

Ideas?

Tagged:

Best Answer

  • Dimitar Dimitrov
    Dimitar Dimitrov Member Posts: 919 Bronze Trophy
    Accepted Answer

    Obviously, your message bean is configured to participate in a global transaction (e.g. a JTA transaction). This is the default message bean's behavior, even if you have not configured it explicitly. When you get and use a JDBC connection in your message bean, this connection is attached to and participates in the message bean's global transaction (because you have been using Oracle's XA-compliant driver, this is seen from the 1st screenshot in your post), so you CANNOT perform a local commit on the JDBC connection.


    Commit/rollback in this case may be performed only on the global transaction either automatically (if the message bean is configured with container-managed transactions) or manually on the UserTransaction object (if the message bean is configured with bean-managed transactions). In other words, you should remove the statement conn.commit(), which is both wrong and obsolete when used in your message bean. If the message bean uses container-managed transactions, the container will commit/rollback automatically at the end of the method all the resources that participate in the global transaction (including your JDBC connection). If the method finishes successfully (i.e. without an exception), a commit will be performed. If the method fails (i.e. it finishes with an exception), a rollback will be performed. If the message bean uses bean-managed transactions, the transaction will be committed/rolled back when you explicitly invoke UserTransaction.commit() or UserTransaction.rollback().


    The code above worked in a JSF managed bean because neither ADF BC nor ADF Faces applications demand and participate in JTA transactions, so your JDBC connection has not been attached to a global transaction and you were able to perform a local commit on the JDBC connection.


    You have a couple of options to resolve the problem. It depends on whether you want your JDBC operations to be in a global transaction with the message bean itself and its other actions or not. If they have to be in a global transaction, then just remove the statement conn.commit() from your code. If they should not be in a global transaction, then you should either configure the message bean not to participate in global transactions or configure the DataSource to use the "Oracle's Driver (Thin) ..." instead of "Oracle's Driver (XA Thin) ...". (Do not forget to mark the DataSource NOT to participate in a global transaction in the 2nd page of the DataSource's configuration dialog in the WLS Admin Console).


    N.B. I am strongly recommending you to keep global transactions. If you choose not to use a global transaction, it may happen that you committed the JDBC operation(s) but an exception happens after that, so the JMS message is not removed from the message queue and it will be processed again after that, thus causing the same JDBC operations to be performed and committed twice, which must be avoided.


    Dimitar

Answers

  • wlovett
    wlovett Member Posts: 87
    edited Apr 9, 2014 12:44AM

    Update

    After quite a bit of testing, I've narrowed down the problem.  Accessing JDBC from a message bean is different than accessing from a backing bean.

    I've got two applications, one that calls JDBC from a message bean and another that calls it from a backing bean.  They both connect to JDBC the exact same way through a data source I created in weblogic using "Oracle Driver (Thin) for Instance Connections ...", which is the same one that the App Module creates.  The one with the message bean throws the distributed transaction error, the backing bean one does not.  I have no idea why.

    // errors at statement.execute()
    java.sql.SQLException: Cannot call Connection.commit in distributed transaction.  Transaction Manager will commit the resource manager when the distributed transaction is committed.
      at weblogic.jdbc.wrapper.JTSConnection.commit(JTSConnection.java:684)
    

    What makes calling JDBC from a message bean so different that it causes the distributed transaction error while calling it from a backing bean does not?

  • Dimitar Dimitrov
    Dimitar Dimitrov Member Posts: 919 Bronze Trophy
    Accepted Answer

    Obviously, your message bean is configured to participate in a global transaction (e.g. a JTA transaction). This is the default message bean's behavior, even if you have not configured it explicitly. When you get and use a JDBC connection in your message bean, this connection is attached to and participates in the message bean's global transaction (because you have been using Oracle's XA-compliant driver, this is seen from the 1st screenshot in your post), so you CANNOT perform a local commit on the JDBC connection.


    Commit/rollback in this case may be performed only on the global transaction either automatically (if the message bean is configured with container-managed transactions) or manually on the UserTransaction object (if the message bean is configured with bean-managed transactions). In other words, you should remove the statement conn.commit(), which is both wrong and obsolete when used in your message bean. If the message bean uses container-managed transactions, the container will commit/rollback automatically at the end of the method all the resources that participate in the global transaction (including your JDBC connection). If the method finishes successfully (i.e. without an exception), a commit will be performed. If the method fails (i.e. it finishes with an exception), a rollback will be performed. If the message bean uses bean-managed transactions, the transaction will be committed/rolled back when you explicitly invoke UserTransaction.commit() or UserTransaction.rollback().


    The code above worked in a JSF managed bean because neither ADF BC nor ADF Faces applications demand and participate in JTA transactions, so your JDBC connection has not been attached to a global transaction and you were able to perform a local commit on the JDBC connection.


    You have a couple of options to resolve the problem. It depends on whether you want your JDBC operations to be in a global transaction with the message bean itself and its other actions or not. If they have to be in a global transaction, then just remove the statement conn.commit() from your code. If they should not be in a global transaction, then you should either configure the message bean not to participate in global transactions or configure the DataSource to use the "Oracle's Driver (Thin) ..." instead of "Oracle's Driver (XA Thin) ...". (Do not forget to mark the DataSource NOT to participate in a global transaction in the 2nd page of the DataSource's configuration dialog in the WLS Admin Console).


    N.B. I am strongly recommending you to keep global transactions. If you choose not to use a global transaction, it may happen that you committed the JDBC operation(s) but an exception happens after that, so the JMS message is not removed from the message queue and it will be processed again after that, thus causing the same JDBC operations to be performed and committed twice, which must be avoided.


    Dimitar

  • wlovett
    wlovett Member Posts: 87

    Thank you very much for the explanation.  I decided to instantiate an ADF Application module in my message bean and use it for my insert instead of JDBC.  Everything is working pretty well for the most part, although I'm having some duplicate JMS messages for some reason.  Ah well, probably a different issue.

    I apologize for the belated response, but again, thank you very much!

This discussion has been closed.