@Transactional 的动态事务隔离级别

Dynamic transaction isolation levels for @Transactional

我一直在努力修复一个非常古老的 Java + Spring MVC 代码库中的 SQL 注入,在 DAO 层有几百个 classes目前正在使用 java.sql.PreparedStatement & java.sql.Connection .

DB 连接隔离级别,DB 连接提交和连接回滚直接在 Connection 对象上使用 - Connection.setIsolationLevel(int isolationLevel)Connection.commit()Connection.rollback() 处理。

假设,我有如下方法,

     public List<String> getReportName() {
            try {

Connection  connection = getConnection();
connection.setIsolationLevel(Isolation.READ_UNCOMMITTED); // Just an example
                String sql = //ActualSQLString;
                PreparedStatement  pstmt = connection.prepareStatement(sql);
                ResultSet  rs = pstmt.executeQuery();
                while (rs.next()) {
                 ......
                 ......
                }
            } catch (Exception e) {
                ...
            } finally {
                //Close connection, statement & result set
            }
            }

如果我想引入 org.springframework.jdbc.core.JdbcTemplate 代替 java.sql.PreparedStatement ,自动要求我摆脱调用 connection.setTransactionIsolation(isolationLevel) 、提交和回滚,因为 JdbcTemplate 在 DatSource 上工作单个连接对象。所以我改变上面的方法如下。 getJdbcTemplate() 仅用于说明目的,我也可以通过 @Autowired 获得它。此外,此要求与修复 SQL 注入的核心要求无关。

@Transactional(isolation = Isolation.READ_UNCOMMITTED, readOnly = true)
public List<String> getReportName() {
            try {
                String sql = //ActualSQLString;
                return getJdbcTemplate().queryForList(sql);
            } catch (Exception e) {
                ...
            } 
            }

提交和回滚的场景由 rollbackFor 属性处理,如果在上述方法中发生的话。

现在,我对方法签名何时如下所示感到困惑,即在另一个 DAO Class 方法中创建连接,在那里设置隔离级别并传递给此方法(在另一个 DAO class ) ,

public List<String> getReportName(Connection  connection) {
            try {
                String sql = //ActualSQLString;
                PreparedStatement  pstmt = connection.prepareStatement(sql);
                ResultSet  rs = pstmt.executeQuery();
                while (rs.next()) {
                 ......
                 ......
                }
            } catch (Exception e) {
                ...
            } finally {
                //Close connection, statement & result set
            }
            }

上述方法有来自不同 classes 的各种调用者,调用层次结构通常是多个级别,即连接对象是在上面两层或三层创建的,并在那里设置了隔离。

例如连接在 DAOClass1.method() 中创建并传递给 DAOClass3 的上面的 getReportName。

DAOClass1.method() -> DAOClass2.method(连接连接) -> DAOClass3.getReportName(连接连接)

是否可以通过引入 @Transactional & JdbcTemplate 组合来重新设计此场景?我会仅在创建 Connection 的调用发起者处应用 @Transactional 还是在该方法中应用?

我想,这更像是一个事务传播案例,但有点混乱。

我的问题与以下问题 # 1 重复,但需要针对我的特定情况的解决方案。

Related Question 2 - How can I get a spring JdbcTemplate to read_uncommitted?

我对这里要求动态隔离级别这一前提表示怀疑。当隔离级别产生影响时,这不是因为特定数据的原因,而是语句本身的组合方式才是问题所在。您似乎不太可能会遇到这样一种情况:使用一组数据调用的方法应该具有一个隔离级别,而使用另一组数据调用的相同方法应该在另一个隔离级别下进行。

我猜遗留代码的核心问题是没有事务服务层的概念。相反,您有一个由控制器直接调用的数据访问对象的大杂烩,而且隔离级别似乎是随意指定的。

需要在服务级别指定隔离级别等事务详细信息。我会遍历控制器,将它们所做的 Web 层工作与业务逻辑分开,并将业务逻辑下推到服务方法中,您可以用诸如隔离级别之类的东西进行注释。

一旦你这样做,你将拥有可以在不同服务中重用的 DAO,其中隔离级别在特定服务中给出,所有代码获取连接、捕获异常和关闭 jdbc 资源可以删除。