最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Spring 6 TransactionManager Isolation Level Support in IBM Liberty - Stack Overflow

programmeradmin1浏览0评论

We have an application running in Liberty 19.X using Spring 5 and Spring Boot 2 which is live in production and working properly. We are migrating that application to Liberty 24.X with Spring 6 and Spring Boot 3. Unfortunately, Spring 6 dropped explicit support for WebSphere/Liberty's JTA transaction management. Spring 5 had a class named WebSphereUowTransactionManager specifically for handling this situation, but it was dropped in Spring 6 and we now have to use the generic JtaTransactionManager. We need a JTA transaction manager because we are connecting to both a database and a JMS queue.

I was able to get the JtaTranactionManager working by manually creating the JtaTransactionManager and setting the Transaction Manager using reflection. Liberty does not provide a JNDI lookup for the IBM transaction manager. Unfortunately, JtaTransactionManager does not support transaction isolation levels, which are required for Spring Integration to work properly (DefaultLockRepository sets an isolation level when attempting to acquire a lock).

NOTE: I see that JtaTransactionManager does provide a "allowCustomIsolationLevels" property, but looking at the source code it looks like it just enables a bypass of the isolation level check rather than adding support for isolation levels.

What is the proper method to get a Spring JtaTransactionManager in IBM Liberty that supports transaction isolation levels?

Here is the declaration for my current JtaTransactionManager:

@Bean
@ConditionalOnClass(name = "com.ibm.tx.jta.TransactionManagerFactory") /* only load on Liberty app servers */
JtaTransactionManager transactionManager() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    // unfortunately Liberty does not provide a transaction manager lookup via JNDI, so we have to find it using reflection
    // Hibernate uses similar reflection code to find the IBM TransactionManager in class WebSphereLibertyJtaPlatform
    // in the future, hopefully Liberty will allow us to get the transaction manager using JNDI and we can clean this up
    TransactionManager ibmTm = (TransactionManager) Class
        .forName("com.ibm.tx.jta.TransactionManagerFactory")
        .getDeclaredMethod("getTransactionManager")
        .invoke(null);
    JtaTransactionManager tm = new JtaTransactionManager();
    tm.setUserTransactionName("java:comp/UserTransaction"); // default
    tm.setTransactionManager(ibmTm);
    return tm;
}

We have an application running in Liberty 19.X using Spring 5 and Spring Boot 2 which is live in production and working properly. We are migrating that application to Liberty 24.X with Spring 6 and Spring Boot 3. Unfortunately, Spring 6 dropped explicit support for WebSphere/Liberty's JTA transaction management. Spring 5 had a class named WebSphereUowTransactionManager specifically for handling this situation, but it was dropped in Spring 6 and we now have to use the generic JtaTransactionManager. We need a JTA transaction manager because we are connecting to both a database and a JMS queue.

I was able to get the JtaTranactionManager working by manually creating the JtaTransactionManager and setting the Transaction Manager using reflection. Liberty does not provide a JNDI lookup for the IBM transaction manager. Unfortunately, JtaTransactionManager does not support transaction isolation levels, which are required for Spring Integration to work properly (DefaultLockRepository sets an isolation level when attempting to acquire a lock).

NOTE: I see that JtaTransactionManager does provide a "allowCustomIsolationLevels" property, but looking at the source code it looks like it just enables a bypass of the isolation level check rather than adding support for isolation levels.

What is the proper method to get a Spring JtaTransactionManager in IBM Liberty that supports transaction isolation levels?

Here is the declaration for my current JtaTransactionManager:

@Bean
@ConditionalOnClass(name = "com.ibm.tx.jta.TransactionManagerFactory") /* only load on Liberty app servers */
JtaTransactionManager transactionManager() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    // unfortunately Liberty does not provide a transaction manager lookup via JNDI, so we have to find it using reflection
    // Hibernate uses similar reflection code to find the IBM TransactionManager in class WebSphereLibertyJtaPlatform
    // in the future, hopefully Liberty will allow us to get the transaction manager using JNDI and we can clean this up
    TransactionManager ibmTm = (TransactionManager) Class
        .forName("com.ibm.tx.jta.TransactionManagerFactory")
        .getDeclaredMethod("getTransactionManager")
        .invoke(null);
    JtaTransactionManager tm = new JtaTransactionManager();
    tm.setUserTransactionName("java:comp/UserTransaction"); // default
    tm.setTransactionManager(ibmTm);
    return tm;
}
Share Improve this question asked Nov 21, 2024 at 0:54 ScottScott 411 silver badge6 bronze badges 2
  • The allowCustomIsolationLevels is the one to set as that allows anything other then DEFAULT to be set. Although you might need to actually do something in the applyIsolationLevel method, so implement your own. – M. Deinum Commented Nov 21, 2024 at 7:14
  • Yes, as noted in my question, setting allowCustomIsolationLevels to true makes the transaction "work", but then the isolation levels are not applied--only ignored. I certainly could write my own transaction manager subclass to implement applyIsolationLevel, but I'd rather not go down the route of a custom transaction manager. I'm not really sure what the Spring author's true intent with the applyIsolationLevel method was, because setting the isolation level requires access to the DB Connection object, which is not readily available anywhere when in the applyIsolationLevel method. – Scott Commented Nov 21, 2024 at 14:16
Add a comment  | 

2 Answers 2

Reset to default 1

According to its Javadoc, Spring provides .springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter, a DataSource that routes to one of various target DataSources based on the current transaction isolation level.

Its javadoc details how its use, in combination with setting allowCustomIsolationLevels to true on its .springframework.transaction.jta.JtaTransactionManager, supports per-transaction isolation level selection using JTA specification-compliant DataSources.

We can wrap a datasource using Spring's IsolationLevelDataSourceAdapter class, which will set the transaction isolation when each DB connection is obtained to match the isolation level of the transaction.

Additionally, IBM support has notified me that a change in Open Liberty has been made in version 25.0.0.1 to allow Spring to directly look up the TransactionManager from JNDI using java:comp/UserTransaction.

@Bean
public DataSource dataSource(DataSourceProperties dsProperties) {
    // create the datasource as defined in the application.yml
    DataSource datasource = dsProperties.initializeDataSourceBuilder().build();
    // alternatively, use JNDI to get the datasource
    // DataSource datasource = (DataSource) new JndiTemplate().lookup(dsProperties.getJndiName());
    
    // wrap the datasource to support custom isolation levels
    IsolationLevelDataSourceAdapter isolationLevelDataSourceAdapter = new IsolationLevelDataSourceAdapter();
    isolationLevelDataSourceAdapter.setTargetDataSource(datasource);
    return isolationLevelDataSourceAdapter;
}

@Bean
@ConditionalOnClass(name = "com.ibm.tx.jta.TransactionManagerFactory") // only load on Liberty app servers
public TransactionManagerCustomizer<JtaTransactionManager> transactionManagerCustomizer() {
    return (jtaTm) -> {
        // unfortunately Liberty does not provide a transaction manager lookup via JNDI, so we have to find it using reflection
        // Hibernate uses similar reflection code to find the IBM TransactionManager in class WebSphereLibertyJtaPlatform
        // in the future, hopefully Liberty will allow us to get the transaction manager using JNDI and we can clean this up
        // UPDATE: according to IBM Support, Liberty v25.0.0.1 will add a JNDI lookup for java:comp/UserTransaction
        try {
            TransactionManager ibmTm = (TransactionManager) Class
                .forName("com.ibm.tx.jta.TransactionManagerFactory")
                .getDeclaredMethod("getTransactionManager")
                .invoke(null);
            jtaTm.setTransactionManager(ibmTm);
            jtaTm.setAllowCustomIsolationLevels(true); // this works in conjunction with the IsolationLevelDataSourceAdapter
        }
        catch (Exception e) {
            throw new RuntimeException("Unable to retrieve IBM Transaction Manager", e);
        }
    };
}
发布评论

评论列表(0)

  1. 暂无评论