Если в транзакции мы делаем запись только в одном источнике данных, возможно ли избежать 2P C или обрабатывать ввод вручную? (J2CA0030E) - PullRequest
3 голосов
/ 10 января 2020

Мы переносим приложение из IBM WebSphere Traditiona 8.5 в OpenLiberty 19.0.0.12 (оба с IBM JDK 8).

В процессе, в котором мы хотим избавиться от транзакций XA, есть только один метод который на самом деле использует два разных источника данных, но только для записи во второй источник данных (первый используется только для чтения).

Итак, представьте что-то вроде этого:

@Path("/path_to_my_service")
@Stateless
public class MyService extends ByBaseService {

  private static final long serialVersionUID = 3470660101451196317L;

  @POST
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public Response create(Model model) {
    try ( Connection connectionFromDataSourceOne = ...;
         Connection connectionFromDataSourceTwo = ... ) {
    // performs some reading from connectionFromDataSourceOne
    // try to performe writing on connectionFromDataSourceTwo
    ) catch () {
        ...
    } finally {
        ...
    }
  }

}

Имейте в виду, что существуют различные операции чтения (от ds1) и записи (до ds2), но они смешаны сложным способом, поэтому для разделения транзакции потребуется глубокий рефакторинг, которого мы сейчас избегаем.

Но мы получаем эту ошибку:

J2CA0030E: Перехват метода пойман java .lang.IllegalStateException: незаконная попытка зачисления нескольких 1P C XAResources

Есть ли способ, чтобы менеджер транзакций 2P C не требовался без какого-либо серьезного рефакторинга кода?

Заранее спасибо.

Редактировать после решения Ион был найден (2020-01-11):

Мы создали перед небольшим ПО C то, что нам нужно, а затем мы попытались найти решение просто изменить управление транзакциями:

@TransactionManagement(TransactionManagementType.BEAN)

Тогда нам удалось применить его к реальному сценарию, и, похоже, все работает правильно. Мы публикуем ПО C на случай, если кому-то будет полезно для тестирования:

@Stateless
@Path("/test/transaction")
@TransactionManagement(TransactionManagementType.BEAN)
public class TransactionTestRest implements Serializable {

private static final long serialVersionUID = -2963030487875284408L;

private static final Logger logger = LoggerFactory.getLogger( TransactionTestRest.class );

@Resource(lookup = DataConsts.DS1_JNDI, name = DataConsts.DS1_NAME )
private DataSource ds1;

@Resource(lookup = DataConsts.DS2_JNDI, name = DataConsts.DS2_NAME )
private DataSource ds2;

private Properties baseProperties() {
    Properties props = new Properties();
    props.setProperty( "testVersion" , "3" );
    return props;
}

@GET
@Path("/conntest_all")
@Produces(MediaType.APPLICATION_JSON)
public Response testAll() {
    Response res = null;
    try ( Connection conn1 = this.ds1.getConnection();
            Connection conn2 = this.ds2.getConnection() ) {
        Properties props = this.baseProperties();
        props.setProperty( "testConn1" , conn1.getMetaData().getUserName() );
        props.setProperty( "testConn2" , conn2.getMetaData().getUserName() );
        conn2.setAutoCommit( false );   
        try ( Statement stm1 = conn1.createStatement();
                ResultSet rs1 = stm1.executeQuery( "SELECT * FROM test_ds1" );
                PreparedStatement pstm2 = conn2.prepareStatement( "INSERT INTO test_ds2 ( ID ) VALUES ( ? )" ) ) {
            while ( rs1.next() ) {
                BigDecimal id = rs1.getBigDecimal( "ID" );
                pstm2.setBigDecimal( 1 , id );
                pstm2.executeUpdate();
            }
            conn2.commit();
            props.setProperty( "result" , "OK!");
        } catch (Exception ie)  {
            props.setProperty( "result" , "Error:"+ie );
            conn2.rollback();
        } finally {
            conn2.setAutoCommit( true );
        }
        res =  Response.ok( props ).build();
    } catch (Exception e) {
        logger.error( "Error on conntest_all "+e, e );
        res = Response.status( Response.Status.INTERNAL_SERVER_ERROR ).build();
    }

    return res;
}

@GET
@Path("/conntest_1")
@Produces(MediaType.APPLICATION_JSON)
public Response test1() {
    Response res = null;
    try ( Connection conn1 = this.ds1.getConnection();) {
        Properties props = this.baseProperties();
        props.setProperty( "testConn1" , conn1.getMetaData().getUserName() );
        res =  Response.ok( props ).build();
    } catch (Exception e) {
        logger.error( "Error on conntest_1 "+e, e );
        res = Response.status( Response.Status.INTERNAL_SERVER_ERROR ).build();
    }
    return res;
}

@GET
@Path("/conntest_2")
@Produces(MediaType.APPLICATION_JSON)
public Response test2() {
    Response res = null;
    try ( Connection conn2 = this.ds2.getConnection();) {
        Properties props = this.baseProperties();
        props.setProperty( "testConn2" , conn2.getMetaData().getUserName() );
        res =  Response.ok( props ).build();
    } catch (Exception e) {
        logger.error( "Error on conntest_2 "+e, e );
        res = Response.status( Response.Status.INTERNAL_SERVER_ERROR ).build();
    }
    return res;
}   

}

1 Ответ

4 голосов
/ 10 января 2020

Основываясь на аннотации @Stateless, создается впечатление, что вы используете сеансовый EJB без сохранения состояния и, вероятно, заканчиваете транзакцией, управляемой контейнером, вокруг обоих соединений, в результате чего в одной транзакции будет два ресурса, что требует двухфазной фиксации .

Если нежелательно иметь эти соединения в одной и той же транзакции, рассмотрите возможность перехода от транзакций, управляемых контейнером, к транзакциям, управляемым компонентом, и в этом случае ваше приложение решает, когда и где начинать / фиксировать.

import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
...
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class MyService extends ByBaseService {
...

После этого с оставшимся кодом может быть все в порядке (полагаясь на автокоммит из драйвера JDB C), или вы можете sh решить, где вручную начинать и заканчивать транзакции, как это необходимо в приложении. код (не показан).

...