简体   繁体   English

如果在事务中我们只在一个数据源上写入,是否可以避免 2PC 或手动处理提交? (J2CA0030E)

[英]If in a transaction we do write only on one data source, is it possibile to avoid 2PC or handle commint manually? (J2CA0030E)

We are migrating an application from IBM WebSphere Traditiona 8.5 to OpenLiberty 19.0.0.12 (Both with IBM JDK 8).我们正在将应用程序从 IBM WebSphere Traditiona 8.5 迁移到 OpenLiberty 19.0.0.12(均使用 IBM JDK 8)。

In the process we want to get rid of XA transactions, are there is only one method that is actually using two different data sources, but only write to second data source (first one is used only for reading).在我们想要摆脱XA事务的过程中,是不是只有一种方法实际上使用了两个不同的数据源,而只写入第二个数据源(第一个仅用于读取)。

So imagine something like that :所以想象一下这样的事情:

@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 {
        ...
    }
  }

} }

Keep in mind that there are various read (from ds1) and write (to ds2) but they are mixed in a complex method, so splitting the transaction would need a deep refactoring that we would avoid right now.请记住,有各种读取(从 ds1)和写入(到 ds2),但它们混合在一个复杂的方法中,因此拆分事务需要进行深度重构,我们现在将避免这种重构。

But we get this error :但是我们得到这个错误:

J2CA0030E: Method enlist caught java.lang.IllegalStateException: Illegal attempt to enlist multiple 1PC XAResources J2CA0030E:方法登记捕获 java.lang.IllegalStateException:非法尝试登记多个 1PC XAResources

Is there any way to telle transaction manager 2PC is not needed without any major refactoring of the code?有没有什么方法可以告诉事务管理器 2PC 不需要对代码进行任何重大重构?

Thanks in advance.提前致谢。

Edit after solution was found (2020-01-11):找到解决方案后编辑(2020-01-11):

We created before a little POC of what we needed and then we tried to solution of just changing transaction management :我们在我们需要的一点点 POC 之前创建了我们,然后我们尝试解决只是改变事务管理的解决方案:

@TransactionManagement(TransactionManagementType.BEAN)

Then we managed to apply it to the real usecase and seems all is warking properly.然后我们设法将它应用到真正的用例中,似乎一切正常。 We post the POC in case could be useful to some for testing :我们发布 POC 以防对某些测试有用:

@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;
}   

} }

Based on the @Stateless annotation, it looks like you are using a stateless session EJB and likely ending up with a container managed transaction around both connections, causing there to be two resources within a single transaction, which requires two phase commit.根据@Stateless注释,您似乎正在使用无状态会话 EJB,并且可能以围绕两个连接的容器管理事务结束,从而导致单个事务中有两个资源,这需要两阶段提交。

If having these connections within the same transaction is not desired, consider switching from container managed transactions to bean managed transactions, in which case your application gets to decide when and where to begin/commit.如果不希望在同一事务中拥有这些连接,请考虑从容器管理的事务切换到 bean 管理的事务,在这种情况下,您的应用程序可以决定何时何地开始/提交。

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

After doing this, the remaining code might be fine as is (relying on autocommit from the JDBC driver) or you might wish to decide where to manually begin and end transactions as needed in the app code (not shown).执行此操作后,其余代码可能会正常(依赖于 JDBC 驱动程序的自动提交),或者您可能希望根据应用程序代码(未显示)中的需要决定手动开始和结束事务的位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Spring的单个事务中如何使用多个数据源(一个用于读取,另一个用于写入)? - How to use multiple data-source (one for read and another for write) within a single transaction in Spring? 提交事务时出现 XAException,J2CA0027E - 方法“xa_end”失败,错误代码为“106”和“-4” - Getting XAException while committing the transaction, J2CA0027E - The method 'xa_end' has failed with errorCode '106' & '-4'' 如何在Zookeeper集群中实现2pc? - How to implement 2pc in Zookeeper cluster? GlassFish 5中的2PC交易(交叉交易) - 2PC transactions (cross transactions) in GlassFish 5 跨多个微服务的 2PC 分布式事务? - 2PC distributed transactions across many microservices? 在Java中嵌套两阶段提交(嵌套/树2PC)? - nested two phase commit (nested/tree 2PC) in java? 我们可以编写自定义 Spring 事务管理器吗? - Can we write custom Spring transaction manager? 当事务只读取数据时,我应该避免使用注释@Transactional吗? - Should I avoid using the annotation @Transactional when the transaction only reads data? 将源代码从一台PC传输到另一台PC - Transferring source code from one pc to another 在J2ME中,我们是否需要Verisign或其他数字签名才能读取/写入内存电话卡? - In J2ME, do we need Verisign or other Digital Signature, to be able to read/write Memory phonecard?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM