@Transactional的动态事务隔离级别

时间:2018-10-04 08:43:28

标签: java jdbc spring-jdbc jdbctemplate spring-transactions

我一直在努力在非常老的Java + Spring MVC代码库中修复SQL注入,该代码库在DAO层上有数百个类,目前正在使用java.sql.PreparedStatementjava.sql.Connection

数据库连接隔离级别,数据库连接提交和连接回滚是使用-ConnectionConnection.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类方法中创建的,在那里设置了隔离级别并传递给该方法(在另一个DAO类中),

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

此方法的调用者来自不同的类,并且调用层次结构通常是多个级别的,即在两个或三个以上级别创建了Connection对象,并在那里设置了隔离。

例如在DAOClass1.method()中创建连接,并将其传递到DAOClass3的getReportName之上。

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

通过引入@TransactionalJdbcTemplate组合可以重新设计这种情况吗?我将仅在创建了@Transactional的呼叫发起方还是在此方法上应用Connection

我想,这更多的是事务传播情况,但有点困惑。

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

Related Question 1 - Variable transaction isolation levels by request

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

1 个答案:

答案 0 :(得分:2)

我对这里是否需要动态隔离级别的前提感到怀疑。当隔离级别有所不同时,这不是因为特定数据有关,而是语句本身组合的方式成为问题。似乎不太可能出现这样的情况:用一组数据调用的方法应该具有一个隔离级别,而用另一组数据调用的相同方法应该经过另一个隔离级别。

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

指定事务详细信息(如隔离级别)需要在服务级别完成。我将仔细研究控制器,将它们在Web层所做的工作与业务逻辑分开,并将业务逻辑下推到可以用隔离级别等内容进行注释的服务方法中。

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

相关问题